一、什么是事务
具有ACID四个特性
- Atomicity(原子性):事务中的所有操作,或者全部完成,或者全部不完成
- 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏
- 事务隔离(Isolation):多个事务之间独立,不相互影响
- 持久性(Durability):事务处理结束后,对数据的修改是永久的
在spring中保证操作的原子性,需要通过spring事务来完成。对于数据库来说,每条语句都是一个单独的事务,而spring事务是从业务层面来控制事务的提交或者回滚
在spring中,使用声明式事务比较多
二、快速使用
在两个方法上标记@Transactional注解
@Transactional
public void methodA() {// DO SOMETHINGxxxMapper.insert();xxxservice.methodB();
}@Transactional
public void methodB() {throw new RuntimeException();
}
事务发生回滚,因此数据没有被插入到数据库中
三、事务传播类型
spring中定义了7种事务传播类型,其中常用的为:
REQUIRED、REQUIRES_NEW、NESTED
- REQUIRED
REQUIRED为默认的事务传播类型,特点:
当前方法存在事务时,子方法加入该事务。此时父子方法共用一个事务,无论父子方法哪个发生异常回滚,整个事务都回滚,即使父方法捕捉了异常,也是会回滚。当前方法不存在事务时,子方法新建一个事务
两个方法methodA和methodB,若A方法不开启事务,B开始事务,则B的事务不包含A,B发生回滚不会让A回滚。
如果A和B都开启事务,B会加入A的事务,父事务回滚时,子事务也回滚,反之同理。
在这种情况下,即便父事务捕获了异常,也会都回滚
- REQUIRES_NEW
特点:
无论当前方法是否存在事务,子方法都会新建一个事务。此时父子方法的事务独立,不会相互影响。
父方法发生回滚,子方法不会回滚。
但要注意,子方法抛出异常,如果父方法没有捕获,会因为异常抛出到上层导致二者都回滚。如果父方法捕获了异常,则不会回滚。
- NESTED
当前方法存在事务时,子方法加入在嵌套事务执行。当父方法事务回滚时,子方法事务也跟着回滚。子方法发生回滚时,父方法根据是否捕获了异常决定是否回滚。
看上去和REQUIRED比较类似,但REQUIRED是共用一个事务,因此在子方法发生回滚时,父方法反应也不同。
对于REQUIRED,无论父子哪个发生了回滚,另一个都会回滚
而NESTED在子方法回滚时,如果父方法捕获了异常,就不会回滚
四、事务失效
- 同一个类中的其它没有@Transactional注解的方法 内部调用另一个@Transactional注解的方法
只有当事务方法被当前类以外的代码调用时,才会有Spring生成的代理对象管理。(Spring AOP代理机制造成的)。
- @Transactional注解作用在非public修饰的方法上,会失效。
aop拦截时检查了方法是否为public
- 多线程任务可能导致@Transaction案例失效
五、为什么同一个类中,无注解调用有注解的方法不开启事务?
spring事务的原理是拦截所有bean的创建,检查@Transaction注解,如果存在注解,则会通过aop的方式去创建这个bean的代理类,代理类中会增加拦截器,拦截配置事务的public方法执行,在方法执行前开启事务,在方法结束后提交或者回滚事务
@Service
public class TestTransaction {@Transactionalvoid a() {}void b() {a();}
}
代理类类似于
public class Proxy$TestTransaction {void a() {startTransaction();//方法开启之前的开启事务方法TestTransaction.a();//类具体的执行方法//提交或回滚事务}void b() {TestTransaction.b();//类具体的执行方法}
}
如果调用方法B,代理方法B直接执行了类的具体方法,而不是执行代理类的A方法,因此没有事务相关的部分。
很多用注解来完成的增强动作失效,都是这个原理,比如@retryable