简体   繁体   English

为什么Spring @Transactional成功回滚(SQL插入)但在(SQL更新)时失败-多个数据源

[英]Why Spring @Transactional is successfully rolling back on (SQL insert) but fails when (SQL update) - Multiple Datasources

I have a project that uses Spring Boot & JPA. 我有一个使用Spring Boot和JPA的项目。 APIs are exposed using REST. 使用REST公开API。

I have two different databases (Mysql 8) connected and for pooling Hikari has been added. 我连接了两个不同的数据库(Mysql 8),并已为池添加了Hikari。 Chained transaction manager has been used for rolling back both DBs in case any one of them failed. 链式事务管理器已用于回滚两个数据库,以防其中任何一个发生故障。

First Issue: If i mark TMSDbConfig as @Primary then no insertion takes place in secondary DB which is UserProfileDbConfig. 第一个问题:如果我将TMSDbConfig标记为@Primary,则在辅助数据库(即UserProfileDbConfig)中不会进行任何插入。 Only One transactionManager start which is capable to insert in TMSDbConfig. 只有一个transactionManager启动,可以插入TMSDbConfig。

Second Issue: If i mark UserProfileDbConfig as @Primary then the above problem is solved and now i can insert in both DBs and on insert failure they both are rolling back as well. 第二个问题:如果我将UserProfileDbConfig标记为@Primary,那么上述问题就解决了,现在我可以在两个数据库中插入,并且在插入失败时,它们都会回滚。 But when i get the Entity from DB (using findByName())do some changes and persist it back on the failure of second the first one never rollback. 但是,当我从DB(使用findByName())获得Entity时,请进行一些更改,并在第二次失败时将其持久化,第一个永远不会回滚。

I have tried, @EnableTransactionManagement on MainApp, @ComponentScan @EntityScan 我已经尝试过MainApp上的@ EnableTransactionManagement,@ ComponentScan @EntityScan

Tried adding annotations @Repository or @Component as well. 尝试添加注释@Repository或@Component。 Also tried to put the methods in different beans. 还尝试将这些方法放在不同的bean中。

Nothing works. 什么都没有。

Following are the configurations, Main: 以下是主要的配置:

@SpringBootApplication
public class SingleEntryAuthServiceApp

DB Config, 数据库配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.mycompany.chainedtransactionmanager.repository.db2",
        entityManagerFactoryRef = "db2EntityManagerFactory", transactionManagerRef = "db2PlatformTransactionManager")
public class UserProfileDbConfig {

    @Primary
    @Bean(name = "db2DataSource")
    @ConfigurationProperties(prefix = "spring.userprofile-datasource")
    public DataSource db2DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "db2EntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean db2EntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(db2DataSource())
                .packages("com.mycompany.chainedtransactionmanager.model.db2")
                .persistenceUnit("db2")
                .build();
    }

    @Primary
    @Bean(name = "db2PlatformTransactionManager")
    public PlatformTransactionManager db2PlatformTransactionManager(EntityManagerFactory db2EntityManagerFactory) {
        return new JpaTransactionManager(db2EntityManagerFactory);
    }
}

Another DB Config, 另一个数据库配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.mycompany.chainedtransactionmanager.repository.pgsql",
        entityManagerFactoryRef = "pgsqlEntityManagerFactory", transactionManagerRef = "pgsqlPlatformTransactionManager")
public class TMSDbConfig {


    @Bean(name = "pgsqlDataSource")
    @ConfigurationProperties(prefix = "spring.tms-datasource")
    public DataSource pgsqlDataSource() {
        return DataSourceBuilder.create().build();
    }


    @Bean(name = "pgsqlEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean 
        pgsqlEntityManagerFactory(@Qualifier("pgsqlDataSource") DataSource 
        pgsqlDataSource, EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(pgsqlDataSource)
                .packages("com.mycompany.chainedtransactionmanager.model.pgsql")
                .persistenceUnit("pgsql")
                .build();
    }


    @Bean(name = "pgsqlPlatformTransactionManager")
    public PlatformTransactionManager pgsqlPlatformTransactionManager(@Qualifier("pgsqlEntityManagerFactory") EntityManagerFactory pgsqlEntityManagerFactory) {
        return new JpaTransactionManager(pgsqlEntityManagerFactory);
    }
}

ChainedTransactionManager, ChainedTransactionManager,

@Configuration
public class TransactionManagerConfig {

    @Bean(name = "chainedTransactionManager")
    public ChainedTransactionManager transactionManager(
            @Qualifier("db2PlatformTransactionManager") PlatformTransactionManager db2PlatformTransactionManager,
            @Qualifier("pgsqlPlatformTransactionManager") PlatformTransactionManager pgsqlPlatformTransactionManager) {
        return new ChainedTransactionManager(db2PlatformTransactionManager,
                pgsqlPlatformTransactionManager);
    }
}

Service Layer Insert Code, Successfully Rolling Back, 服务层插入代码,成功回滚,

@Override
@Transactional(value = "chainedTransactionManager", rollbackFor = {Exception.class}, propagation = Propagation.REQUIRES_NEW)
public void saveService() {
    try {
        {//Insert code..


            DB2Test1 SecTable = new DB2Test1();
            SecTable.setName("LongTextToCreateSqlError");
            db2Repo.save(SecTable);

            DB1Test1 firstTable = new DB1Test1();
            firstTable.setName("acceptedText");
            db1Repo.save(firstTable);
        }
    } catch (Exception e) {
    }

Service Layer Update Code, Failed Rolling Back, 服务层更新代码,回滚失败,

@Override
    @Transactional(value = "chainedTransactionManager", rollbackFor = {Exception.class}, propagation = Propagation.REQUIRES_NEW)
    public void saveService() {
try {
    DB2Test1 SecTable = db2Repo.findByName("Jhon");
    SecTable.setName("LongTextToCreateFailed");

    try {
        db2Repo.save(SecTable);
    } catch (Exception e) {
        throw e;
    }
    DB1Test1 firstTable =  db1Repo.findByName("Jhon");
    firstTable.setName("AcceptedText");
    try {
        db1Repo.save(firstTable);
    } catch (Exception e) {
        throw e;
    }
} catch (Exception e) {
    throw e;
}
}

In the logs i can see that in case of Insert the entityManager didnt closed until both db operation got success, 在日志中,我可以看到在插入两个数据库操作成功之前,entityManager并没有关闭,

but in case of update after running the first update Query the entityManager closed. 但如果在运行第一个更新后进行更新,则查询实体管理器将关闭。 I am unable to understand the behavior, Insert case is successfully rolling back but on update its not rolling back. 我无法理解该行为,插入大小写已成功回滚,但在更新时未回滚。 If its realated with managed beans then what is the difference between insert code and update code. 如果使用托管bean实现,那么插入代码和更新代码之间有什么区别。

Thanks a lot Greetings 非常感谢

Solution

Found a Solution, May be it will save couple of days for someone searching for this, 找到了一个解决方案,可能会为搜索此内容的人节省几天的时间,

I switched from JPA simple .save() method to .saveAndFlush() and all the test cases have been passed either both persists in DB or both rollback. 我从JPA简单的.save()方法切换到.saveAndFlush(),并且所有测试用例都通过了,要么都保留在数据库中,要么都通过了回滚。

Cheers! 干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM