You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring JdbcTemplate跨双数据库(SQL Server+DB2)事务管理实现方案

基于Spring JdbcTemplate实现跨SQL Server和DB2的分布式事务管理

这个问题我之前帮不少开发者解决过——跨不同数据库的事务一致性确实是Spring项目里的常见痛点,尤其是用JdbcTemplate的时候。核心是要用到分布式事务,因为本地事务(比如DataSourceTransactionManager)只能覆盖单个数据源,而你需要同时保证SQL Server和DB2的操作要么都成功,要么都回滚。下面是一步步的实现方案:


1. 配置两个XA兼容的数据源

首先,你需要为SQL Server和DB2分别配置支持XA协议的数据源——XA是分布式事务的基础,它能让多个数据库参与到同一个事务中。这里以Spring Boot为例,用Atomikos作为XA事务管理器(开源JTA实现,兼容性很好):

@Configuration
public class DataSourceConfig {

    // SQL Server XA数据源配置
    @Bean(name = "sqlServerXADataSource")
    public XADataSource sqlServerXADataSource() {
        SQLServerXADataSource xaDataSource = new SQLServerXADataSource();
        xaDataSource.setURL("jdbc:sqlserver://your-sqlserver-host:1433;databaseName=your-db");
        xaDataSource.setUser("username");
        xaDataSource.setPassword("password");
        return xaDataSource;
    }

    // DB2 XA数据源配置
    @Bean(name = "db2XADataSource")
    public XADataSource db2XADataSource() {
        DB2XADataSource xaDataSource = new DB2XADataSource();
        xaDataSource.setUrl("jdbc:db2://your-db2-host:50000/your-db");
        xaDataSource.setUser("username");
        xaDataSource.setPassword("password");
        return xaDataSource;
    }

    // 包装XA数据源为Atomikos的DataSourceProxy
    @Bean(name = "sqlServerDataSource")
    public DataSource sqlServerDataSource(@Qualifier("sqlServerXADataSource") XADataSource xaDataSource) {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        dataSourceBean.setXaDataSource(xaDataSource);
        dataSourceBean.setUniqueResourceName("sqlServerDB");
        dataSourceBean.setPoolSize(5);
        return dataSourceBean;
    }

    @Bean(name = "db2DataSource")
    public DataSource db2DataSource(@Qualifier("db2XADataSource") XADataSource xaDataSource) {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        dataSourceBean.setXaDataSource(xaDataSource);
        dataSourceBean.setUniqueResourceName("db2DB");
        dataSourceBean.setPoolSize(5);
        return dataSourceBean;
    }
}

2. 配置对应JdbcTemplate实例

每个数据源需要绑定一个独立的JdbcTemplate,这样你可以分别操作两个数据库:

@Configuration
public class JdbcTemplateConfig {

    @Bean(name = "sqlServerJdbcTemplate")
    public JdbcTemplate sqlServerJdbcTemplate(@Qualifier("sqlServerDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean(name = "db2JdbcTemplate")
    public JdbcTemplate db2JdbcTemplate(@Qualifier("db2DataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

3. 配置分布式事务管理器

接下来要配置JTA事务管理器,让Spring能够协调两个数据库的事务:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager() {
        AtomikosTransactionManager atomikosTransactionManager = new AtomikosTransactionManager();
        atomikosTransactionManager.setForceShutdown(false);

        UserTransaction userTransaction = new UserTransactionImp();
        try {
            userTransaction.setTransactionTimeout(300); // 设置事务超时时间
        } catch (SystemException e) {
            throw new RuntimeException(e);
        }

        return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
    }
}

4. 在业务逻辑中使用事务

最后,在你的业务方法上添加@Transactional注解,指定事务管理器,并确保任何异常都会触发回滚:

@Service
public class BatchDataService {

    private final JdbcTemplate sqlServerJdbcTemplate;
    private final JdbcTemplate db2JdbcTemplate;

    // 构造注入两个JdbcTemplate
    public BatchDataService(@Qualifier("sqlServerJdbcTemplate") JdbcTemplate sqlServerJdbcTemplate,
                            @Qualifier("db2JdbcTemplate") JdbcTemplate db2JdbcTemplate) {
        this.sqlServerJdbcTemplate = sqlServerJdbcTemplate;
        this.db2JdbcTemplate = db2JdbcTemplate;
    }

    // 声明式事务:任何Exception都会触发回滚
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void batchInsertData(List<SqlServerData> sqlServerDataList, List<Db2Data> db2DataList) {
        // 批量插入SQL Server数据
        String sqlServerInsertSql = "INSERT INTO your_sqlserver_table (col1, col2) VALUES (?, ?)";
        sqlServerJdbcTemplate.batchUpdate(sqlServerInsertSql,
                new BatchPreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        SqlServerData data = sqlServerDataList.get(i);
                        ps.setString(1, data.getCol1());
                        ps.setString(2, data.getCol2());
                    }

                    @Override
                    public int getBatchSize() {
                        return sqlServerDataList.size();
                    }
                });

        // 模拟异常,测试回滚(可以注释掉)
        // if (true) throw new RuntimeException("Test rollback");

        // 批量插入DB2数据
        String db2InsertSql = "INSERT INTO your_db2_table (col_a, col_b) VALUES (?, ?)";
        db2JdbcTemplate.batchUpdate(db2InsertSql,
                new BatchPreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        Db2Data data = db2DataList.get(i);
                        ps.setString(1, data.getColA());
                        ps.setString(2, data.getColB());
                    }

                    @Override
                    public int getBatchSize() {
                        return db2DataList.size();
                    }
                });
    }
}

关键注意事项

  • XA驱动支持:确保你的SQL Server和DB2驱动是支持XA的版本,比如SQL Server的mssql-jdbc(6.0+版本支持XA),DB2的db2jcc驱动。
  • 连接池配置:使用支持XA的连接池,比如Atomikos自带的AtomikosDataSourceBean,或者HikariCP的XA实现。
  • 事务超时:合理设置事务超时时间,避免长时间占用数据库连接。
  • 异常处理@TransactionalrollbackFor = Exception.class确保checked exception也会触发回滚(默认只处理RuntimeException)。

内容的提问来源于stack exchange,提问作者PRIYANK SINHA

火山引擎 最新活动