При переходе с 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? А что означает комментарий «на JPA» именно в коде, который выдает исключение? Есть ли способ восстановить старое поведение?