简体   繁体   中英

Spring JDBC's transactions handling doesn't work with Google Guice

I use Google Guice and jOOQ in my project. Currently I decided to introduce transaction handling using Spring JDBC.

So I did the following.

I set a data source and a transaction manager in Guice module.

@Provides
@Singleton
DataSource provideDataSource(IExternalSettings settings) {
    Jdbc3PoolingDataSource dataSource = new Jdbc3PoolingDataSource();
    // configuring DataSource
    return dataSource;
}


@Provides
@Singleton
DataSourceTransactionManager provideDataSourceTransactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(new TransactionAwareDataSourceProxy(dataSource));
}

Then I inject my transaction manager to a persistence facade

@Inject
public PersistenceFacade(final DataSourceTransactionManager transactionManager) {
    this.dataSource = transactionManager.getDataSource();
    this.transactionManager = transactionManager;
}

Later, I use this data source to create jOOQ factory: new Factory(dataSource, ...) .

Finaly I run my database access code:

DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
    // db code in transaction
    transactionManager.commit(transaction);
    return result;
} catch (Exception e) {
    transactionManager.rollback(transaction);
    throw e;
}

So far, so good. It works as expected .

So, my next step is to introduce @Transactional annotation using Guice AOP. I created an interceptor

class TransactionalMethodInterceptor implements MethodInterceptor {

    @Inject
    private DataSourceTransactionManager transactionManager;

    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        try {
            Object result = invocation.proceed();
            transactionManager.commit(transaction);
            return result;
        } catch (Exception e) {
            transactionManager.rollback(transaction);
            throw e;
        }
    }
}

And configured it in configure() method of Guice module:

TransactionalMethodInterceptor transactionalMethodInterceptor = new TransactionalMethodInterceptor();
requestInjection(transactionalMethodInterceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionalMethodInterceptor);

And now the issues begin . I can see, using debugger, that control flow reaches interceptor. In particular, it reaches transactionManager.rollback(...) invocation. But the transaction is not actually roll backed .

I have no clue what's the reason. Any ideas? I'll be grateful. Thanks!

I finally managed to come back to this one and I think I found a solution.

First of all, I need to mention I introduced a bit of misunderstanding, when I said it all works without AOP. Today I couldn't reproduce it and I noticed that the connection that is rolled back is different than the one that jOOQ is using. So Alan Vrecko (see comment above) was right!

Then I found this answer and this snippet . I decided to give them a try and it worked! However, it appeared that all previous steps are valid and still need to be there (including Google Guice's interceptor).

The only I change I had to introduce was to remove DataSourceUtils.releaseConnection(con, dataSource); from SpringExceptionTranslationExecuteListener. exception(ExecuteContext ctx) SpringExceptionTranslationExecuteListener. exception(ExecuteContext ctx) . So finally the method looks like

@Override
public void exception(ExecuteContext ctx) {
    SQLException ex = ctx.sqlException();
    Statement stmt = ctx.statement();
    Connection con = ctx.getConnection();
    DataSource dataSource = ctx.getDataSource();
    JdbcUtils.closeStatement(stmt);
    ctx.exception(getExceptionTranslator(dataSource).translate("jOOQ", ctx.sql(), ex));
}

Then all seems to work properly. Thank you all for comments. Of course I'm still open to new/better solutions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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