事务导致多数据源切换失败问题剖析记录

1.2.1、框架

 Spring3+Mybatis3+Mybatis-spring-1.1.1

针对框架简单说几句,主要是为什么要做整合,方便后续的认知。

1、 Mybatis的sqlsession是否是线程安全的?为什么?

a) DefaultSqlSession是经常看到的一个实现类,源码中官方标注是线程不安全的

b) 因为当并发操作同一个DefaultSqlSession的时候拿到的具体Executor同样是同一个,其中会getConnection获取到同一个连接,那么在ThreadA做新增操作时,此时ThreadB正在做删除操作,ThreadAconnection执行完毕后会提交事务,将ThreadB操作一并提交,ThreadA再次查询时,导致ThreadA出现了脏读的现象。其次在一级缓存中也有可能出现转换类型的错误。

2、 spring整合mybatis如何做到保持sqlSession线程安全?

       (对应附件spring整合mybatis保证session安全简版.svg)

将sqlSessionTemplate代替DefaultSqlSession操作,这样会到一个jdk代理中,代理第一步获取session的时候就会查找当前线程的事务中是否有session,如果有就直接取出来用,做到线程之间的session隔离。该查询请求结束时,只是记录holder中session调用,不会真的提交,做到事务的整体控制。

1.2.2、启动加载(仅描述部分相关bean加载)

 dynamicDataSource:将所有数据源进行加载和映射存储,并设置好默认数据源,该类自身就可以看作一个DataSource。

 sqlSessionFactory:mybatis的sql操作都是由sqlSessionFactory的openSession取得的sqlSession进行db操作的,所以涉及到的数据源就加载了dynamicDataSource动态数据源。并且注意,注入的class是org.mybatis.spring.SqlSessionFactoryBean,可以看到已经是mybatis和spring的整合包内容,相当于内容已经开始整合,其次可以看到这是一个FactoryBean,实现了FactoryBean接口,会在IOC初始化实例的时候自行做一些操作,生产出合适的sqlSessionFactory,其中值得注意的是其中的afterPropertiesSet方法,在init初始化该bean的属性值之后,便会调用该方法,其中有这么一个创建this.transactionFactory = new SpringManagedTransactionFactory();将事务托付给了spring。这个类很重要,之后会遇到。

 transactionManager:spring提供的事务管理,与Mybatis整合后使用到的是DataSourceTransactionManager,该类同样要依赖数据源,所以同样注入dynamicDataSource动态数据源。

TransactionSynchronizationManager:该类全程参与事务上下文的信息同步管理,包括之前所说holder也就是从该类中获取(本质上就是一个线程变量ThreadLocal > resources )。

 TransactionInterceptor:要知道spring要做到声明式事务管理,是内置了一个Interceptor的,具体作用就是判断请求的methodName是否需要事务管理从而对。

 BaseDao:加载类BaseDaoImpl,该类继承了SqlSessionDaoSupport类,在注入sqlSessionFactory的时候就实例化了sqlSessionTemplate,也将sqlSessionProxy进行了实例化。

1.2.3、请求处理流程

 首先注意:openSession并不是在真正获取数据库连接,只是在应用侧开启会话对象,填充事务、执行器等必要的参数。真正获取连接是在executor的preperstatment的getConnection时候,才真正获取到了数据库连接(即多数据源切换得重点在于getConnection这一步)。

 Ps:橙色:我们手写的业务、工具类

 红色:Spring提供的类

 蓝色:Mybatis提供的类

 紫色:mybatis-spring整合类提供的类

 (对应附件事务与多数据源处理过程.svg)

 该时序图已经很详细叙述了数据源切换的位置(12-16)以及事务判断的位置和作用,故不再文字赘述(若有疑问可自行追寻源码分析)。

你可能感兴趣的:(事务导致多数据源切换失败问题剖析记录)