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实现。 - 事务超时:合理设置事务超时时间,避免长时间占用数据库连接。
- 异常处理:
@Transactional的rollbackFor = Exception.class确保checked exception也会触发回滚(默认只处理RuntimeException)。
内容的提问来源于stack exchange,提问作者PRIYANK SINHA




