最近有个需求-当删除旧数据然后保存新数据时,只有删除旧数据成功同时保存新数据成功才可以不然的话就回滚,因为新数据保存那数据量比较大由子线程完成,然后删除数据是在主线程完成。
在方法上加上如下注解,看似可以其实不行,因为主线程与子线程不是同一个事务
@Transactional(rollbackFor = Exception.class)
@Transactional失效有如下场景
1.@Transactional修饰的方法为非public方法
因为@Transactional是基于动态代理来实现的,非public的方法,@Transactional的动态代理对象信息为空,所以不能回滚。
2.在类内部没有添加@Transactional的方法,调用@Transactional方法时
@Transactional是基于动态代理对象来实现的,而在类内部的方法的调用是通过this关键字来实现的,没有经过动态代理对象,所以事务回滚失效。
3.在@Transactional方法内部捕获了异常,没有在catch代码块里面重新抛出异常,事务也不会回滚
4.方法用final或static修饰
如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法而添加事务功能。
5.未被spring管理
通常情况下,我们通过@Controller、@Service、@Component、@Repository等注解,可以自动实现bean实例化和依赖注入的功能。
6.未开启事务
7.多线程调用
同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。
8.表不支持事务
可以看看数据库引擎
创建事务管理器
@Component
public class txManagerConfig {
// 创建事务管理器
@Bean(name = "txManager")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
实现代码
@Autowired
private PlatformTransactionManager txManager;
private boolean isExec = false;
@Override
@Transactional(rollbackFor = Exception.class)
public R save(List<XXXDataDTO> params) {
if (params == null || params.isEmpty()) {
return R.error("没有数据,保存失败!");
}
try {
//判断有无该条记录的旧数据,有则删除
......
//开启线程,执行新数据保存
Callable<Object> callable = new Callable<Object>() {
@Override
public Object call() throws Exception {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
//新数据保存
......
// 提交事务
txManager.commit(status);
} catch (Exception e) {
// 回滚事务
txManager.rollback(status);
throw new RuntimeException();
}
return "success";
}
};
FutureTask<Object> futureTask = new FutureTask<Object>(callable);
thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
} catch (Exception e) {
e.printStackTrace();
isExec = true;
throw new RuntimeException("xxx保存失败!");
} finally {
if (thread.isAlive()) {
thread.interrupt();
}
}
return isExec ? R.error("保存失败!") : R.ok();
}