Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Hibernate 5.2.x Транзакция уже активна, странное поведение

Автор вопроса: M AОтветов: 2

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

  • Не могли бы вы опубликовать весь код вашего метода, где вы используете транзакцию? И какая среда выполнения у вас есть? Simon Martinelli 02.11.2018
  • @SimonMartinelli Спасибо за комментарий. Я уточнил код. Среда представляет собой отдельное приложение, использующее Hibernate с пользовательской TM. M A 02.11.2018
  • Хорошо, но почему вы вызываете session.beginTransaction() после transactionManager.begin()? У вас уже есть транзакция от TransactionManager, поэтому Hibernate прав, когда жалуется. Simon Martinelli 02.11.2018
  • На самом деле код является частью модульного теста, и код, который открывает и закрывает сеанс, находится в методе, который вызывается повторно (код снова обновлен). Цель состоит в том, чтобы убедиться, что все вызовы повторно используют одно и то же соединение XA, поддерживаемое TransactionManager. Я понимаю, что это может не иметь совершенного смысла, но я не могу понять, как это объясняется в руководстве пользователя, тогда как на самом деле это не работает, когда транзакция JTA уже активна. M A 02.11.2018
  • Похоже, что изменение в Hibernate 5.2.0 было сделано для соответствия EntityTransaction. Кажется, JPA требует этой ошибки? Но странно то, что опять же... это противоречит примеру в руководстве пользователя. M A 02.11.2018
  • Я рекомендую зарегистрировать проблему в Hibernate JIRA hibernate.atlassian.net/secure/Dashboard.jspa Simon Martinelli 02.11.2018

javahibernatetransactionsjpajta

Ответы

Ответ 1 · рейтинг 1 · aschoerk

Поведение соответствует ожидаемому. Вы начали транзакцию в диспетчере транзакций. Я предполагаю, что он работает в соответствии с JTA. Поэтому ожидается, что соединение, которое вы получили для openSession, примет участие в уже запущенной транзакции менеджеров транзакций.

Запуск дополнительной транзакции должен привести к этому Исключению.

Ответ 2 · рейтинг 1 · M A

На самом деле это был "регресс", о котором я сообщил в https://hibernate.atlassian.net/browse/HHH-13076, который теперь исправлен (вот ссылка для PR).

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы