zoukankan      html  css  js  c++  java
  • 深入分析Spring混合事务

    在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事务管理器开启事务的那个数据库连接的。

  • 相关阅读:
    Oracle存储过程和自定义函数笔记
    怎样将一个Long类型的数据转换成字节数组
    Java集合框架整理
    有了这些,java IO就不愁了
    java生成Excel文件,下载
    ajax使用
    java transient关键字
    Mysql乐观锁与悲观锁
    Spring(六)Spring执行流程
    Spring(五)AOP
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157725.html
Copyright © 2011-2022 走看看