使用路由数据源和JdbcTemplate引导多个数据库事务

问题描述

我需要在两个不同的数据库之间执行事务操作。我可以找到JPA多个数据源事务的多个示例,但JdbcTemplate找不到任何示例。无论如何,我使用ChainedTransactionManager进行了一些改动,尝试了相同的方法。这是示例代码

// This gives the Datasource list in cluster 1. Similarly done for cluster 2 too.
    @Bean(name = "loadCluster1Bean")
    public List<DataSource> loadCluster1() {
        List<DataSource> dataSources = new ArrayList<>();
        try {
            // Do Hikari Configs and Get the DataSources
            dataSources = getDatasources()
        } catch (Exception e) {
          ...LOG
        }
        return dataSources;
    }

在这里,我们使用数据源列表创建datasourceTransaction Manager。

    @Bean(name = "cluster1TransactionManager")
    @DependsOn({"loadCluster1Bean"})
    List<DataSourceTransactionManager> tm1(@Qualifier("loadCluster1Bean") List<DataSource> dataSources) {
        List<DataSourceTransactionManager> dataSourceTransactionManagers = new ArrayList<>();
        for (DataSource dataSource : dataSources) {
            dataSourceTransactionManagers.add(new DataSourceTransactionManager(dataSource));
        }
        return dataSourceTransactionManagers;
    } 
    @Bean(name = "cluster2TransactionManager")
    @DependsOn({"loadCluster2Bean"})
    List<DataSourceTransactionManager> tm2(@Qualifier("loadCluster2Bean") List<DataSource> dataSources) {
        List<DataSourceTransactionManager> dataSourceTransactionManagers = new ArrayList<>();
        for (DataSource dataSource : dataSources) {
            dataSourceTransactionManagers.add(new DataSourceTransactionManager(dataSource));
        }
        return dataSourceTransactionManagers;
    }

下面是chainedTransactionManager

@Configuration
@ComponentScan
public class TransactionManagerConfig {
    @Bean(name = "chainedTransactionManager")
    public ChainedTransactionManager transactionManager(
            @Qualifier("cluster1TransactionManager") List<DataSourceTransactionManager> cluster1TransactionManagers,@Qualifier("cluster2TransactionManager") List<DataSourceTransactionManager> cluster2TransactionManagers) {

        List<DataSourceTransactionManager> transactionManagers = new ArrayList<>();
        transactionManagers.addAll(cluster1TransactionManagers);
        transactionManagers.addAll(cluster2TransactionManagers);

        return new ChainedTransactionManager(transactionManagers.toArray(new DataSourceTransactionManager[transactionManagers.size()]));
    }
}

然后将如下所示的chainedTransactionManager添加到服务层(伪代码)

@Service
public class ServiceClass {

    @Transactional(value = "chainedTransactionManager")
    public void addData(data,dbName) {
        for(cluster : clusters) {   //Same data needs to be inserted into multiple clusters. This is where the transactional requirement comes. 
            DBConnectionContextHolder.setDatabaseInstance(cluster,dbName);
            Dao.addData(data);
        }
    }  
    
}

下面,我添加了DBConnectionContextHolder类和AbstractRoutingDataSource类。

public class DBConnectionContextHolder {

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

    private static Set<String> enabledDatabases = new HashSet<>();

    private DBConnectionContextHolder() {
    }

    public static boolean setDatabaseInstance(String dbName,String cluster) {}
        contextHolder.set(cluster+"_"+dbName);
        return true;
    }

    public static void setEnabledDatabases(Set<String> enabledDatabases) {
        DBConnectionContextHolder.enabledDatabases = enabledDatabases;
    }

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

}

public class RoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DBConnectionContextHolder.getDatabase();
    }
}
    private static final RoutingDataSource updatedRoutingDataSource = new RoutingDataSource();
             //targetDataSource is a map which contains key: db-cluster name,value: HikariDataSource

updatedRoutingDataSource.setTargetDataSources(targetDataSources);
updatedRoutingDataSource.afterPropertiesSet();

但是当我运行代码时,它不起作用:/似乎连接没有更新到下一个群集。

org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; SQL [INSERT INTO BLAH (A,B,C,D,E,F,G,H,I) VALUES (?,?,?)]; Duplicate entry 'abababab-1122233-2020-08-18 00:00:00-2020-09-30 00:00:00-1599064' for key 'idx_abcde'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'abababab-1122233-2020-08-18 00:00:00-2020-09-30 00:00:00-1599064' for key 'idx_abcde'

在没有@Transactional的情况下,它可以工作,并且数据被添加到两个群集中。但是,是的,如果一个群集发生故障,它不会回滚。

如果有人可以帮助使其达到工作状态,我将非常感激:) 谢谢。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...