При переходе с Hibernate 4.x на последнюю версию Hibernate 5 я столкнулся с проблемой, связанной с управлением транзакциями.
В моем коде есть диспетчер транзакций, который начинает транзакцию JTA, за которой следует вызов Session.beginTransaction. Ниже приведен пример, который воспроизводит проблему (сценарий не использует Spring или любое другое управление транзакциями, управляемыми контейнером):
transactionManager.begin();
saveOrUpdate(entity1);
saveOrUpdate(entity2);
...
transactionManager.commit();
private void saveOrUpdate(SomeEntity entity) {
try (Session session = sessionFactory.openSession()) {
session.setFlushMode(FlushMode.AUTO);
session.beginTransaction(); // throws IllegalStateException "Transaction already active"
try {
session.saveOrUpdate(entity);
session.getTransaction().commit();
} catch (Exception ex) {
session.getTransaction().rollback();
throw RuntimeException(ex);
}
}
}
Это вызывает появление IllegalStateException с сообщением "Transaction already active". Похоже, такое поведение было введено в Hibernate 5.2.0 (). Ранее Hibernate просто игнорировал начало самой физической транзакции, потому что знал, что вложенная транзакция присутствует: он просто создавал оболочку JtaTransaction с isInitiator, установленным в false.
Это исключение возникает в org.hibernate.engine.transaction.internal.TransactionImpl, в частности, в методе begin():
@Override
public void begin() {
if ( !session.isOpen() ) {
throw new IllegalStateException( "Cannot begin Transaction on closed Session/EntityManager" );
}
if ( transactionDriverControl == null ) {
transactionDriverControl = transactionCoordinator.getTransactionDriverControl();
}
// per-JPA
if ( isActive() ) { // *** This is the problematic part *** //
throw new IllegalStateException( "Transaction already active" );
}
LOG.debug( "begin" );
this.transactionDriverControl.begin();
}
Это также противоречит руководству пользователя, где говорится следующее:
// Note: depending on the JtaPlatform used and some optional settings,
// the underlying transactions here will be controlled through either
// the JTA TransactionManager or UserTransaction
Session session = sessionFactory.openSession();
try {
// Assuming a JTA transaction is not already active,
// this call the TM/UT begin method. If a JTA
// transaction is already active, we remember that
// the Transaction associated with the Session did
// not "initiate" the JTA transaction and will later
// nop-op the commit and rollback calls...
session.getTransaction().begin();
Это ошибка в Hibernate? И что означает комментарий «per-JPA» именно в коде, выдающем исключение? Есть ли способ восстановить старое поведение?
TransactionManager. Я понимаю, что это может не иметь совершенного смысла, но я не могу понять, как это объясняется в руководстве пользователя, тогда как на самом деле это не работает, когда транзакция JTA уже активна. - person M A   schedule 02.11.2018EntityTransaction. Кажется, JPA требует этой ошибки? Но странно то, что опять же... это противоречит примеру в руководстве пользователя. - person M A   schedule 02.11.2018