mybatis plus动态数据源切换及查询过程浅析
程序源程序 人气:0mybatis plus多数据源切换
mybatis plus多数据源切换使用注解 @DS
DS注解作为多数据源切点,具体实现作用主要由两个类完成
DynamicDataSourceAnnotationAdvisor
DynamicDataSourceAnnotationInterceptor
DS多数据源切换实现
1.DynamicDataSourceAnnotationAdvisor类实现切面配置,其中AnnotationMatchingPointcut用于寻找切点,进入可看到支持类和方法的切点,多个切点会执行多次,根据代码顺序,方法的切点执行晚于类切点,所以方法的切点会覆盖类,但是都会被执行
2.DynamicDataSourceAnnotationInterceptor 实现切面功能,匹配到切点后,根据切点值(数据源id)设置当前线程有效数据源私有变量,用于执行查询时动态数据源能获取,执行完毕会清楚此变量,此存储功能由DynamicDataSourceContextHolder提供
3.执行时动态数据源确定,mybatisplus动态数据源实现类为DynamicRoutingDataSource,其维护一个map保存所有配置的数据源,以数据源ID作为key,执行查询时,获取连接,交由spring事务管理器SpringManagedTransaction进行连接获取,若当前存在连接则直接返回,不存在是创建连接,只有创建连接时,才有机会切换数据源,此处需要注意的是,同一事务下,使用的同一个sqlSession,执行查询时使用的是同一个执行器executor,最终使用的是同一个事务管理器,所以获取连接时无法创建新连接,mybatisplus切换数据源功能失效,代码无法被执行
此处逻辑较多,一笔带过有点草率,后续附带mybatisplus查询过程,感兴趣的老铁一会在看。
4.动态数据源敲定,接第3步,若当前事务管理器还未创建连接,那就打开一个连接,使用DataSourceUtils获取一个连接,入参为mybatisplus的动态数据源DynamicRoutingDataSource,一步步往下巴拉,忽略不需要代码,最终执行到了这一句:
Connection con = dataSource.getConnection();
使用DynamicRoutingDataSource获取连接,瞧,兜兜转转,最终怎么找连接,敲定数据源又交给了mybatisplus,getConnection方法在其父级AbstractRoutingDataSource中,使用this.determineDataSource().getConnection()获取连接,.getConnection()是数据源获取连接方法,那确定数据源顾名思义就是determineDataSource方法了,这个方法的实现就在DynamicRoutingDataSource中,来瞧瞧!是不是瞬间舒服了,根据第二部切面设置的数据源,这个返回对应的数据源。
5.主要逻辑已经清楚了,那么来延伸下,如何手动切换数据源mybatisplus切换数据源主要是使用DynamicDataSourceContextHolder的线程独享变量,那么如果没有DS切点,无法自动切换数据源,需要切换数据源时就可以使用DynamicDataSourceContextHolder.setDataSourceLookupKey 设置数据源,使用完后再清除掉(默认数据源生效),这个方法同第4步所提的事务问题,存在事务则无法切换(压根就不执行 只设置无法执行切换代码),如果将要执行的代码已存在切点,则执行前手动设置也是无效的,因为切点会覆盖你的设置。
注:如果需要在事务存在的情况下切换数据源,则估计要覆盖掉或替换掉spring的事务管理器,此处待后续再议。如果事务内仍需要切换数据源,则需要单独定义service并设置切点,设置此切点的事务传播行为为PROPAGATION__REQUIRES_NEW,则执行切面方法时单独创建一个事务,数据源会自动切换。
mybatisplus执行查询过程
现分析mybatisplus执行查询过程
1.执行selectById方法,执行return this.baseMapper.selectById(id);经过springaop切面进行一系列巴拉巴拉的处理,最终进入mybatisplus PageMapperMethod类中执行execute方法,根据sql类型进行不同处理,分新增,修改,删除,查询,我们本次只关注查询,查询里也有很多东西。
可以看到此处提供多种返回值的查询,有空返回值,多个,map,游标,及啥也不是。空值查询猜测是另有处理器直接处理返回值,此处不做延伸了,有需要再议,回到按照ID查询及进入啥也不是分支,本次查询不是分页查询,直接进入selectOne ,result = sqlSession.selectOne(this.command.getName(), param); 这里的sqlSession是SqlSessionTemplate,执行selectOne时首先获取sqlSession(默认为DefaultSelSession)
2.获取真正的sqlSession并执行selectOne查询,首先使用事务管理器获取缓存的sqlSession的持有者(sqlSession包装类),不存在则创建并缓存注册。
得到真正的sqlSession后,执行selectOne,发现还是执行的selectList,然后就是大众写法,size=1返回get(0),否则为空或者报错结果集太多。
3.来看看selectList在干嘛,先获取查询语句相关的MappedStatement,然后使用执行器executor执行查询 this.executor.query
执行器是啥?怎么来的?
执行器是sqlSession内部的一个属性,sqlSession其实也是个外包装,提供了一堆规范化的操作,但是并不直接实现这些操作,而是交给执行器,执行器来执行增删改查,默认使用的是SimpleExecutor,就以他入手,SimpleExecutor继承抽象类BaseExecutor,BaseExecutor实现了Executor接口,这个query就在BaseExecutor中:这里主要是获取执行的sql,以及根据执行语句和入参来生成一个缓存的key,会缓存查询结果,如果下次再来个一毛一样的查询 就直接用缓存了,不查了,这个就是mybatis的一级缓存,缓存使用的是一个封装的类PerpetualCache,最终对应的就是一个map :
private Map<Object, Object> cache = new HashMap();
4.执行数据库查询queryFromDatabase -> doQuery,这里和spring一个习惯,真正做事情的都是doXXX前面都是铺垫前戏,看的人云里雾里的
这个doQuery的实现在SimpleExecutor中,进入查看,
先创建了一个Statement,这个我知道,是jdbc里的东西,jdbc大概就是加载驱动,获取连接,打开一个执行语句块,然后执行获取结果。具体这个东西学名和作用还是百度下吧:
Statement 对象用于把 SQL 语句发送到 DBMS。你只须简单地创建一个 Statement 对象并且然后执行它,使用适当的方法执行你发送的 SQL 语句。
这个也就是jdbc层面的sqlSession了吧。
创建完Statement后就会执行查询,先看来看Statement如何创建
5.Statement创建,创建第一步获取connection,这个就涉及到数据源了吧,进入查看,使用事务管理器Transaction获取连接,默认的事务管理器是SpringManagedTransaction,然后获取数据源,最终创建或使用一个已有的连接并返回,进而创建出一个Statement,其他细节已无心再细究,不影响本次分析目的。
6.执行查询,直接略过看最终执行处,doFinish-> query ,执行jdbc的PreparedStatement.execute,之后的代码就先不看了,看意思就是将原始ResultSet结果集转化为list。
加载全部内容