spring--事务原理、mybatis--MapperScannerConfigurer 和 mybatis--MapperProxy事务,最近想把spring mybatis中的事务和mapper接口的原理分析分析,陆陆续续写了些,这篇做个总结。
spring+mybatis mapper接口 声明式事务配置
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" lazy-init="false"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:sqlmapper/*Mapper.xml"/> <property name="plugins"> <list> <bean class="***"> <property name="dialect"> <bean class="***"/> </property> </bean> </list> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="*.*.*" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="do*" read-only="false" rollback-for="java.lang.Exception"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pc" expression="execution(* springtry.web.service.*.*(..))"/> <aop:advisor pointcut-ref="pc" advice-ref="txAdvice"/> </aop:config>
ps:以上在service层注入事务,do开头方法传播机制是required,其它的是supports
1. org.mybatis.spring.SqlSessionFactoryBean解析mapper xml配置,根据namespace添加class mapper组装Configuration对象,设置它的environment(包含springManagedTransactionFactory),最后getObject()包装出DefaultSqlSessionFactory;
2. org.mybatis.spring.mapper.MapperScannerConfigurer扫描basePackage下的mapper接口,每个接口封装为MapperFactoryBean(包含封装了DefaultSqlSessionFactory的SqlSessionTemplate),注册给spring容器。
ps:当调用"mapper接口"时去DefaultSqlSessionFactory-》Configuration获得接口类的代理类
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
3. 前面两步是系统的前提工作。
从service层方法开始,首先进入TransactionInerceptor事务拦截器,由 DataSourceTransactionManager 生成一个DataSourceTransactionObject事务对象,封装在DefaultTransactionStatus中,调用doBegin开启这个事务对象,设置这个事务对象 setConnectionHolder(new ConnectionHolder(Connection ex)),把这个ConnectionHolder 以键值对绑定在当前线程上 <this.getDataSource(),txObject.getConnectionHolder()>:
TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());
返回这个 DefaultTransactionStatus对象,封装在TransactionAspectSupport.TransactionInfo中,绑定在当前线程上,之后调用实际service方法
4. service方法中会注入mybatis mapper接口,调用时实际调用
MapperFactoryBean-》SqlSessionTemplate-》DefaultSqlSessionFactory-》Configuration.getMapper(mapper接口, SqlSessionTemplate);
返回MapperProxy对mapper接口的代理,接着调用MapperProxy
5. MapperProxy中,根据调用的Method返回一个MapperMethod对象.execute(SqlSessionTemplate this.sqlSession, args),其中调用sqlSession.insert, sqlSession.update,sqlSession.delete,sqlSession.selectList,sqlSession.selectMap,sqlSession.selectOne等,
6. 接下来调用SqlSessionTemplate的方法,实际调用SqlSessionTemplate.sqlSessionProxy(SqlSessionTemplate.SqlSessionInterceptor拦截器拦截的代理),拦截器中使用 SqlSessionUtils 调用
SqlSessionTemplate.DefaultSqlSessionFactory.openSession-》Configuration-》environment-》springManagedTransactionFactory
生成一个SpringManagedTransaction事务对象,封装在一个Executor(SimpleExecutor/ReuseExecutor/BatchExecutor)中,封装在DefaultSqlSession中并返回。
封装在SqlSessionHolder中,并以键值对绑定在当前线程<DefaultSqlSessionFactory,SqlSessionHolder>
SqlSessionUtils 返回DefaultSqlSession
7. 调用DefaultSqlSession的(update/select等),其中
MappedStatement e = this.configuration.getMappedStatement(接口名.方法名 statement);
this.executor.query/update
8. 回到第6步生成的Executor,doupdate/doquery时会prepareStatement,其中会getConnection调用SpringManagedTransaction事务对象的getConnection(),
DataSourceUtils.getConnection(this.dataSource);
看 mybatis--MapperProxy事务 最后分析的
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
使用的是第三步当前线程的键值对,也就是service层开启spring事务的connection
其实,神秘的spring事务本质还是jdbc的connection,跟我们直接使用jdbc是一样的,但是做了增强