Seata AT模式源码解析(二) ------ DataSourceProxy

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

  由于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是否有值
		
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值