2017-02-21 60 views
0

Что происходит при совершении транзакции из диспетчера объектов, который не содержит какого-либо грязного объекта? Может ли быть, что команда COMMIT не отправляется в БД?JPA фиксирует изменения, сделанные путем развернутого соединения

У меня были некоторые тестовые случаи, которые случались сейчас, а затем без разумной причины. После некоторого расследования у меня теперь есть теория, которую я хотел бы подтвердить здесь.

У меня есть небольшая инфраструктура для подготовки данных к БД для каждого теста. Светильники используют такой способ для хранения объектов в БД с использованием JPA (Hibernate):

public <R> R doInTransaction(final Function<EntityManager, R> whatToDo) { 
    final EntityManager em = emf.createEntityManager(); 
    final R result; 
    try { 
     try { 
     em.getTransaction().begin(); 
     result = whatToDo.apply(em); 
     em.getTransaction().commit(); 
     } finally { 
     if (em.getTransaction().isActive()) { 
      em.getTransaction().rollback(); 
     } 
     } 
    } finally { 
     em.close(); 
    } 
    return result; 
    } 

Таким образом, прибор вызывает этот метод, проходящий функцию whatToDo, где сохранялись объекты и метод оборачивает транзакцию вокруг переданной функции , В моих неудачных тестовых случаях используется прибор, который использует устаревший код, который использует хранимые процедуры и сохраняет объекты непосредственно через JDBC, т.е. е. вместо того, чтобы использовать em.persist(), я использую следующий в переданной функции для вызова хранимых процедур:

em.unwrap(Session.class).doWork(connection -> { 
    // stored procedures are called here directly over JDBC 
}); 

Итак, моя теория состоит в том, что JPA этого обстоятельства не сразу совершение, как нет JPA грязных объектов, управляемых EntityManager. Следовательно, фактические фиксации происходят только позже, т.е. е. после утверждения моего теста и теста не удается. Может быть?

Что такое транзакционное поведение Hibernate при «разворачивании» соединения из EntityManager?

Я добавил em.flush() до em.getTransaction().commit(), и, похоже, это помогает, но я все еще не уверен на 100%, что это решает проблему. Может кто-нибудь подтвердить?

+0

Вы должны быть осторожны при смешивании и сопоставлении обработчиков персистентности. Hibernate поддерживает тайник, который грязный, а что нет. Если вы обновляете вещи за пределами hibernate, есть возможность спящего режима выйти из синхронизации с db и сбросить обновление, поскольку он считает, что его кешированная версия объекта по-прежнему действительна. Я несколько лет назад сталкивался с такими проблемами, и если я правильно помню, мы отключили кеш в режиме спящего режима, чтобы предотвратить его поведение. – Stephan

+0

Я предпочитаю не входить в таинственный мир кеширования в Hibernate ... :) Если 'em.flush()' будет выполнять эту работу здесь, этого было бы более чем достаточно для меня. – Alex

+0

Если вы в тестовой среде, вы всегда можете попробовать em.flush() и посмотреть. Что касается настроек кеша, если вы не ожидаете смешного количества одновременных пользователей, вы должны быть в состоянии отключить кеш в спящем режиме и позволить кешу в базе данных обрабатывать большую часть работы. Другое дело, кэширование по умолчанию db может помешать кэшированию по умолчанию для спящего режима. Я настоятельно рекомендую отключить кэш гибернации, если вы продолжаете сталкиваться с проблемами. – Stephan

ответ

0

Поведение такое же, независимо от развертывания соединения. если вы не используете JTA, альтернативой является базовая транзакция, предоставляемая JDBC, то есть локальная транзакция. (или вы можете реализовать собственный поставщик управляемых транзакций)

Когда вы разворачиваете соединение и имеете дело непосредственно с JDBC, вы все равно получаете то же самое соединение, которое первоначально было получено этим менеджером сеанса/сущности. Так что это тот же эффект.

+0

Исправить, разворачивать соединение и напрямую обращаться к JDBC, дает мне такое же соединение, поэтому я надеялся, что это 'em.getTransaction(). Commit()' также зафиксирует изменения, сделанные непосредственно на JDBC. Но это, по-видимому, не обязательно, если нет грязных объектов, верно? Поэтому моя теория действительна? Будет ли 'em.flush()' принудить фиксацию? Я предпочитаю не совершать непосредственно с JDBC, так что все, что делается внутри 'doInTransaction()', - это одна транзакция с точки зрения БД. – Alex

+0

Если есть какие-либо обновления, сделанные для объекта JPA, он помечен как грязный. В Hibernate изменения в сущности преобразуются в события, и только во время очистки события преобразуются в SQL и передаются JDBC. Если изменений нет (нет грязных сущностей), в конвейере не будет никакого SQL, поэтому фиксация или отсутствие фиксации будет иметь тот же эффект, т. Е. Нет op. – raminr