在ORM框架的事务管理器的事务内,使用JdbcTemplate执行SQL是不会纳入事务管理的。

下面进行源码分析,看为什么必须要在DataSourceTransactionManager的事务内使用JdbcTemplate。


1开启事务

DataSourceTransactionManager


protected void doBegin(Object transaction,TransactionDefinition definition) {                    DataSourceTransactionObjecttxObject = (DataSourceTransactionObject) transaction;                    Connection con = null;                      try {                             if(txObject.getConnectionHolder() == null ||                                                txObject.getConnectionHolder().isSynchronizedWithTransaction()){                                      ConnectionnewCon = this.dataSource.getConnection();                                      if(logger.isDebugEnabled()) {                                                logger.debug("AcquiredConnection [" + newCon + "] for JDBC transaction");                                      }                                      txObject.setConnectionHolder(newConnectionHolder(newCon), true);                             }                               txObject.getConnectionHolder().setSynchronizedWithTransaction(true);                             con =txObject.getConnectionHolder().getConnection();                               IntegerpreviousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con,definition);                             txObject.setPreviousIsolationLevel(previousIsolationLevel);                               // Switch to manualcommit if necessary. This is very expensive in some JDBC drivers,                             // so we don't wantto do it unnecessarily (for example if we've explicitly                             // configured theconnection pool to set it already).                             if(con.getAutoCommit()) {                                      txObject.setMustRestoreAutoCommit(true);                                      if(logger.isDebugEnabled()) {                                                logger.debug("SwitchingJDBC Connection [" + con + "] to manual commit");                                      }                                      con.setAutoCommit(false);                             }                             txObject.getConnectionHolder().setTransactionActive(true);                               int timeout =determineTimeout(definition);                             if (timeout !=TransactionDefinition.TIMEOUT_DEFAULT) {                                      txObject.getConnectionHolder().setTimeoutInSeconds(timeout);                             }                               // Bind the sessionholder to the thread.                             if(txObject.isNewConnectionHolder()) {                                      TransactionSynchronizationManager.bindResource(getDataSource(),txObject.getConnectionHolder());                             }                    }                      catch (Exception ex) {                             DataSourceUtils.releaseConnection(con,this.dataSource);                             throw newCannotCreateTransactionException("Could not open JDBC Connection fortransaction", ex);                    }          }


doBegin()方法会以数据源名为Key,ConnectionHolder(保存着连接)为Value,将已经开启事务的数据库连接绑定到一个ThreadLocal变量上。


2绑定连接

TransactionSynchronizationManager


public static void bindResource(Objectkey, Object value) throws IllegalStateException {                    Object actualKey =TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);                    Assert.notNull(value,"Value must not be null");                    Map<Object, Object> map = resources.get();                    // set ThreadLocal Map ifnone found                    if (map == null) {                             map = newHashMap<Object, Object>();                             resources.set(map);                    }                    Object oldValue = map.put(actualKey, value);                    // Transparently suppress aResourceHolder that was marked as void...                    if (oldValue instanceofResourceHolder && ((ResourceHolder) oldValue).isVoid()) {                             oldValue = null;                    }                    if (oldValue != null) {                             throw newIllegalStateException("Already value [" + oldValue + "] for key[" +                                                actualKey+ "] bound to thread [" + Thread.currentThread().getName() +"]");                    }                    if (logger.isTraceEnabled()){                             logger.trace("Boundvalue [" + value + "] for key [" + actualKey + "] to thread[" +                                                Thread.currentThread().getName()+ "]");                    }          }


resources变量就是上面提到的ThreadLocal变量,这样后续JdbcTemplate就可以用DataSource作为Key,查找到这个数据库连接。


3执行SQL

JdbcTemplate


public Objectexecute(PreparedStatementCreator psc, PreparedStatementCallback action)                             throwsDataAccessException {                      Assert.notNull(psc,"PreparedStatementCreator must not be null");                    Assert.notNull(action,"Callback object must not be null");                    if (logger.isDebugEnabled()){                             String sql =getSql(psc);                             logger.debug("Executingprepared SQL statement" + (sql != null ? " [" + sql +"]" : ""));                    }                      Connection con = DataSourceUtils.getConnection(getDataSource());                    PreparedStatement ps = null;                    try {                             Connection conToUse= con;                             if(this.nativeJdbcExtractor != null &&                                                this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()){                                      conToUse =this.nativeJdbcExtractor.getNativeConnection(con);                             }                             ps =psc.createPreparedStatement(conToUse);                             applyStatementSettings(ps);                             PreparedStatementpsToUse = ps;                             if(this.nativeJdbcExtractor != null) {                                      psToUse =this.nativeJdbcExtractor.getNativePreparedStatement(ps);                             }                             Object result =action.doInPreparedStatement(psToUse);                             handleWarnings(ps);                             return result;                    }                    catch (SQLException ex) {                             // ReleaseConnection early, to avoid potential connection pool deadlock                             // in the case whenthe exception translator hasn't been initialized yet.                             if (psc instanceofParameterDisposer) {                                      ((ParameterDisposer)psc).cleanupParameters();                             }                             String sql =getSql(psc);                             psc = null;                             JdbcUtils.closeStatement(ps);                             ps = null;                             DataSourceUtils.releaseConnection(con,getDataSource());                             con = null;                             throwgetExceptionTranslator().translate("PreparedStatementCallback", sql,ex);                    }                    finally {                             if (psc instanceofParameterDisposer) {                                      ((ParameterDisposer)psc).cleanupParameters();                             }                             JdbcUtils.closeStatement(ps);                             DataSourceUtils.releaseConnection(con,getDataSource());                    }          }



4获得连接

DataSourceUtils


public static Connection doGetConnection(DataSourcedataSource) throws SQLException {                    Assert.notNull(dataSource,"No DataSource specified");                      ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);                    if (conHolder != null&& (conHolder.hasConnection() ||conHolder.isSynchronizedWithTransaction())) {                             conHolder.requested();                             if(!conHolder.hasConnection()) {                                      logger.debug("Fetchingresumed JDBC Connection from DataSource");                                      conHolder.setConnection(dataSource.getConnection());                             }                             returnconHolder.getConnection();                    }                    // Else we either got noholder or an empty thread-bound holder here.                      logger.debug("FetchingJDBC Connection from DataSource");                    Connection con =dataSource.getConnection();                      if (TransactionSynchronizationManager.isSynchronizationActive()){                             logger.debug("Registeringtransaction synchronization for JDBC Connection");                             // Use sameConnection for further JDBC actions within the transaction.                             // Thread-boundobject will get removed by synchronization at transaction completion.                             ConnectionHolderholderToUse = conHolder;                             if (holderToUse ==null) {                                      holderToUse= new ConnectionHolder(con);                             }                             else {                                      holderToUse.setConnection(con);                             }                             holderToUse.requested();                             TransactionSynchronizationManager.registerSynchronization(                                                newConnectionSynchronization(holderToUse, dataSource));                             holderToUse.setSynchronizedWithTransaction(true);                             if (holderToUse !=conHolder) {                                      TransactionSynchronizationManager.bindResource(dataSource,holderToUse);                             }                    }                      return con;          }



由此可见,DataSourceUtils也是通过TransactionSynchronizationManager获得连接的。所以只要JdbcTemplate与DataSourceTransactionManager有相同的DataSource,就一定能得到相同的数据库连接,自然就能正确地提交、回滚事务。


再以Hibernate为例来说明开篇提到的问题,看看为什么ORM框架的事务管理器不能管理JdbcTemplate。


5 ORM事务管理器

HibernateTransactionManager


if(txObject.isNewSessionHolder()) {                                      TransactionSynchronizationManager.bindResource(getSessionFactory(),txObject.getSessionHolder());                             }


因为ORM框架都不是直接将DataSource注入到TransactionManager中使用的,而是像上面Hibernate事务管理器一样,使用自己的SessionFactory等对象来操作DataSource。所以尽管可能SessionFactory和JdbcTemplate底层都是一样的数据源,但因为在TransactionSynchronizationManager中绑定时使用了不同的Key(一个是sessionFactory名,一个是dataSource名),所以JdbcTemplate执行时是拿不到ORM事务管理器开启事务的那个数据库连接的。