跨两个数据源的事务管理(ChainedTransactionManager)——SpringBoot

Transaction Management across two data sources (ChainedTransactionManager) - SpringBoot

为什么 Spring ChainedTransactionManager 被弃用? spring 是否提供任何替代库来支持多个事务管理器?

我的用例:- 我们正在构建一个 spring 启动应用程序,它连接到两个数据源,比方说(db1 和 db2),它对两个数据库(db1 和 db2)执行插入操作。我们的要求是这样的: 插入 -> DB1 -> 成功 插入 -> DB2 -> 错误 回滚 DB1

目前,我们正在使用 ChaninedTransactionManager 并且它按预期工作,但我可以看到 lib 已被弃用?所以,只是想确保使用它是否安全,或者 Spring 是否提供任何我们可以用来替代它的替代库?

因为有 API 改进。从文档 https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/transaction/ChainedTransactionManager.html

Instead of using ChainedTransactionManager for attaching callbacks to transaction commit (pre commit/post commit), either register a TransactionSynchronization to explicitly follow transaction cleanup with simplified semantics in case of exceptions.

您仍然可以自由使用它,请记住,在某些时候 class 将在 Spring 的未来版本中删除,并且如果不重构就无法升级那部分。

ChainedTransactionManager

The configured instances will start transactions in the order given and commit/rollback in reverse order, which means the PlatformTransactionManager most likely to break the transaction should be the last in the list configured.

如果您按以下顺序链接交易:交易 1、交易 2

transaction1 begin
  transaction2 begin
  transaction2 commit -> error rollbacks, rollbacks transction1 too
transaction1 commit -> error, only rollbacks transaction1

案例 insert -> DB1 -> SUCCESSFUL insert -> DB2 -> ERROR ROLLBACK DB1 有效。

但是,如果您有insert -> DB1 -> FAIL ROLLBAK DB1 -> DB2 -> SUCCESSFUL,插入是为 DB2 而不是为 DB1 提交的。 this article.

中有更多详细信息

如果您对此满意,可以在您的项目中复制 class 并继续使用它:https://github.com/spring-projects/spring-data-commons/issues/2232#issuecomment-1018473289

带有 Atomikos 的 JtaTransactionManager

为了在事务失败时回滚所有事务,我将我的 conf 更改为使用 JtaTransactionManager(具有 spring 引导和 posgtres)。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

application.properties

my.datasource.one.unique-resource-name=
my.datasource.one.xa-properties.url=jdbc:postgresql://
my.datasource.one.xa-data-source-class-name=org.postgresql.xa.PGXADataSource
my.datasource.one.xa-properties.user=
my.datasource.one.xa-properties.password=
my.datasource.one.max-pool-size=
my.datasource.one.min-pool-size=

my.datasource.two.unique-resource-name=
my.datasource.two.xa-properties.url=jdbc:postgresql://
my.datasource.two.xa-data-source-class-name=org.postgresql.xa.PGXADataSource
my.datasource.two.xa-properties.user=
my.datasource.two.xa-properties.password=
my.datasource.two.max-pool-size=
my.datasource.two.min-pool-size=
    @Bean
    @Primary
    @ConfigurationProperties("my.datasource.one")
    public DataSource dataSourceOne() {
        return new AtomikosDataSourceBean();
    }

    @Bean("entityManagerOne")
    @DependsOn("transactionManager")
    public LocalContainerEntityManagerFactoryBean entityManagerOne(@Autowired JpaVendorAdapter jpaVendorAdapter) {
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        properties.put("hibernate.default_schema", "public");
        properties.put("hibernate.ddl-auto", "none");

        LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
        entityManager.setJtaDataSource(dataSourceOne());
        entityManager.setJpaVendorAdapter(jpaVendorAdapter);
        entityManager.setPackagesToScan("");
        entityManager.setPersistenceUnitName("");
        entityManager.setJpaPropertyMap(properties);

        return entityManager;
    }

    @Bean
    @Primary
    @ConfigurationProperties("my.datasource.two")
    public DataSource dataSourceTwo() {
        return new AtomikosDataSourceBean();
    }

    @Bean("entityManagerTwo")
    @DependsOn("transactionManager")
    public LocalContainerEntityManagerFactoryBean entityManagerTwo(@Autowired JpaVendorAdapter jpaVendorAdapter) {
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        properties.put("hibernate.default_schema", "public");
        properties.put("hibernate.ddl-auto", "none");

        LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
        entityManager.setJtaDataSource(dataSourceTwo());
        entityManager.setJpaVendorAdapter(jpaVendorAdapter);
        entityManager.setPackagesToScan("");
        entityManager.setPersistenceUnitName("");
        entityManager.setJpaPropertyMap(properties);

        return entityManager;
    }

jta.properties

com.atomikos.icatch.enable_logging=false
com.atomikos.icatch.default_jta_timeout=60000000
com.atomikos.icatch.max_timeout=100000000
com.atomikos.icatch.threaded_2pc=true
    @Bean
    public JpaVendorAdapter jpaVendorAdapter(@Autowired Environment env) {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql(false);
        return hibernateJpaVendorAdapter;
    }

    @Bean
    public UserTransaction userTransaction() throws SystemException {
        var userTransaction = new UserTransactionImp();
        userTransaction.setTransactionTimeout(60000);
        return userTransaction;
    }

    @Bean
    public TransactionManager atomikosTransactionManager() throws SystemException {
        var userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(true);
        return userTransactionManager;
    }

    @Bean
    public PlatformTransactionManager transactionManager(@Autowired UserTransaction userTransaction, @Autowired TransactionManager atomikosTransactionManager) throws Throwable {
        return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
    }

为 postgres 启用准备好的事务

postgresql.conf

max_prepared_transactions = 100     # zero disables the feature

帮助我得到这个的资源:

https://www.baeldung.com/java-atomikos

http://www.thedevpiece.com/configuring-multiple-datasources-using-springboot-and-atomikos/

https://github.com/YihuaWanglv/spring-boot-jta-atomikos-sample