【发布时间】:2011-11-09 19:44:44
【问题描述】:
我有一个 Hibernate 项目,其中对 update() 的调用需要将内存中修改后的对象与已保存到数据库中的数据进行比较。例如,我的业务逻辑指出,如果记录“生效”(生效日期是今天或更早),则更新无法更改生效日期。为了实现这一点,我有以下代码(有点长且涉及):
经理
public class LogicManager {
@Autowired
SessionFactory sessionFactory
private Session getSession() {
return sessionFactory.getCurrentSession();
}
public MemberRecord findRecord(Integer id) {
// << Code to check authorization >>
return memberRecordDAO.findById(id);
}
public void updateRecord(MemberRecord record) {
getSession().evict(record);
MemberRecord oldRecord = memberRecordDAO.findById(record.getId());
Date oldEffectiveDate = oldRecord.getEffectiveDate();
if ( isEffective(oldEffectiveDate) &&
!oldEffectiveDate.equals(record.getEffectiveDate)) {
throw new IllegalArgumentException("Cannot change date");
}
// << Other data checks >>
memberRecordDAO.update(record);
}
}
DAO
public class MemberRecordDAO {
@Autowired
private SessionFactory sessionFactory;
private Session getSession() {
return sessionFactory.getCurrentSession();
}
public MemberRecord findById(Integer id) {
return (MemberRecord)getSession()
.getNamedQuery("findMemberById")
.setInteger("id", id)
.uniqueResult();
}
}
客户端代码
// ...
public void changeEffectiveDate(Integer recordId, Date newDate) {
LogicManager manager = getBean("logicManager");
MemberRecord record = manager.findById(recordId);
record.setEffectiveDate(newDate);
manager.updateRecord(record);
}
在管理器中添加evict() 调用之前,我注意到管理器的行为方式出乎意料。为了更新记录,我首先必须通过调用findById() 来获取该记录,这会将记录放入会话缓存中。我会对该对象进行更改,然后调用updateRecord(),后者将调用findById() 来获取(假定的)持久数据。我意识到对findById() 的第二次调用不会查看数据库数据,而只是从缓存中提取对象。这将导致我的 oldEffectiveDate 总是 与我新更改的日期相同,因为 record 和 oldRecord 将是完全相同的对象。
为了解决这个问题,我添加了对evict() 的调用,我理解这意味着该对象将从缓存中删除,从而迫使 Hibernate 去数据库获取MemberRecord。在我进行更改后,我的MemberRecordDAO 在调用uniqueResult() 时抛出异常,即AssertionFailed: possible nonthreadsafe access to session。当我运行调试器时,我看到LogicManager 和MemberRecordDAO 都使用相同的Session,我认为这是正确的。
所以,我的问题:
- 我的想法/算法是否正确?
evict()是正确的做法吗?有没有更好的办法?我对会话、缓存或evict()不太了解。在处理线程问题之前,我想确保这个逻辑是正确的。 - 为什么从 DAO 访问
Session不是线程安全的?
【问题讨论】:
-
tl;dr 但从标题来看,您可能想看看
session.refresh(...)和session.merge(...) -
@Martin -
refresh()看起来很有希望。我也在研究分离和重新附加对象。
标签: java hibernate spring hibernate-session