Проверка версии объекта в памяти по данным в базе данных - PullRequest
1 голос
/ 09 ноября 2011

У меня есть проект 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, что я и считаю правильным.

Итак, мои вопросы:

  1. Правильно ли мое мышление / алгоритм?evict() правильная вещь?Есть ли способ лучше?Я не слишком разбираюсь в сессиях, кэшировании или evict().Я хочу убедиться, что эта логика верна, прежде чем решать проблемы с потоками.
  2. Почему доступ к Session из DAO не является потокобезопасным?

Ответы [ 2 ]

2 голосов
/ 10 ноября 2011

Подойдет метод 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 () обновит все поля oldRecord значениями из newRecord.

0 голосов
/ 13 декабря 2011

Это было решение, которое прошло мои тесты, но оно все еще кажется мне немного грубым:

Менеджер

  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);
  }

Если это можно сделать чище, пожалуйста, скажите мне.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...