事务(ACID)的相关概念以及基本使用这里就不展开介绍,主要记录我在解决事物回滚失效在解决过程中需要注意的几个问题。
首先说明一下我之前忽略了的一个点,该项目是配置了双数据源,这是造成我事务失效的很关键的一个点。
最初我的方法的调用流程:
@Service
public class WjStaticalService {
public void getInitData(WjStaticalInputBean inputBean,
WjStaticalOutputBean outputBean) {
try {
/* 业务逻辑处理,解析文件数据*/
// 调用该方法将解析的数据存入数据库
saveData(inputBean, outputBean); // 最初的调用方式,事务失效
} catch (Exception ex) {
ex.printStackTrace();
}
@Transactional
public void saveData(WjStaticalInputBean inputBean,
WjStaticalOutputBean outputBean) {
/* 业务逻辑处理*/
wjFileDao.insert(wjFile);// 插入文件记录表
/* 业务逻辑处理*/
dataDetailDao.insertBatch(dataDetails); // 插入详情表
}
}
导致事务失效,原因是在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象,只有被动态代理直接调用的才会产生事务。这里的内部方法调用的是真实对象的方法而不是代理对象的方法。
解决方法:
将this调用替换成代理类的方法调用
public void getInitData(WjStaticalInputBean inputBean,
WjStaticalOutputBean outputBean) {
try {
/* 业务逻辑处理,解析文件数据*/
//saveData(inputBean, outputBean);
// 替换为:
WjStaticalService proxy =(WjStaticalService) AopContext.currentProxy();
proxy.saveData(inputBean, outputBean);
} catch (Exception ex) {
ex.printStackTrace();
}
}
参考Spring事务失效的2种情况
如果使用以上方式,调用代理类的方法抛出异常:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available
,
解决方法:
1、需要在数据源配置类上加上该注解@EnableAspectJAutoProxy(exposeProxy = true)
(见下文的代码实例)
2、参考这篇文章
由于操作的是第二个数据源,然而我并没有配置并指定第二个数据源的事务管理器,导致默认使用第一个数据源的事务管理器,当然会导致第二个数据源的事务失效。在多数据源中配置事务,前提是首先把多数据源都配好的情况下
SpringBoot配置多数据源和事务管理
解决方法:
@Configuration
@EnableTransactionManagement
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan(basePackages = "ys.manufacture.qins.dao", sqlSessionFactoryRef = "secondSqlSessionFactory")
public class SecondDataSourceConfig{
// 配置第二个数据源
@Bean(name = "secondDataSource")
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource secondDataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "secondSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/second/*.xml"));
return bean.getObject();
}
@Bean(name = "secondSqlSessionTemplate")
public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 配置第二个数据源的事务管理
* @param dataSource
* @return
*/
@Bean(name = "secondTransactionManager")
public PlatformTransactionManager secondTransactionManager() {
return new DataSourceTransactionManager(secondDataSource());
}
}
在声明事务时需要指定上面配置的当前数据源事务管理
@Service
public class WjStaticalService {
public void getInitData(WjStaticalInputBean inputBean,
WjStaticalOutputBean outputBean) {
try {
/* 业务逻辑处理,解析文件数据*/
// 调用该方法将解析的数据存入数据库
//saveData(inputBean, outputBean)
WjStaticalService proxy =(WjStaticalService) AopContext.currentProxy();
proxy.saveData(inputBean, outputBean);
} catch (Exception ex) {
ex.printStackTrace();
}
@Transactional(value = "secondTransactionManager",rollbackFor = Exception.class)
public void saveData(WjStaticalInputBean inputBean,
WjStaticalOutputBean outputBean) {
/* 业务逻辑处理*/
wjFileDao.insert(wjFile);// 插入文件记录表
/* 业务逻辑处理*/
dataDetailDao.insertBatch(dataDetails); // 插入详情表
}
}
新建一个配置类实现TransactionManagementConfigurer
@Configuration
public class DigitalOpsStartup implements TransactionManagementConfigurer {
@Autowired
private SecondDataSourceConfig dataSource;
// 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return dataSource.secondTransactionManager();
}
}
完美解决事务回滚失效