1、事务分类
1、编程式事务
基于底层的API,如PlatformTransactionManager、TransactionDefinition 和 TransactionTemplate 等核心接口
@Autowired
private DataSourceTransactionManager transactionManager;
public void test() {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// TODO 事务操作
// 事务提交
transactionManager.commit(status);
} catch (DataAccessException e) {
// 事务回滚
transactionManager.rollback(status);
throw e;
}
}
或者使用spring 的bean对象TransactionTemplate 实现
@Autowired
private TransactionTemplate transactionTemplate;
public void test(){
return transactionTemplate.execute(transactionStatus -> {
//事务操作
});
}
public void test1(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
}
});
}
2、声明式事务
通过@Transactional 注解,本质上通过AOP方式实。仅限声明在方法上
@Transactional
public void test() {
// 事务操作
}
事务不生效场景
- 1、声明在非public 方法或static方法上
- 2、同一个class文件,方法内部调用
- 3、不是通过bean对象 . 方法的方式调用
- 4、抛出的异常与rollbackFor不一致
- 5、方法内部手动catch异常,并未抛出
- 6、数据库未开启或不支持事务
- 7、属性 propagation 设置错误
局限性
1、RPC远程调用,本地方法回滚,但是rpc远程方法无法回滚
2、方法上的声明,粒度大,无法针对具体的代码块,必须提炼成新方法
3、远程调用,导致事务长时间锁定
4、出现在分库分表、跨库查询中,无法回滚
5、AOP切面间相互影响,比如存在全局异常捕获的切面
[参考]@Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
3、多线程下的事务生效问题
1、基于注解的@Transcational 在多线程下不生效,由于aop切面维护了一个ThreadLocal存储事务的上下文信息。
2、基于编程式,在多线程下生效需要开启线程间数据共享
- 1、 通过传递参数的方式,在创建子线程传递。
- 2、父线程创建 InheritableThreadLocal 静态的类变量,子线程可以自动继承父线程的threadlocal,从而达成共享