与Guice和JDBC的事务-解决方案讨论

与Guice和JDBC的事务-解决方案讨论,jdbc,transactions,guice,transactionmanager,Jdbc,Transactions,Guice,Transactionmanager,在我的应用程序中,我需要将纯JDBC与Guice一起使用。但是,Guice不提供任何内置支持来管理事务。guice persist只提供基于JPA的支持,我不能使用JPA 因此,我尝试实现一个简单的解决方案,用Guice和JDBC管理事务。以下是第一个版本: 使用TransactionHolder存储每个线程的事务 公共类JdbcTransactionHolder{ private static ThreadLocal<JdbcTransaction> currentTransact

在我的应用程序中,我需要将纯JDBC与Guice一起使用。但是,Guice不提供任何内置支持来管理事务。guice persist只提供基于JPA的支持,我不能使用JPA

因此,我尝试实现一个简单的解决方案,用Guice和JDBC管理事务。以下是第一个版本:

  • 使用TransactionHolder存储每个线程的事务

    公共类JdbcTransactionHolder{

    private static ThreadLocal<JdbcTransaction> currentTransaction = new ThreadLocal<JdbcTransaction>();
    
    public static void setCurrentTransaction(JdbcTransaction transaction) {
        currentTransaction.set(transaction);
    }
    
    public static JdbcTransaction getCurrentTransaction() {
        return currentTransaction.get();
    }
    
    public static void removeCurrentTransaction() {
        currentTransaction.remove();
    }
    
    }

  • 为数据源实现一个包装器,如果事务已启动,该包装器可以从事务持有者获取当前连接:

    公共类JdbcDataSource实现了DataSource{

    private final static org.slf4j.Logger logger = LoggerFactory.getLogger(JdbcDataSource.class);
    
    private DataSource dataSource;
    
    public JdbcDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return dataSource.getLogWriter();
    }
    
    @Override
    public int getLoginTimeout() throws SQLException {
    
        return dataSource.getLoginTimeout();
    }
    
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    
        return dataSource.getParentLogger();
    }
    
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        this.dataSource.setLogWriter(out);
    }
    
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        this.dataSource.setLoginTimeout(seconds);
    }
    
    @Override
    public boolean isWrapperFor(Class<?> arg0) throws SQLException {
        return this.isWrapperFor(arg0);
    }
    
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
    
        return this.unwrap(iface);
    }
    
    @Override
    public Connection getConnection() throws SQLException {
        JdbcTransaction transaction = JdbcTransactionHolder.getCurrentTransaction();
        if(transaction != null) {
            // we get the connection from the transaction
            logger.debug("Transaction exists for the thread: {}.", Thread.currentThread());
            return transaction.getConnection();
        }
        Connection conn = this.dataSource.getConnection();
        conn.setAutoCommit(false);
        return conn;
    }
    
    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        JdbcTransaction transaction = JdbcTransactionHolder.getCurrentTransaction();
        if(transaction != null) {
            // we get the connection from the transaction
            logger.debug("Transaction exists for the thread: {}.", Thread.currentThread());
            return transaction.getConnection();
        }
    
        return this.dataSource.getConnection(username, password);
    }
    
  • 然后实现TransactionalMethodInterceptor来管理带有事务注释的方法的事务:

    公共类TransactionalMethodInterceptor实现MethodInterceptor{

    private final static Logger logger = LoggerFactory.getLogger(TransactionalMethodInterceptor.class);
    
    @Inject
    private JdbcTransactionManager transactionManager;
    
    @Override
    public Object invoke(MethodInvocation method) throws Throwable {
    
        try {
            // Start the transaction
            transactionManager.begin();
            logger.debug("Start to invoke the method: " + method);
    
            Object result = method.proceed();
            logger.debug("Finish invoking the method: " + method);
            transactionManager.commit();
            return result;
        } catch (Exception e) {
            logger.error("Failed to commit transaction!", e);
            try {
                transactionManager.rollback();
    
            }
            catch(Exception ex) {
                logger.warn("Cannot roll back transaction!", ex);
            }
            throw e;
        }
    }
    
    }

  • 最后,将代码放在一起,以便Guice可以注入实例:

    bind(DataSource.class).toProvider(DataSourceProvider.class).in(Scopes.SINGLETON);
    
    bind(TransactionManager.class).to(JdbcTransactionManager.class);
    TransactionalMethodInterceptor transactionalMethodInterceptor = new TransactionalMethodInterceptor();
    requestInjection(transactionalMethodInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionalMethodInterceptor);
    
    
    bind(TestDao.class).to(JdbcTestDao.class);
    bind(TestService.class).to(TestServiceImpl.class);
    
  • 我使用c3p0作为数据源池。因此,它在我的测试中运行良好

    我发现另一个相关问题:

    但到目前为止,我还没有找到任何类似的方法,除了SpringFramework中的一些方法。但是,即使是Spring中的实现也似乎相当复杂

    我想问一下,是否有人对这个解决方案有任何建议


    谢谢。

    记住一件事:不使用RuntimeException,而是需要定义特定于处理错误的异常。
    private final static Logger logger = LoggerFactory.getLogger(TransactionalMethodInterceptor.class);
    
    @Inject
    private JdbcTransactionManager transactionManager;
    
    @Override
    public Object invoke(MethodInvocation method) throws Throwable {
    
        try {
            // Start the transaction
            transactionManager.begin();
            logger.debug("Start to invoke the method: " + method);
    
            Object result = method.proceed();
            logger.debug("Finish invoking the method: " + method);
            transactionManager.commit();
            return result;
        } catch (Exception e) {
            logger.error("Failed to commit transaction!", e);
            try {
                transactionManager.rollback();
    
            }
            catch(Exception ex) {
                logger.warn("Cannot roll back transaction!", ex);
            }
            throw e;
        }
    }
    
    bind(DataSource.class).toProvider(DataSourceProvider.class).in(Scopes.SINGLETON);
    
    bind(TransactionManager.class).to(JdbcTransactionManager.class);
    TransactionalMethodInterceptor transactionalMethodInterceptor = new TransactionalMethodInterceptor();
    requestInjection(transactionalMethodInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionalMethodInterceptor);
    
    
    bind(TestDao.class).to(JdbcTestDao.class);
    bind(TestService.class).to(TestServiceImpl.class);