apache / shardingsphere

Distributed SQL transaction & query engine for data sharding, scaling, encryption, and more - on any database.
Apache License 2.0
19.89k stars 6.73k forks source link

about ShardingSphereXA Distributed transaction: #13873

Closed yeruilong closed 3 months ago

yeruilong commented 2 years ago

@ShardingTransactionType(TransactionType.XA) @transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class) public Boolean A() { mapperA.updateById(); B(); }

@transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class) public Boolean b() { mapperB.updateById(); } WARN com.atomikos.datasource.xa.XAResourceTransaction - XA resource 'resource-1-master': suspend for XID '3139322E3136382E35362E312E746D313633383237323939323830323030303031:3139322E3136382E35362E312E746D31' raised -5: invalid arguments were given for the XA operation com.mysql.cj.jdbc.MysqlXAException: XAER_INVAL: Invalid arguments (or unsupported command)

when I call interface reported an error, but no solution was found

jingshanglu commented 2 years ago

@yeruilong Can you show a demo? I'll check it.

yeruilong commented 2 years ago

The following are the main Class

1、 @Configuration @MapperScan(basePackages = "com.xx.cloud.uc.admin.mapper", sqlSessionFactoryRef = "sqlSessionFactory") public class DataSourceConfig {

@Resource
@Qualifier("shardingDataSource")
private DataSource dataSource;

@Bean
public DataSourceSwitch dataSourceSwitch() {
    Map<Object, Object> targetDataSources = new HashMap<>();
    return new DataSourceSwitch(dataSource, targetDataSources);
}

@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceSwitch") DataSource dynamicDataSource) throws Exception {
    MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
    bean.setDataSource(dynamicDataSource);
    return bean.getObject();
}

@Bean(name = "sqlSessionFactory")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);
}

}

2、 @Slf4j public class DataSourceSwitch extends AbstractRoutingDataSource {

public  final Map<Object, Object> dataSources;

/**
 * 动态数据源构造器
 *
 * @param defaultDataSource 默认数据源
 * @param targetDataSource  目标数据源映射
 */
public DataSourceSwitch(DataSource defaultDataSource,Map<Object, Object> targetDataSource) {
    dataSources = targetDataSource;
    super.setDefaultTargetDataSource(defaultDataSource);
    // 存放数据源的map
    super.setTargetDataSources(dataSources);
    // afterPropertiesSet 的作用很重要,它负责解析成可用的目标数据源
    super.afterPropertiesSet();
}

@Override
protected Object determineCurrentLookupKey() {
    return DynamicDataSourceContextHolder.get();
}

public void addDataSource(DatasourceDto datasource) {
    HikariDataSource dataSource = new HikariDataSource();
    String jdbcUrl = "jdbc:mysql://" +
            datasource.getHost() +
            ":" +
            datasource.getPort() +
            "/" +
            datasource.getDbName() +
            "?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true";
    dataSource.setJdbcUrl(jdbcUrl);
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUsername(datasource.getUsername());
    dataSource.setPassword(datasource.getPwd());
    dataSource.setMaximumPoolSize(3);
    dataSource.setIdleTimeout(30000);
    dataSource.setMinimumIdle(3);
    // 将传入的数据源对象放入动态数据源类的静态map中,然后再讲静态map重新保存进动态数据源中
    dataSources.putIfAbsent(datasource.getDbCode(), dataSource);
    super.setTargetDataSources(dataSources);
    super.afterPropertiesSet();
}

}

3、

public class DynamicDataSourceContextHolder {

private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();

public static void set(String dbType) {
    contextHolder.set(dbType);
}

public static String get() {
    return contextHolder.get();
}

public static void remove() {
    contextHolder.remove();
}

}

4、 @Service @Slf4j public class BusinessConfigServiceImpl extends ServiceImpl<BusinessConfigMapper, BusinessConfig> implements BusinessConfigService {

@Resource
private BusinessConfigMapper businessConfigMapper;

@Override
@ShardingTransactionType(TransactionType.XA)
@Transactional
public Boolean updateBusinessConfig() {

    LambdaQueryWrapper<BusinessConfig> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(BusinessConfig::getId, 1);
    // DynamicDataSourceContextHolder.get();
    BusinessConfig businessConfig = businessConfigMapper.selectOne(wrapper);
    businessConfig.setType(1111);
    businessConfigMapper.updateById(businessConfig);
    // DynamicDataSourceContextHolder.remove();

    BusinessConfigServiceImpl businessConfigServiceImpl = SpringContextUtil.getBean("businessConfigServiceImpl", BusinessConfigServiceImpl.class);
    businessConfigServiceImpl.updateBusinessConfig2();

    //int l = 1 / 0;

    return true;
}

@Override
//@ShardingTransactionType(TransactionType.XA)
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public Boolean updateBusinessConfig2() {

    // 切换数据源
    DataSourceSwitch dataSourceSwitch = SpringContextUtil.getBean("dataSourceSwitch", DataSourceSwitch.class);
    DatasourceDto datasourcedto = new DatasourceDto();
    datasourcedto.setHost("127.0.0.1");
    datasourcedto.setPort(3306);
    datasourcedto.setUsername("root");
    datasourcedto.setPwd("123456");
    datasourcedto.setDbName("uc-center");
    datasourcedto.setDbCode("localhost");
    datasourcedto.setBelongCode("localhost");
    dataSourceSwitch.addDataSource(datasourcedto);
    DynamicDataSourceContextHolder.set(datasourcedto.getDbCode());

    LambdaQueryWrapper<BusinessConfig> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(BusinessConfig::getId, 1);
    // DynamicDataSourceContextHolder.get();
    BusinessConfig businessConfig = businessConfigMapper.selectOne(wrapper);
    businessConfig.setType(3333);
    //int l = 1 / 0;
    int count = businessConfigMapper.updateById(businessConfig);

    DynamicDataSourceContextHolder.remove();
    //int l = 1 / 0;
    return true;
}

}

jingshanglu commented 2 years ago

@yeruilong It seems that the usage scenario is the same as the issue and now there are some problems with the XA transaction interface.

zhfeng commented 2 years ago

I think there are some misunderstands of ShardingTransactionType with TransactionType.XA. It is an internal transaction used by sharding-shpere to coordinate the distributed backend databases. @yeruilong if you have only one dataSource, can you check if it works by removing @ShardingTransactionType(TransactionType.XA) ?

yeruilong commented 2 years ago

if i retain one database,and removing @ShardingTransactionType(TransactionType.XA),transaction not effective,it must contain @ShardingTransactionType(TransactionType.XA) and @Transactional then transation can work.can you add me qq/weixin?qq:269365937 vx:dhbsz999

github-actions[bot] commented 2 years ago

Hello , this issue has not received a reply for several days. This issue is supposed to be closed.

github-actions[bot] commented 6 months ago

There hasn't been any activity on this issue recently, and in order to prioritize active issues, it will be marked as stale.