mybatis源码 mybatis的事务如何被spring管理

2022-09-28 09:55:19

要想使用spring的事务,要加入mybatis-spring依赖包

<!-- 引用插件依赖:MyBatis整合Spring --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency>

配置文件:

<?xml version="1.0" encoding="UTF-8"?><context:property-placeholderlocation="classpath:properties/*.properties" /><beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"destroy-method="close"><propertyname="url"value="${jdbc.url}" /><propertyname="username"value="${jdbc.username}" /><propertyname="password"value="${jdbc.password}" /><propertyname="driverClassName"value="${jdbc.driver}" /><propertyname="maxActive"value="10" /><propertyname="minIdle"value="5" /></bean><!-- sqlsessionfactory --><!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 --><beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 数据库连接池 --><propertyname="dataSource"ref="dataSource" /><!-- 加载mybatis的全局配置文件 --><propertyname="configLocation"value="classpath:mybatis/SqlMapConfig.xml" /></bean><!-- mapper扫描器 --><beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer"><propertyname="basePackage"value="com.taotao.mapper" /></bean>

配置sqlSessionFactory给spring来管理
SqlSessionFactoryBean这是一个FactoryBean相信读过spring源码的都知道
org.mybatis.spring.SqlSessionFactoryBean#getObject

@Overridepublic SqlSessionFactorygetObject()throws Exception {if (this.sqlSessionFactory ==null) {//sqlSessionFactory没有构建好,那么就调用afterPropertiesSet
      afterPropertiesSet();
    }//返回构建好的sqlSessionFactoryreturnthis.sqlSessionFactory;
  }

org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSet

@OverridepublicvoidafterPropertiesSet()throws Exception {
    notNull(dataSource,"Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder,"Property 'sqlSessionFactoryBuilder' is required");
    .....//构建SqlSessionFactorythis.sqlSessionFactory = buildSqlSessionFactory();
  }

org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory

protected SqlSessionFactorybuildSqlSessionFactory()throws IOException {

    .....if (this.transactionFactory ==null) {this.transactionFactory =new SpringManagedTransactionFactory();
    }

    configuration.setEnvironment(new Environment(this.environment,this.transactionFactory,this.dataSource));

    ......returnthis.sqlSessionFactoryBuilder.build(configuration);
  }

这段代码可以看出transactionFactory 等于null的时候回去创建一个SpringMngTransFactory
并且把transFactory设置给Environment然后再把Environment设置给configuration,最后
sqlSessionFactoryBuilder.build(configuration);返回sqlSessionFactory


在DefaultSqlSessionFactory#openSessionFromConnection方法中创建了Transaction

private SqlSessionopenSessionFromConnection(ExecutorType execType, Connection connection) {try {boolean autoCommit;try {
        autoCommit = connection.getAutoCommit();
      }catch (SQLException e) {// Failover to true, as most poor drivers// or databases won't support transactions
        autoCommit =true;
      }//从configuration中取出environment对象final Environment environment = configuration.getEnvironment();//从environment中取出TransactionFactoryfinal TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//创建Transactionfinal Transaction tx = transactionFactory.newTransaction(connection);//创建包含事务操作的执行器final Executor executor = configuration.newExecutor(tx, execType);//构建包含执行器的SqlSessionreturnnew DefaultSqlSession(configuration, executor, autoCommit);
    }catch (Exception e) {throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    }finally {
      ErrorContext.instance().reset();
    }
  }

DefaultSqlSessionFactory#getTransactionFactoryFromEnvironment

private TransactionFactorygetTransactionFactoryFromEnvironment(Environment environment) {//如果没有配置事务工厂,则返回托管事务工厂if (environment ==null || environment.getTransactionFactory() ==null) {returnnew ManagedTransactionFactory();
    }return environment.getTransactionFactory();
  }

这里返回SpringManagedTransactionFactory对象,然后调用
SpringManagedTransactionFactory#newTransaction(javax.sql.DataSource, org.apache.ibatis.session.TransactionIsolationLevel, boolean)

@Overridepublic TransactionnewTransaction(DataSource dataSource, TransactionIsolationLevel level,boolean autoCommit) {returnnew SpringManagedTransaction(dataSource);
  }

也就是说mybatis的执行事务的事务管理器就切换成了SpringManagedTransaction
我们再看org.mybatis.spring.transaction.SpringManagedTransaction#getConnection

@Overridepublic ConnectiongetConnection()throws SQLException {if (this.connection ==null) {//获取connection
      openConnection();
    }returnthis.connection;
  }

privatevoidopenConnection()throws SQLException {//调用DataSourceUtils 拿到数据库连接this.connection = DataSourceUtils.getConnection(this.dataSource);this.autoCommit =this.connection.getAutoCommit();this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection,this.dataSource);

  .....

org.springframework.jdbc.datasource.DataSourceUtils#getConnection

publicstatic ConnectiongetConnection(DataSource dataSource)throws CannotGetJdbcConnectionException {try {return doGetConnection(dataSource);
        }catch (SQLException ex) {thrownew CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
        }
    }

此处进入spring-jdbc的模块代码,猜想:

  1. spring的@trans事务操作的conn会保存在一个ThreadLocal中
  2. 当mybatis操作数据库时从这个ThreadLocal中去取conn,这样就可以做到spring来控制mybatis的数据库操作了

下面我们就来验证我们的猜想:
org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

publicstatic ConnectiondoGetConnection(DataSource dataSource)throws SQLException {
        Assert.notNull(dataSource,"No DataSource specified");//获取ConnectionHolder
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);/**
         * conHolder不为空 有链接
         */if (conHolder !=null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
            conHolder.requested();//如果conHolder没有连接if (!conHolder.hasConnection()) {
                logger.debug("Fetching resumed JDBC Connection from DataSource");//取出一个连接设置给conHolder
                conHolder.setConnection(fetchConnection(dataSource));
            }//返回conHolder中的连接return conHolder.getConnection();
        }

        .....

想看怎么获取connHolder
org.springframework.transaction.support.TransactionSynchronizationManager#getResource

    @Nullablepublicstatic ObjectgetResource(Object key) {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);//获取ConnectionHolder
        Objectvalue = doGetResource(actualKey);
        ....returnvalue;
    }
//保存数据库连接的ThreadLocalprivatestaticfinal ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");@Nullableprivatestatic ObjectdoGetResource(Object actualKey) {/**
         * 从threadlocal <Map<Object, Object>>中取出来当前线程绑定的map
         * map里面存的是<dataSource,ConnectionHolder>
         */
        Map<Object, Object> map = resources.get();if (map ==null) {returnnull;
        }//map中取出来对应dataSource的ConnectionHolder
        Object value = map.get(actualKey);// Transparently remove ResourceHolder that was marked as void...if (valueinstanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
            map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {
                resources.remove();
            }
            value =null;
        }return value;
    }

可以看到确实是从ThreadLocal中取出来的conn,而spring自己的事务也是操作的这个ThreadLocal中的conn来进行事务的开启和回滚
然后就取出来connHolder中的conn返回,当取出来的conn为空时候,调用
org.springframework.jdbc.datasource.DataSourceUtils#fetchConnection

privatestatic ConnectionfetchConnection(DataSource dataSource)throws SQLException {//从数据源取出来conn
        Connection con = dataSource.getConnection();if (con ==null) {thrownew IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
        }return con;
    }

然后把从数据源取出来的连接返回

到此mybatis的事务是怎么被spring管理的就显而易见了

  • 作者:靛蓝忆
  • 原文链接:https://blog.csdn.net/u011702633/article/details/82153743
    更新时间:2022-09-28 09:55:19