简体   繁体   English

将spring JDBC事务与hibernate事务隔离开来

[英]Isolating a spring JDBC transaction from a hibernate transaction

In spring the HibernateTransactionManager uses the SessionFactory it was initialised with to "bind" a Session to the current thread context when creating a new transaction. 在Spring中,HibernateTransactionManager使用它初始化的SessionFactory在创建新事务时将Session“绑定”到当前线程上下文。 Then when HibernateTemplate is used it find that bound Session and uses it. 然后,当使用HibernateTemplate时,它会找到绑定的Session并使用它。

However I found today that HTM also binds its transaction to the underlying DataSource as well as the SessionFactory (if possible). 但是我今天发现HTM还将其事务绑定到底层DataSource以及SessionFactory(如果可能的话)。 This allows code to use JdbcTemplate within the transaction scope and, provided the DataSource used by JdbcTemplate is the same as the SessionFactory uses, the Jdbc operations will participate in the transaction (using the same underlying Connection). 这允许代码在事务范围内使用JdbcTemplate,并且如果JdbcTemplate使用的DataSource与SessionFactory使用的相同,则Jdbc操作将参与事务(使用相同的底层连接)。

This bit me quite badly today when I had some code in my hibernate id allocator that was creating a DataSourceTransactionManager and JdbcTemplate to allocate ids out of a high-lo table. 今天,当我在我的hibernate id分配器中创建了一些代码来创建一个DataSourceTransactionManager和JdbcTemplate以从高级表中分配id时,这一点非常糟糕。 I was intending that this be a standalone transaction that would fetch the next high number and then commit the change to the id table. 我打算这是一个独立的事务,它将获取下一个高位数,然后将更改提交给id表。 However because of the above behaviour it was actually participating in my "outer" hibernate transaction AND even worse committing it early. 然而,由于上述行为,它实际上参与了我的“外部”hibernate事务,更糟糕的是提前提交它。 Suffice to say not good. 我只想说不好。

I tried playing around with transaction propogation settings (used REQUIRES_NEW) but this didn't help. 我尝试使用事务传播设置(使用REQUIRES_NEW),但这没有帮助。

Does anyone know the best way to use JdbcTemplate within a hibernate transaction and NOT have them share a transaction, even tho they share the same DataSource? 有没有人知道在hibernate事务中使用JdbcTemplate的最佳方法,而不是让它们共享一个事务,即使它们共享相同的DataSource?

EDIT: 编辑:

I have a SessionFactory (S) which is created by the spring LocalSessionFactoryBean using a DataSource (D). 我有一个SessionFactory(S),它由Spring LocalSessionFactoryBean使用DataSource(D)创建。 The HibernateTransactionManager is created with that SessionFactory (S). 使用该SessionFactory(S)创建HibernateTransactionManager。

some business logic code would look like this.. 一些业务逻辑代码看起来像这样..

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)
    }
} );

my id allocation does this (paraphrased), the DataSource below is the same (D) as the one used in the SessionFactory (S). 我的id分配执行此操作(释义),下面的DataSource与SessionFactory(S)中使用的相同(D)。

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 );
    }
} );

When the transactionOperations execute above completes it will commit the underlying transaction which seems to be the same as the 'outer' hibernate transaction. 当transactionOperations在上面执行完成时,它将提交底层事务,它似乎与'外部'hibernate事务相同。 I have confirmed this by checking locks/transactions in the DB. 我通过检查数据库中的锁/事务来确认这一点。

I tried it with REQUIRES_NEW - it works as expected (on HSQLDB), perhaps it's DB-dependent: 我用REQUIRES_NEW尝试了它 - 它按预期工作(在HSQLDB上),也许它依赖于DB:

// 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 );
    }
}); 

Don't create a new DataSourceTransactionManager in your id allocation code. 不要在id分配代码中创建新的DataSourceTransactionManager。 Instead use REQUIRES_NEW and the HibernateTransactionManager . 而是使用REQUIRES_NEW和HibernateTransactionManager

In allocateBatchTxn() , the safest way to get the JDBC connection is via Spring's DataSourceUtils.getConnection() method. allocateBatchTxn() ,获取JDBC连接的最安全方法是通过Spring的DataSourceUtils.getConnection()方法。

Answering my own question. 回答我自己的问题。

The root cause of my problem is a couple of things in HibernateTransactionManager. 我的问题的根本原因是HibernateTransactionManager中的一些事情。

  • The setting 'autodetectDataSource' which defaults to true 设置'autodetectDataSource',默认为true
  • In afterPropertiesSet() with the above true it auto-detects the DataSource from the SessionFactory 在具有上述true的afterPropertiesSet()中,它会自动检测SessionFactory中的DataSource
  • In doBegin() if the DataSource is not null it will bind new transactions to the SessionFactory AND the DataSource 在doBegin()中,如果DataSource不为null,它会将新事务绑定到SessionFactory和DataSource

This is causing my problem because also I have a new DataSourceTransactionManager it still uses the same underlying storage (TransactionSynchronizationManager) to manage transactions and because both use the DataSource you get this leaking of transactions between txn managers. 这导致了我的问题,因为我还有一个新的DataSourceTransactionManager,它仍然使用相同的底层存储(TransactionSynchronizationManager)来管理事务,因为两者都使用DataSource,这会导致txn管理器之间的事务泄漏。 I might argue that a txn manager should include its own 'key/id' in the key for the transactional resources so there are independent but it doesn't appear to do that. 我可能会争辩说,txn管理器应该在事务资源的密钥中包含自己的“密钥/ id”,因此它们是独立的,但似乎并没有这样做。

The response above are sensible. 上面的回应是明智的。 Using the hibernate txn manager rather than creating a new DataSourceTransactionManager and then using REQURES_NEW would solve the problem. 使用hibernate txn管理器而不是创建新的DataSourceTransactionManager然后使用REQURES_NEW可以解决问题。 However in my case that would introduce a circular dependency between HTM -> SessionFactory -> IdAllocator -> HTM. 但是在我的情况下会在HTM - > SessionFactory - > IdAllocator - > HTM之间引入循环依赖。

I came up a solution that works but isn't the most elegant thing ever. 我提出了一个有效但不是最优雅的解决方案。

When constructor the id allocator it is passed a DataSource in the constructor. 当构造函数id分配器时,它在构造函数中传递一个DataSource。 I simply wrap that DataSource in a delegating wrapper that is 100% pass through. 我只是将DataSource包装在一个100%传递的委托包装器中。 This changes the DataSource reference so the txn logic does not think there is a transaction in progress and works as I want it to. 这会更改DataSource引用,因此txn逻辑不会认为正在进行中的事务并且按我的意愿工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM