我有一个 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);
}
}
道
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()
对MemberRecord
. 在我做出那个改变之后,MemberRecordDAO
当它调用时我会抛出一个异常,上面uniqueResult()
写着AssertionFailed: possible nonthreadsafe access to session
. 当我运行调试器时,我看到两者LogicManager
都MemberRecordDAO
使用相同的Session
,这是我认为正确的。
所以,我的问题:
- 我的想法/算法是否正确?是
evict()
正确的做法吗?有没有更好的办法?我对 Sessions、缓存或evict()
. 在处理线程问题之前,我想确保这个逻辑是正确的。 - 为什么
Session
从 DAO 访问不是线程安全的?