1

我有一个 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. 当我运行调试器时,我看到两者LogicManagerMemberRecordDAO使用相同的Session,这是我认为正确的。

所以,我的问题:

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

2 回答 2

2

evict() 方法会起作用,但我相信“首选的休眠方式”是使用 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 的所有字段。

于 2011-11-09T20:33:52.673 回答
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);
  }

如果这种事情可以做得更干净,请告诉我。

于 2011-12-12T20:47:18.820 回答