首先咱们来看看DataSourceUtils.doGetConnection的源码:spring-jdbc jar下的org.springframework.jdbc.datasource提供用来获取数据库链接java
经过前几篇幅对mybatis的执行过程分析可知:
在新建defaultSqlSession实例时,其内部成员对象SpringManagedTransaction实例内拥有的connect对象仍是null,但最终在执行SimpleExecutor.prepareStatement方法内获取真实链接时会跳入DataSourceUtils.doGetConnection
spring
那关键来了,这个getConnection()作了什么呢?sql
结论
- TDataSource 与 mycat的实现不一样:
前者是在应用程序内经过分库分表策略(TableRule) dispatcher到具体的单个库(TGroupDataSource)具体单个表,经过TDruidDataSource (实际上内部仍是DruidDataSource) 来具体处理; 而mycat是代理了数据库层,应用无感知,经过mycat配置table的dataNode与rule来肯定分库分表规则。
- 在spring中的使用方式 DruidDataSource 与 TDataSource 基本相同。不一样的是: 在常规的执行SimpleExecutor.prepareStatement方法准备prepareStatement前获取Connection时:
- 数据源 DruidDataSource:此刻是直接开启Druid链接;
- 数据源 TDataSource : 此时并不会直接开启!!而是在以后的PreparedStatementHandler调用PreparedStatement真正执行时, 将经过分库分表策略定位到具体的DruidDataSource,再开启Druid链接。
DruidDataSource
在执行getConnection方法会真正的初始化,并从DruidPooledConnection数据库链接池管理中获取链接。(详见getConnectionDirect方法)数据库


TDataSource
- 每个分表配置对应一个TableRule;每一个库真实对应一个DruidDataSource, 多个DruidDataSource能够对应相同的一个 TGroupDataSource 。(当前只使用了分表分库功能,只有一个库对应一个group)

- 启动时会从远端配置服务上拉取分库分表配置,构建 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
TableRule中有:(VirtualTableRoot.init)
- 每一个真实的库Group与每一个具体表的对应关系;
- 分表规则、分库规则。
- 库名、表名的格式

执行过程
本文直接从【执行SimpleExecutor.prepareStatement方法】开始讲述,前面的过程与常规过程相似。bootstrap
总(TDataSource -> TConnectionImp -> TPreparedStatementImp )-> 分库(TGroupDataSource -> TGroupConnection -> TGroupPreparedStatement) -> 实际 (OneDBSelector ->DataSourceWrapper ->TDruidDataSource -> DruidDataSource -> DruidPooledConnection)mybatis
具体的执行过程以下:app
- 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进行真实数据库链接获取。

getConnection
对getConnection的处理只是初始化了对象。通常默认使用TConnectionImp!ui

很重要的是:会将全部的分库组TGroupDataSource 绑定在这个TConnectionImp上!
spa
TDDL何时真正开启链接
最终执行的步骤是在:SimpleExecutor调用PreparedStatementHandler调用具体执行方法(execute等)来dml。在这以前会经过prepareStatement方法来使用指定的connection封装PreparedStatementHandler对象。
TConnectionImp的prepareStatement方法构建的是TPreparedStatementImp对象。.net
接着咱们来看看是若是处理分库分表的:
- 首先咱们看PreparedStatementHandler再执行时的入参中的sql,发现此时仍是没有肯定分表的

- 关键点:
执行到TPreparedStatementImp (TStatementImp)的executeQuery时,会调用 buildSqlExecutionContextUsePipeline 肯定好具体要调用哪一个库的哪一个表等信息保存在RealSqlContextImp中( SpringBasedDispatcherImpl implements SqlDispatcher)

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

com.taobao.tddl.client.jdbc.executeplan.ExecutionPlanImp中的内容包含肯定好的真实的执行sql及真实的库group等信息

- 经过肯定好的分组库配置获取链接TGroupConnection,并构建TGroupPreparedStatement。

此时的dbSelectorId为真实Group标识


- TGroupPreparedStatement.executeQuery:

着重看下这个方法的“gotoRead”标记。 根据originalSql的类型会断定在TGroupConnection的getBaseConnection方法中是找读库仍是写库

- AbstractDBSelector.tryExecute: 会根据策略选中指定group下的惟一的dataSource。

- 经过TGroupConnection.createNewConnection最终调用DruidDataSource.getConnection!!
