由于seata需要兼容现有常用的ORM框架,所以直接使用DataSourceProxy实现DataSource接口,在项目中作为数据源对象。
@Primary
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
//持有了目标数据源对象,该DataSourceProxy构造函数,会把RootContext的DEFAULT_BRANCH_TYPE设置为BranchType.AT
return new DataSourceProxy(dataSource);
}
先看到dataSourceProxy的重写的getConnection方法
@Override
public ConnectionProxy getConnection() throws SQLException {
//获取targetConnection(目标数据源对象)
Connection targetConnection = targetDataSource.getConnection();
//返回的是ConnectionProxy对象,持有了targetConnection
return new ConnectionProxy(this, targetConnection);
}
再看到ConnectionProxy实现了Connection接口重写的prepareStatement方法
public PreparedStatement prepareStatement(String sql) throws SQLException {
String dbType = getDbType();
// support oracle 10.2+
PreparedStatement targetPreparedStatement = null;
//AT模式
if (BranchType.AT == RootContext.getBranchType()) {
//封裝sql的为SQLRecognizer对象
List<SQLRecognizer> sqlRecognizers = SQLVisitorFactory.get(sql, dbType);
if (sqlRecognizers != null && sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
//如果是插入,需要获取插入后的主键
if (sqlRecognizer != null && sqlRecognizer.getSQLType() == SQLType.INSERT) {
//获取表的信息
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dbType).getTableMeta(getTargetConnection(),
sqlRecognizer.getTableName(), getDataSourceProxy().getResourceId());
String[] pkNameArray = new String[tableMeta.getPrimaryKeyOnlyName().size()];
//获取的到主键的列名填充到pkNameArray中
tableMeta.getPrimaryKeyOnlyName().toArray(pkNameArray);
//使用targetConnection调用prepareStatement
targetPreparedStatement = getTargetConnection().prepareStatement(sql,pkNameArray);
}
}
}
//不是插入的情况
if (targetPreparedStatement == null) {
targetPreparedStatement = getTargetConnection().prepareStatement(sql);
}
//返回了PreparedStatementProxy,持有了targetPreparedStatement
return new PreparedStatementProxy(this, targetPreparedStatement, sql);
}
再看到PreparedStatementProxy实现了PreparedStatement接口重写的execute方法
public boolean execute() throws SQLException {
//(statement, args) -> statement.execute(),传入了目标方法的调用
return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
}
public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
//如果RootContext的CONTEXT_HOLDER(threadlocal)的TX_LOCK为空(@GlobalLock注解)且
//如果没有XID,或者有XID但不是AT模式,直接调用目标方法
if (!RootContext.requireGlobalLock() && BranchType.AT != RootContext.getBranchType()) {
// Just work as original statement
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
String dbType = statementProxy.getConnectionProxy().getDbType();
if (CollectionUtils.isEmpty(sqlRecognizers)) {
sqlRecognizers = SQLVisitorFactory.get(
statementProxy.getTargetSQL(),
dbType);
}
Executor<T> executor;
if (CollectionUtils.isEmpty(sqlRecognizers)) {
executor = new PlainExecutor<>(statementProxy, statementCallback);
}
else {
//单条sql
if (sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
switch (sqlRecognizer.getSQLType()) {
//不同类型的sql交给不同的executor
case INSERT:
//spi,加载META-INF/services和META-INF/seata/下的文件(已经加载过的不会重复加载)
//这里获取InsertExecutor的实现类,并使用dbType匹配LoadLevel注解中的name值,取到对应的实现类(mysql对应MySQLInsertExecutor)
executor = EnhancedServiceLoader.load(InsertExecutor.class, dbType,
new Class[]{
StatementProxy.class, StatementCallback.class, SQLRecognizer.class},
new Object[]{
statementProxy, statementCallback, sqlRecognizer});
break;
case UPDATE:
executor = new UpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case DELETE:
executor = new DeleteExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case SELECT_FOR_UPDATE:
executor = new SelectForUpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
default:
executor = new PlainExecutor<>(statementProxy, statementCallback);
break;
}
}
//批量sql
else {
executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers);
}
}
T rs;
try {
//看到execute方法
rs = executor.execute(args);
} catch (Throwable ex) {
if (!(ex instanceof SQLException)) {
// Turn other exception into SQLException
ex = new SQLException(ex);
}
throw (SQLException) ex;
}
return rs;
}
//BaseTransactionalExecutor
public T execute(Object... args) throws Throwable {
//获取XID
String xid = RootContext.getXID();
//有XID的情况下,设置到ConnectionProxy的context(xid)属性中
if (xid != null) {
statementProxy.getConnectionProxy().bind(xid);
}
//判断CONTEXT_HOLDER(threadlocal)的TX_LOCK是否有值

本文深入剖析了Seata在数据源代理中的实现,通过DataSourceProxy和ConnectionProxy实现对数据库连接的拦截,确保在AT模式下正确处理全局事务。在prepareStatement和execute方法中,Seata进行SQL解析,根据不同的SQL类型(如INSERT、UPDATE、DELETE等)选择不同的Executor执行。在执行过程中,Seata利用LockRetryPolicy处理全局锁冲突,确保事务的幂等性和一致性。同时,Seata通过beforeImage和afterImage记录数据变更,用于生成undo_log,以便在事务回滚时能准确还原数据。在事务提交时,Seata与服务端交互,报告分支事务状态,确保全局事务的正确性。
最低0.47元/天 解锁文章
908

被折叠的 条评论
为什么被折叠?



