【发布时间】:2012-07-04 09:42:56
【问题描述】:
我正在使用用户身份验证的 WebSocket 服务器工作。我现在真的不知道如何在请求中存储这个用户。我的 WebSocket 连接可能会保持打开数小时,有时每分钟会收到 100 个请求,有时每小时只收到 2 个请求(这是不可预测的)。用户数据可能会被另一个应用程序更改,这不是我的问题,但它可能是相关的。我正在使用 spring 3 和 hibernate 3.6.9。
我的问题是:我应该使用特定的事务管理以及如何管理这些用户数据。
我已经测试了 2 个用例:
1。 在登录时加载我的用户实体并在每个请求中合并()它。我必须合并它,因为每次请求后事务都会关闭。合并不是一个好的解决方案,因为它不加载数据库实体,而是持久化合并的实体。因此,如果数据在其他地方更新,它将还原它们并且不会加载更新的数据。
2。 登录时,我只存储用户 ID,并在每次请求时 find() 我的用户。为避免数据库请求泛滥,我设置了二级缓存,但根据我的 sql server 日志,hibernate 仍然为每个 find() 运行一个请求。
这是一些具体的代码和配置:
persistence.xml
<persistence-unit name="greenpacs">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/db"/>
<property name="hibernate.connection.user" value="root"/>
<property name="hibernate.connection.password" value="password"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
</properties>
</persistence-unit>
applicationContext.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="greenpacs" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
用户实体
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class User {
@Id
@Column(name = "userId", unique = true, nullable = false)
private Integer id;
private String pwd;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "User_Role", joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "userId") }, inverseJoinColumns = { @JoinColumn(name = "role_id", referencedColumnName = "id") })
private List<Role> roles;
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="id")
private Person person;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user", cascade = CascadeType.ALL)
private List<Pacs> pacs;
// getter&setter
WebSocket 监听器
@Component
public class UserManager implements WampMessageHandler {
UserService userService;
//Autowired setter
User user;
@Override
public void onConnected(WampConnection connection) {
//loginmethod return userId
user = userService.find(userId);
}
@Transactional
@Override
public boolean onMessage(String sessionId, int messageType, Object[] msg)
throws BadMessageFormException {
user = userService.merge(user);
//or
user = userService.find(user.getId());
...
}
我的 UserService 只是重定向到 EntityManager 的方法。
【问题讨论】:
-
您的第二个解决方案看起来很明显。如果您的问题是“如何避免每次查找的额外查询”,那么我的问题是:“这个查询是做什么的?”?
-
没有额外的查询它只是为每个查找休眠进行查询它似乎不使用任何缓存。由于关系,查询很长,但它只不过是一个简单的“SELECT * FROM User WHERE id=?”。
-
您确定用户在缓存中吗?如果用户可以修改,为什么要使用 READ_ONLY 策略?您是否尝试将 hibernate.cache.use_second_level_cache 设置为 true?您确定查询会加载用户的字段,而不仅仅是其急切的关联(未缓存)?
-
好的,问题是第二个缓存级别属性。我觉得自己很愚蠢,因为我知道我不止一次看过它。我使用只读缓存,因为更改将由另一个应用程序而不是这个应用程序进行(计划使用 infinispan)。无论如何,你想回答(solution2+2nd cache lvl)这样我就可以回答这个问题了吗?
标签: hibernate persistence jpa-2.0