老师,能帮我看一下吗?两个spring data jpa数据源共用一个jtaTransactionManager出问题啦~

来源:4-9 JTA多数据源事务实例

他门说这就是人生

2019-12-16

我想测试两个MySQL数据库的多数据源事务,配置了两个spring data jpa数据源。如下:

@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
    basePackages = "com.gaojingsi.transaction.muiltidatasourcetransactiondemo.repository.db2",
    entityManagerFactoryRef = "db2EntityManager", 
    transactionManagerRef = "jtaTransactionManager"
)
public class Db2Config {
    @Autowired
    private Environment env;

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean db2EntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(db2DataSource());
        em.setPackagesToScan(
          new String[] { "com.gaojingsi.transaction.muiltidatasourcetransactiondemo.entity.db2" });

        HibernateJpaVendorAdapter vendorAdapter
          = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
          env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Primary
    @Bean
    public DataSource db2DataSource() {

        DriverManagerDataSource dataSource
          = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                env.getProperty("db2.driver-class-name"));
        dataSource.setUrl(env.getProperty("db2.jdbc-url"));
        dataSource.setUsername(env.getProperty("db2.username"));
        dataSource.setPassword(env.getProperty("db2.password"));

        return dataSource;
    }

//    @Primary
//    @Bean
//    public PlatformTransactionManager db2TransactionManager() {
//
//        JpaTransactionManager transactionManager
//          = new JpaTransactionManager();
//        transactionManager.setEntityManagerFactory(
//          db2EntityManager().getObject());
//        return transactionManager;
//    }
}
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
    basePackages = "com.gaojingsi.transaction.muiltidatasourcetransactiondemo.repository.db1",
    entityManagerFactoryRef = "db1EntityManager", 
    transactionManagerRef = "jtaTransactionManager"
)
public class Db1Config {
    @Autowired
    private Environment env;

    @Bean
    public LocalContainerEntityManagerFactoryBean db1EntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(db1DataSource());
        em.setPackagesToScan(
          new String[] { "com.gaojingsi.transaction.muiltidatasourcetransactiondemo.entity.db1" });

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
          env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public DataSource db1DataSource() {

        DriverManagerDataSource dataSource
          = new DriverManagerDataSource();
        dataSource.setDriverClassName(
          env.getProperty("db1.driver-class-name"));
        dataSource.setUrl(env.getProperty("db1.jdbc-url"));
        dataSource.setUsername(env.getProperty("db1.username"));
        dataSource.setPassword(env.getProperty("db1.password"));

        return dataSource;
    }

//    @Bean
//    public PlatformTransactionManager db1TransactionManager() {
//
//        JpaTransactionManager transactionManager
//          = new JpaTransactionManager();
//        transactionManager.setEntityManagerFactory(
//          db1EntityManager().getObject());
//        return transactionManager;
//    }
}

还配置了一个公共的PlatformTransactionManager:

@Configuration
@EnableTransactionManagement
public class WebConfig implements WebMvcConfigurer {

    @Bean(name = "jtaTransactionManager")
    public PlatformTransactionManager jtaTransactionManager() {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        UserTransaction userTransaction = new UserTransactionImp();
        return new JtaTransactionManager(userTransaction, userTransactionManager);
    }

}

如果在@EnableJpaRepositories的transactionManagerRef中,各用各的transactionManager,两个数据库都能成功插入数据,但没法保障两个数据库的事务。

所以我配置了一个jtaTransactionManager,希望两个数据源共用这个jtaTransactionManager,结果在没有异常和错误的情况下也只有一个数据库的操作生效。不知道如何测试两个MySQL数据库的事务,并保证一致性和原子性。

ps:

我的demo在gitee上:

https://gitee.com/feitianzouxiu/multi-datasource-transaction-demo.git

老师抽空帮我看看行吗?感激不尽呀~~~

写回答

1回答

大漠风

2020-01-06

首先,实在是抱歉,我前段时间一直出差,也比较忙,没有顾上看这个问题,我看到你问的几个多数据源的问题,我还给你回复了,结果你都自己解决了。还请见谅。

你试一下:

HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA");
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setJpaPropertyMap(properties);

如果还不行,那就是需要使用XADataSource,也就是`MysqlXADataSource`。


0
0

分布式事务实践,从原理到实例,解决数据一致性

掌握分布式事务实现技术,是架构师必备技能。

1149 学习 · 153 问题

查看课程