1前言
之前我们分析了GlobalTransactional注解的整个生态,但是一些细节性的逻辑还没有深入去看,例如:数据源代理、各种Client的初始化、模板方法等。在这一篇里面,我们会了解一下在上一篇中没有讲完的数据源代理。
数据源代理是非常重要的一个环节。我们知道,在分布式事务运行过程中,很多undo log等的记录、资源的锁定等,都是用户无感知的,因为这些操作都在数据源的代理中完成了。
2数据源代理DataSourceProxy
在上一篇中,我们知道在postProcessAfterInitialization时,会生成对应的数据源代理DataSourceProxy。
DataSourceProxy与普通数据源有哪些区别呢? 它在构造方法中调用了一个自定义的init方法,主要做了以下能力的增强:
1为每个数据源标识了资源组ID
2如果配置打开,会有一个定时线程池定时更新表的元数据信息并缓存到本地
3生成代理连接ConnectionProxy
以下是init方法的代码:
private void init(DataSource dataSource, String resourceGroupId) {
//资源组ID,如果初始化的时候,构造方法没有传,会使用一个“default”这个默认值
this.resourceGroupId = resourceGroupId;
try (Connection connection = dataSource.getConnection()) {
//根据原始数据源得到JDBC连接和数据库类型
jdbcUrl = connection.getMetaData().getURL();
dbType = JdbcUtils.getDbType(jdbcUrl, null);
} catch (SQLException e) {
throw new IllegalStateException("can not init dataSource", e);
}
DefaultResourceManager.get().registerResource(this);
if (ENABLE_TABLE_META_CHECKER_ENABLE) {
//如果配置开关打开,会定时线程池不断更新表的元数据信息
tableMetaExcutor.scheduleAtFixedRate(() -> {
try (Connection connection = dataSource.getConnection()) {
TableMetaCacheFactory.getTableMetaCache(DataSourceProxy.this.getDbType())
.refresh(connection, DataSourceProxy.this.getResourceId());
} catch (Exception e) {
}
}, 0, TABLE_META_CHECKER_INTERVAL, TimeUnit.MILLISECONDS);
}
}
这3个增强里面,前两个都比较容易理解,第三是最重要的。我们知道在AT模式里面,会自动记录undo log、资源锁定等等,都是通过ConnectionProxy完成的。
另外,DataSourceProxy重写了几个方法。
一个是getConnection,此时会返回一个ConnectionProxy,而不是原生的Connection,ConnectionProxy我们会在下一个小节详细介绍:
@Override
public ConnectionProxy getConnection() throws SQLException {
Connection targetConnection = targetDataSource.getConnection();
return new ConnectionProxy(this, targetConnection);
}
@Override
public ConnectionProxy getConnection(String username, String password) throws SQLException {
Connection targetConnection = targetDataSource.getConnection(username, password);
return new ConnectionProxy(this, targetConnection);
}
另外就是一些辅助性的方法了,如获取资源组、获取分支事务的类型,随便看看就好了:
@Override
public String getResourceGroupId() {
return resourceGroupId;
}
@Override
public String getResourceId() {
if (jdbcUrl.contains("?")) {
return jdbcUrl.substring(0, jdbcUrl.indexOf("?"));
} else {
return jdbcUrl;
}
}
@Override
public BranchType getBranchType() {
//分支事务返回AT模式
return BranchType.AT;
}
3ConnectionProxy的总体类结构
ConnectionProxy继承了AbstractConnectionProxy。一看到Abstract,就知道它的父类封装了很多通用工作。它的父类里面还使用了PreparedStatementProxy、StatementProxy、DataSourceProxy。
DataSourceProxy在上面已经讲过,只有3个增强点,并重写了getConnection等方法而已,其余都是使用的DataSource原生的方法,因此就不再赘述。
我们先看AbstractConnectionProxy。
4PreparedStatementProxy和StatementProxy
无论是StatementProxy还是PreparedStatementProxy,它们都是使用了一个模板执行具体的逻辑步骤,这个模板叫ExecuteTemplate
。举个例子:模板里面定义了1、2、3、4…的步骤,其中4是需要外部实现的,即到底用Statement还是PreparedStatement执行sql:
public boolean execute() throws SQLException {
return ExecuteTemplate.execute(this, new StatementCallback<Boolean, PreparedStatement>() {
@Override
public Boolean execute(PreparedStatement statement, Object... args) throws SQLException {
//最终个性化的操作就是使用PreparedStatement做execute
return statement.execute();
}
});
}
就如代码里一样,PreparedStatementProxy的execute方法调用了ExecuteTemplate的execute方法,并传入了一个StatementCallback,这个Callback就是第4步的个性化逻辑,其余逻辑都是通用。
我们继续看execute方法,这个也是模板里面唯一的方法:
public static <T, S extends Statement> T execute(SQLRecognizer sqlRecognizer,
StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
if (!RootContext.inGlobalTransaction() && !RootContext.requireGlobalLock()) {
// 没有分布式事务,执行像普通sql一样执行
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
//生成一个sql的识别器对象,它能解析sql
if (sqlRecognizer == null) {
sqlRecognizer = SQLVisitorFactory.get(
statementProxy.getTargetSQL(),
statementProxy.getConnectionProxy().getDbType());
}
Executor<T> executor = null;
//根据sql的类型生成不同的类型的executor,传入不同发statementCallback
if (sqlRecognizer == null) {
executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
} else {
switch (sqlRecognizer.getSQLType()) {
case INSERT:
executor = new InsertExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
break;
case UPDATE:
executor = new UpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
break;
case DELETE:
executor = new DeleteExecutor