mybatis plus动态数据源切换及查询过程浅析
mybatis 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。
到此这篇关于mybatis plus动态数据源切换及查询过程浅析的文章就介绍到这了,更多相关mybatis plus动态数据源切换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!