记录下seata源码的有意思地方


why?

一开始写了一段delete的sql,也用GloblaTransaction注解了,但是打断点的时候,发现在undo-log表 没找到branch_id,即并没有生成分支事务。就很好奇,所以不断的打断点找原因。

原因:

在执行delete操作时,会通过 DeleteExecutor 根据查询条件拼接查询语句,获得删除前的数据量。
获得表里的数据量-beforeImage,
然后 创建一个空的afterImage
根据beforeImage,afterImage是否为空来 准备undoLog,如果before为空(after肯定为空)就不创建 分支事务

	protected T executeAutoCommitFalse(Object[] args) throws Exception {
        if (!JdbcConstants.MYSQL.equalsIgnoreCase(getDbType()) && isMultiPk()) {
            throw new NotSupportYetException("multi pk only support mysql!");
        }
        // 获得
        TableRecords beforeImage = beforeImage();
        T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
        TableRecords afterImage = afterImage(beforeImage);
        prepareUndoLog(beforeImage, afterImage);
        return result;
    }

	@Override
	// 会根据查询条件拼接查询语句,获得删除前的数据量
    protected TableRecords beforeImage() throws SQLException {
        SQLDeleteRecognizer visitor = (SQLDeleteRecognizer) sqlRecognizer;
        TableMeta tmeta = getTableMeta(visitor.getTableName());
        ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
        String selectSQL = buildBeforeImageSQL(visitor, tmeta, paramAppenderList);
        return buildTableRecords(tmeta, selectSQL, paramAppenderList);
    }

	//io.seata.rm.datasource.exec.BaseTransactionalExecutor#prepareUndoLog	
	protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException {
	    if (beforeImage.getRows().isEmpty() && afterImage.getRows().isEmpty()) {
	        return;
	    }

    	ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();

    	TableRecords lockKeyRecords = sqlRecognizer.getSQLType() == SQLType.DELETE ? beforeImage : afterImage;
    	String lockKeys = buildLockKey(lockKeyRecords);
    	connectionProxy.appendLockKey(lockKeys);//操作1

    	SQLUndoLog sqlUndoLog = buildUndoItem(beforeImage, afterImage);
    	connectionProxy.appendUndoLog(sqlUndoLog);//操作2
	}
	
	//操作1,2执行后,ConnectionContext内对应参数集合size>0
	//进而影响 到 io.seata.rm.datasource.ConnectionProxy#register
	private void register() throws TransactionException {
    	if (!context.hasUndoLog() || context.getLockKeysBuffer().isEmpty()) {//由于上面都append了,就会执行下面的 分支事务id的注册
        	return;
    	}
    	//向服务端注册 分支事务,并返回branchId
    	Long branchId = DefaultResourceManager.get().branchRegister(
			BranchType.AT,getDataSourceProxy().getResourceId(),null, 
			context.getXid(), null, context.buildLockKeys());
    	context.setBranchId(branchId);
	}

	//io.seata.rm.AbstractResourceManager#branchRegister
	@Override
	public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException {
	    try {
	        BranchRegisterRequest request = new BranchRegisterRequest();
	        request.setXid(xid);
	        request.setLockKey(lockKeys);
	        request.setResourceId(resourceId);
	        request.setBranchType(branchType);
	        request.setApplicationData(applicationData);
			//向seata服务端发送请求,获得branchId
	        BranchRegisterResponse response = (BranchRegisterResponse) RmNettyRemotingClient.getInstance().sendSyncRequest(request);
	        if (response.getResultCode() == ResultCode.Failed) {
	            throw new RmTransactionException(response.getTransactionExceptionCode(), String.format("Response[ %s ]", response.getMsg()));
	        }
	        return response.getBranchId();
	    } catch (TimeoutException toe) {
	        throw new RmTransactionException(TransactionExceptionCode.IO, "RPC Timeout", toe);
	    } catch (RuntimeException rex) {
	        throw new RmTransactionException(TransactionExceptionCode.BranchRegisterFailed, "Runtime", rex);
	    }
	}

与mybatis-plus结合时,失效的解决方法

当seata+mybatis-plus结合时,由于mp是通过 MybatisSqlSessionFactoryBean创建SqlSessionFactory的,所以需要将 DataSourceProxy 作为参数 传进去
另外 当:手动由spring实例化时,主键的类型 将是 IdType.ID_WORKER,如果主键是int类型会超出长度,所以需要 设置DbConfig.IdType = IdType.AUTO

@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
    // 这里用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
    MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
    mybatisSqlSessionFactoryBean.setDataSource(dataSourceProxy);
    mybatisSqlSessionFactoryBean.setTypeAliasesPackage("com.*.pojo.model");
    mybatisSqlSessionFactoryBean.setTypeHandlersPackage("com.*.common.mybatisplus.handler");
    mybatisSqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath*:/mapper/*.xml"));

    GlobalConfig globalConfig = new GlobalConfig();
    GlobalConfig.DbConfig dbConfig =  new GlobalConfig.DbConfig();
    dbConfig.setIdType(IdType.AUTO);
    globalConfig.setDbConfig(dbConfig);
    mybatisSqlSessionFactoryBean.setGlobalConfig(globalConfig);
    return mybatisSqlSessionFactoryBean.getObject();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值