首先我们来看看DataSourceUtils.doGetConnection的源码:spring-jdbc jar下的org.springframework.jdbc.datasource提供用来获取数据库连接

通过前几篇幅对mybatis的执行过程分析可知: 

在新建defaultSqlSession实例时,其内部成员对象SpringManagedTransaction实例内拥有的connect对象还是null,但最终在执行SimpleExecutor.prepareStatement方法内获取真实连接时会跳入DataSourceUtils.doGetConnection

Java 获取mybatis 获取sql语句_python

那关键来了,这个getConnection()做了什么呢?



结论

  1. TDataSource 与 mycat的实现不同:
    前者是在应用程序内通过分库分表策略(TableRule) dispatcher到具体的单个库(TGroupDataSource)具体单个表,通过TDruidDataSource (实际上内部还是DruidDataSource) 来具体处理; 而mycat是代理了数据库层,应用无感知,通过mycat配置table的dataNode与rule来确定分库分表规则。
  2. 在spring中的使用方式 DruidDataSource 与 TDataSource  基本相同。不同的是: 在常规的执行SimpleExecutor.prepareStatement方法准备prepareStatement前获取Connection时:
  1. 数据源 DruidDataSource:此刻是直接开启Druid连接;
  2. 数据源 TDataSource :  此时并不会直接开启!!而是在之后的PreparedStatementHandler调用PreparedStatement真正执行时, 将通过分库分表策略定位到具体的DruidDataSource,再开启Druid连接


DruidDataSource

在执行getConnection方法会真正的初始化,并从DruidPooledConnection数据库连接池管理中获取连接。(详见getConnectionDirect方法)

Java 获取mybatis 获取sql语句_分表_02

Java 获取mybatis 获取sql语句_python_03



TDataSource

  1. 每一个分表配置对应一个TableRule;每个库真实对应一个DruidDataSource, 多个DruidDataSource可以对应相同的一个 TGroupDataSource 。(当前只使用了分表分库功能,只有一个库对应一个group)
  2. 启动时会从远端配置服务上拉取分库分表配置,构建 VirtualTableRoot :记录每一个分表配置 TableRule  ( extends VirtualTable  )
2019-02-28 16:45:50,796 [main] WARN  [com.taobao.tddl.interact.rule.VirtualTableRoot] [VirtualTableRoot.java:40] [trace=,span=,parent=,name=,app=,begintime=,endtime=] - virtual table start to init :decision_apply_data
2019-02-28 16:45:50,995 [main] WARN  [com.taobao.tddl.interact.rule.VirtualTableRoot] [VirtualTableRoot.java:50] [trace=,span=,parent=,name=,app=,begintime=,endtime=] - virtual table inited :decision_apply_data
2019-02-28 16:45:50,995 [main] WARN  [com.taobao.tddl.interact.rule.VirtualTableRoot] [VirtualTableRoot.java:40] [trace=,span=,parent=,name=,app=,begintime=,endtime=] - virtual table start to init :decision_exec_data_req
2019-02-28 16:45:51,175 [main] WARN  [com.taobao.tddl.interact.rule.VirtualTableRoot] [VirtualTableRoot.java:50] [trace=,span=,parent=,name=,app=,begintime=,endtime=] - virtual table inited :decision_exec_data_req
  1. TableRule中有:(VirtualTableRoot.init)
  1. 每个真实的库Group与每个具体表的对应关系;
  2. 分表规则、分库规则。
  3. 库名、表名的格式
  4. Java 获取mybatis 获取sql语句_分库分表_04



执行过程

本文直接从【执行SimpleExecutor.prepareStatement方法】开始讲述,前面的过程与常规过程类似。

(TDataSource  -> TConnectionImp -> TPreparedStatementImp )-> 分库(TGroupDataSource -> TGroupConnection -> TGroupPreparedStatement -> 实际 (OneDBSelector ->DataSourceWrapper ->TDruidDataSource -> DruidDataSource -> DruidPooledConnection)

具体的执行过程如下:

  • SimpleExecutor.prepareStatement方法构建的prepareStatement是由具体的Connection对象确定的:TDataSource.getConnection方法返回Connection是TConnectionImp; 其构建的prepareStatement是TPreparedStatementImp
  • 之后常规执行PreparedStatementHandler内具体方法(execute等)时,真实是使用TPreparedStatementImp 其内部会确定具体的TGroupDataSource与真实的分表名(原sql被改为真实执行sql)。
  • 再通过TGroupDataSource获取的连接TGroupConnection 构建TGroupPreparedStatement
  • 进入TGroupPreparedStatement的具体方法(execute等)后 :TGroupConnection.createNewConnection方法会真正调用TDruidDataSource.getConnection来真正获取到DruidDataSource进行真实数据库连接获取。

Java 获取mybatis 获取sql语句_python_05



getConnection

对getConnection的处理只是初始化了对象。一般默认使用TConnectionImp!

Java 获取mybatis 获取sql语句_python_06

很重要的是:会将所有的分库组TGroupDataSource 绑定在这个TConnectionImp上!

Java 获取mybatis 获取sql语句_python_07



TDDL什么时候真正开启连接

(execute等)来dml。在这之前会通过prepareStatement方法来使用指定的connection封装PreparedStatementHandler对象。
TConnectionImp的prepareStatement方法构建的是TPreparedStatementImp对象。

接着我们来看看是如果处理分库分表的:

  1. 首先我们看PreparedStatementHandler再执行时的入参中的sql,发现此时还是没有确定分表的
  2. 关键点
    执行到TPreparedStatementImp (TStatementImp)的executeQuery时,会调用 buildSqlExecutionContextUsePipeline 确定好具体要调用哪个库的哪个表等信息保存在RealSqlContextImp中( SpringBasedDispatcherImpl implements SqlDispatcher)

    PipelineBootstrap的bootstrap根据配置文件中的originalSql来判定是否走SimpleHintParser(有一些特殊的操作可以用到。eg. tddl 支持批量插入的方法)

    com.taobao.tddl.client.jdbc.executeplan.ExecutionPlanImp中的内容包含确定好的真实的执行sql及真实的库group等信息
  3. 通过确定好的分组库配置获取连接TGroupConnection,并构建TGroupPreparedStatement。

    此时的dbSelectorId为真实Group标识
  4. TGroupPreparedStatement.executeQuery:  

    着重看下这个方法的“gotoRead”标记。 根据originalSql的类型会判定在TGroupConnectiongetBaseConnection方法中是找读库还是写库
  5. AbstractDBSelector.tryExecute: 会根据策略选中指定group下的唯一的dataSource。
  6. 通过TGroupConnection.createNewConnection最终调用DruidDataSource.getConnection!!