多数据源时,使用TestNG进行单元测试数据未回滚

场景

上午准备写接口的单元测试,结果发现测试数据全部未回滚
之前写单测是没问题的,另外一个相同的工程,也是一样的配置,但是数据能正常回滚
在网上寻找了很多答案,都说继承 AbstractTransactionalTestNGSpringContextTests 即可
最开始也是这样做的,没有问题
但现在测试方法产生的数据却不受事务控制,无法回滚

环境

Springboot、Mybatis、PostgreSQL、TestNG

查找原因

通过对比两个工程,发现事务回滚时,日志信息有差异,如图1、图2


图1 数据未回滚的单测
图2 数据回滚的单测

从图中我们可以发现,事务一个由Neo4jTransactionManager管理,一个由DataSourceTransactionManager管理

通过日志,突然想起之前有同事要使用Neo4j,于是在工程里查找,发现一个关于Neo4j的配置类,如图3

图3 Neo4j配置的事务管理

该配置项类创建了 Neo4jTransactionManager
所以怀疑是这里将事务的控制权拿走了,造成PostgreSql不能受到spring的事务控制
为了确认DataSourceTransactionManager 是否失效,启动工程后,故意产生一个空指针,发现,程序虽然发生异常,但是报错前产生的数据,依然存入了pgsql,并未进行回滚
在这里,可以完全确认,是多数据源造成的事务不一致

解决

通过查询资料,确定ChainedTransactionManager可以实现多数据源的事务控制,如图4

图4 多数据源事务管理

注:Neo4jConfig图3 框注的代码需注释掉,否则会产生两个manager

代码

MultiDataSourceTransactionManager

@Component
public class MultiDataSourceTransactionManager {

    @Resource
    private DataSourceConfig dataSourceConfig;

    @Resource
    private Neo4jConfig neo4jConfig;
  
    /**
     * Transaction 相关配置
     * 因为有多个数据源,
     * 所有使用ChainedTransactionManager把它们放在一起管理
     */
    @Bean("transactionManager")
    public PlatformTransactionManager transactionManager() throws BizException {
        DataSourceTransactionManager pgSql = new DataSourceTransactionManager(dataSourceConfig.dataSource());
        Neo4jTransactionManager neo4j = new Neo4jTransactionManager(neo4jConfig.sessionFactory());
        return new ChainedTransactionManager(pgSql, neo4j);
    }
}

DataSourceConfig

@Configuration
public class DataSourceConfig {

    @Value("${spring.datasource.password}")
    private String passWord;
    @Value("${spring.datasource.username}")
    private String userName;
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Resource
    private Environment env;

    @Bean
    public DataSource dataSource() throws BizException {
        DataSource configDataSource = new DataSource();
        configDataSource.setUrl(url);
        configDataSource.setDriverClassName(driverClassName);
        configDataSource.setUsername(userName);
        configDataSource.setPassword(passWord);
        configDataSource.setInitialSize(5);
        configDataSource.setDefaultAutoCommit(true);

        if (configDataSource.getUrl().startsWith("jdbc:postgresql://ip")) {
            throw new BizException("请配置数据源地址");
        }
        if (configDataSource.getUsername().startsWith("username")) {
            throw new BizException("请配置数据源用户名");
        }
        if (configDataSource.getPassword().startsWith("password")) {
            throw new BizException("请配置数据源密码");
        }

        configDataSource.setMinEvictableIdleTimeMillis(1800000);
        configDataSource.setTestWhileIdle(true);
        configDataSource.setMaxActive(50);
        configDataSource.setMaxIdle(20);
        configDataSource.setMinIdle(5);
        configDataSource.setMaxWait(6000);
        configDataSource.setRemoveAbandoned(true);
        configDataSource.setRemoveAbandonedTimeout(6000);
        configDataSource.setValidationQuery("select now();");
        configDataSource.setValidationQueryTimeout(60000);
        configDataSource.setValidationInterval(30000);
        configDataSource.setTimeBetweenEvictionRunsMillis(1800000);
        configDataSource.setTestOnBorrow(true);

        return configDataSource;
    }
}

Neo4jConfig

@Configuration
public class Neo4jConfig {

    @Value("${neo4j.username}")
    private String username;

    @Value("${neo4j.password}")
    private String password;

    @Value("${neo4j.uri}")
    private String uri;

//    @Bean
//    public Neo4jTransactionManager transactionManager() {
//        return new Neo4jTransactionManager(sessionFactory());
//    }


    @Bean
    public SessionFactory sessionFactory() {
        return new SessionFactory(configuration(),"com.**.neo4j.entity");
    }

    @Bean
    public org.neo4j.ogm.config.Configuration configuration() {
        org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
        config
                .driverConfiguration()
                .setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver")
                .setURI(String.format("http://%s:%s@%s", username, password, uri));
        return config;
    }
}

你可能感兴趣的:(多数据源时,使用TestNG进行单元测试数据未回滚)