根据数据库中的数据检查对象的内存中版本

时间:2011-11-09 19:44:44

标签: java hibernate spring hibernate-session

我有一个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);
}

在我在Manager中添加evict()调用之前,我注意到管理器的行为方式出乎意料。为了更新记录,我首先必须通过调用findById()来获取该记录,这将把记录放入会话缓存中。我对该对象进行了更改,然后调用updateRecord()来调用findById()以获取(据称)持久化数据。我意识到第二次调用findById()查看数据库数据,而只是从缓存中提取对象。这会导致我的oldEffectiveDate 始终与我新更改的日期相同,因为recordoldRecord将是完全相同的对象。

为了抵消这一点,我添加了对evict()的调用,我理解这意味着该对象将从缓存中删除,迫使Hibernate转到数据库以获取MemberRecord。完成更改后,我的MemberRecordDAO会在调用uniqueResult() AssertionFailed: possible nonthreadsafe access to session时抛出异常。当我运行调试器时,我发现LogicManagerMemberRecordDAO都使用相同的Session,这是我认为正确的。

所以,我的问题:

  1. 我的思维/算法是否正确? evict()是正确的做法吗?有没有更好的办法?我对Sessions,缓存或evict()都不太了解。我想在处理线程问题之前确保这个逻辑是正确的。
  2. 为什么从DAO访问Session不是线程安全的?

2 个答案:

答案 0 :(得分:2)

evict()方法可行,但我相信'首选的hibernate做事方式'是使用Session.merge(),如:

public MemberRecord updateRecord(MemberRecord newRecord) {

    MemberRecord oldRecord = memberRecordDAO.findById(record.getId());

    Date oldEffectiveDate = oldRecord.getEffectiveDate();
    if ( isEffective(oldEffectiveDate) && 
     !oldEffectiveDate.equals(newRecord.getEffectiveDate)) {
      throw new IllegalArgumentException("Cannot change date");
    } else {
       MemberRecord merged = (MemberRecord) session.merge(newRecord);
       return merged;
    }
}

请记住,Session.merge()将使用newRecord中的值更新oldRecord的所有字段。

答案 1 :(得分:0)

这是通过我的测试的解决方案,但它对我来说似乎有点粗糙:

<强>管理器

  public void updateRecord(MemberRecord record) {
    MemberRecord oldRecord = record;
    record = record.clone();   //Added a clone() to MemberRecord
    getSession().evict(record);
    getSession().evict(oldRecord);
    getSession().refresh(oldRecord);
    // At this point, record has all of the new values, but none of the Hibernate
    //  data attached to it, due to the clone(). 
    // oldRecord is populated with the data currently in the database.

    Date oldEffectiveDate = oldRecord.getEffectiveDate();
    if ( isEffective(oldEffectiveDate) && 
         !oldEffectiveDate.equals(record.getEffectiveDate)) {
      throw new IllegalArgumentException("Cannot change date");
    }


    // << Other data checks >>
    memberRecordDAO.update(record);
  }

如果这种类型的东西可以做得更干净,请告诉我。