场景
上午准备写接口的单元测试,结果发现测试数据全部未回滚
之前写单测是没问题的,另外一个相同的工程,也是一样的配置,但是数据能正常回滚
在网上寻找了很多答案,都说继承 AbstractTransactionalTestNGSpringContextTests 即可
最开始也是这样做的,没有问题
但现在测试方法产生的数据却不受事务控制,无法回滚
环境
Springboot、Mybatis、PostgreSQL、TestNG
查找原因
通过对比两个工程,发现事务回滚时,日志信息有差异,如图1、图2


从图中我们可以发现,事务一个由Neo4jTransactionManager管理,一个由DataSourceTransactionManager管理
通过日志,突然想起之前有同事要使用Neo4j,于是在工程里查找,发现一个关于Neo4j的配置类,如图3

该配置项类创建了 Neo4jTransactionManager
所以怀疑是这里将事务的控制权拿走了,造成PostgreSql不能受到spring的事务控制
为了确认DataSourceTransactionManager 是否失效,启动工程后,故意产生一个空指针,发现,程序虽然发生异常,但是报错前产生的数据,依然存入了pgsql,并未进行回滚
在这里,可以完全确认,是多数据源造成的事务不一致
解决
通过查询资料,确定ChainedTransactionManager可以实现多数据源的事务控制,如图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;
}
}
网友评论