Выделение весенней транзакции JDBC из спящего режима - PullRequest
3 голосов
/ 20 января 2011

Весной HibernateTransactionManager использует SessionFactory, с которой он был инициализирован, чтобы "привязать" сеанс к текущему контексту потока при создании новой транзакции. Затем, когда используется HibernateTemplate, он находит связанный Session и использует его.

Однако сегодня я обнаружил, что HTM также связывает свою транзакцию с базовым источником данных, а также с SessionFactory (если это возможно). Это позволяет коду использовать JdbcTemplate в области транзакции и, при условии, что DataSource, используемый JdbcTemplate, такой же, как SessionFactory, операции Jdbc будут участвовать в транзакции (используя то же базовое соединение).

Это меня очень огорчило сегодня, когда в моем распределителе идентификаторов гибернации был какой-то код, который создавал DataSourceTransactionManager и JdbcTemplate для распределения идентификаторов из таблицы высокого уровня. Я намеревался, что это будет автономная транзакция, которая получит следующее большое число и затем передаст изменения в таблицу идентификаторов. Однако из-за описанного выше поведения он фактически участвовал в моей «внешней» транзакции гибернации И еще хуже, совершая ее рано. Достаточно сказать не хорошо.

Я пытался поиграть с настройками распространения транзакции (использовал REQUIRES_NEW), но это не помогло.

Кто-нибудь знает лучший способ использовать JdbcTemplate в транзакции гибернации и НЕ заставлять их совместно использовать транзакцию, даже если они совместно используют один и тот же источник данных?

EDIT:

У меня есть SessionFactory (S), который создается пружинным LocalSessionFactoryBean с использованием DataSource (D). HibernateTransactionManager создается с этим SessionFactory (S).

некоторый код бизнес-логики будет выглядеть следующим образом ..

hibernateTransactionOperations.execute( new TransactionCallbackWithoutResult()
{
    @Override
    protected void doInTransactionWithoutResult( TransactionStatus status )
    {
        // some transactional code here using a HibernateTemplate

        // will include calls to id allocation when doing hibernateTemplate.save(obj)
    }
} );

мое распределение идентификаторов делает это (перефразировано), источник данных ниже тот же (D), что и используемый в SessionFactory (S).

PlatformTransactionManager txManager = new DataSourceTransactionManager( dataSource );
TransactionOperations transactionOperations = new TransactionTemplate( txManager );

return transactionOperations.execute( new TransactionCallback<Long>()
{
    public Long doInTransaction( TransactionStatus status )
    {
        return allocateBatchTxn( idKey, batchSize );
    }
} );

Когда транзакция транзакции, выполненная выше, завершается, он фиксирует основную транзакцию, которая выглядит так же, как и «внешняя» транзакция гибернации. Я подтвердил это, проверив блокировки / транзакции в БД.

Ответы [ 3 ]

2 голосов
/ 20 января 2011

Не создавайте новый DataSourceTransactionManager в своем коде распределения идентификаторов. Вместо этого используйте REQUIRES_NEW и HibernateTransactionManager .

В allocateBatchTxn() самый безопасный способ получить соединение JDBC - это метод Spring * DataSourceUtils.getConnection().

2 голосов
/ 21 января 2011

Отвечая на мой вопрос.

Основная причина моей проблемы - несколько вещей в HibernateTransactionManager.

  • Параметр autodetectDataSource со значением по умолчанию true
  • В afterPropertiesSet () с указанным выше значением true автоматически определяет источник данных из SessionFactory
  • В doBegin (), если DataSource не равен NULL, он будет связывать новые транзакции с SessionFactory И DataSource

Это вызывает мою проблему, потому что у меня также есть новый DataSourceTransactionManager, он все еще использует то же базовое хранилище (TransactionSynchronizationManager) для управления транзакциями, и потому что оба используют DataSource, вы получаете утечку транзакций между менеджерами txn. Я мог бы возразить, что менеджер txn должен включать свой собственный ключ / идентификатор в ключ для транзакционных ресурсов, поэтому они независимы, но, похоже, этого не происходит.

Ответ выше является разумным. Использование диспетчера гибернации txn вместо создания нового DataSourceTransactionManager и последующего использования REQURES_NEW решит проблему. Однако в моем случае это привело бы к циклической зависимости между HTM -> SessionFactory -> IdAllocator -> HTM.

Я придумал решение, которое работает, но не самое элегантное в истории.

Когда конструктор распределителя идентификаторов передается DataSource в конструктор. Я просто оборачиваю этот DataSource в делегирующую оболочку, которая проходит на 100%. Это изменяет ссылку на источник данных, поэтому логика txn не считает, что транзакция выполняется, и работает так, как я хочу.

2 голосов
/ 20 января 2011

Я пробовал с REQUIRES_NEW - он работает как положено (на HSQLDB), возможно, это зависит от БД:

// txManager is a HibernateTransactionManager obtained from the application context
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
transactionOperations.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);

return transactionOperations.execute(new TransactionCallback<Long>() {
    public Long doInTransaction( TransactionStatus status ) {
        return allocateBatchTxn( idKey, batchSize );
    }
}); 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...