Spring事务

109 阅读4分钟

Spring 框架支持声明式和编程式事务管理。

1、在声明式事务管理中,可以使用注释或XML配置声明如何管理事务。 通过AOP(面向切面)方式在方法前使用编程式事务的方法开启事务,在方法后提交或回滚。用配置文件的方法或注解方法(如:@Transactional)控制事务。
2、在编程式事务管理中,可以使用编程方式管理事务 手动开启、提交、回滚事务。

事务管理

事务管理器主要有三个接口:

PlatformTransactionManager: 提供了管理事务的基本操作,如开始事务,提交事务和回滚事务。

获取事务 TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
提交事务 void commit(TransactionStatus status) throws TransactionException;
回滚事务 void rollback(TransactionStatus status) throws TransactionException;
TransactionDefinition: 提供了事务的定义,如隔离级别,超时和传播行为。
TransactionStatus: 提供了事务的状态,如是否已提交或已回滚。\

Spring 框架提供了许多实现 PlatformTransactionManager 接口的类, 其中包括:

DataSourceTransactionManager: 用于在JDBC事务中使用。
JpaTransactionManager: 用于在JPA事务中使用。
HibernateTransactionManager: 用于在Hibernate事务中使用。\

Spring事务七大传播特性

PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;否则,创建一个新事务。 PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;否则,不使用事务。 PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;否则,抛出异常。 PROPAGATION_REQUIRES_NEW: 创建一个新事务,并挂起当前事务(如果存在)。 PROPAGATION_NOT_SUPPORTED: 不使用事务;如果当前存在事务,则挂起该事务。 PROPAGATION_NEVER: 不使用事务;如果当前存在事务,则抛出异常。 PROPAGATION_NESTED: 如果当前存在事务,则在嵌套事务中执行;否则,创建一个新事务。

Spring事务失效常见场景

1、事务方法访问修饰符非public,导致事务失效\

1.1、解决

方式一:将方法修饰符改为public
方式二:开启AspectJ代理模式

1.2、注意

  如果事务是static、final的,同样无法通过动态代理,事务也是不会生效的。
Spring的声明式事务是基于动态代理实现的,我们无法重写final修饰的方法;
不管是JDK动态代理还是Cglib的动态代理,就是要通过代理的方式获取到代理的具体对象,而static方法修饰的方法是属于类的,不属于任何对象,所以static方法不能被重写,即便写法上是重写,但是并不具备重写的含义,也就是说static方法也不被进行动态代理

2、事物回滚默认:运行时异常(RuntimeException)和程序错误(Error)
1.2、注意
IOException、SQLException不会回滚
3、catch掉异常之后,没有再次抛出异常,导致事务失效\

4、数据表本身是不支持事务,导致事务失效
4.1、实例
如果使用MySQL且存储引擎是MyISAM,则事务是不起作用的,原因是MyIASM不支持事务。
4.2、解决
数据表可以改为InnoDB存储引擎,支持事务
5、方法自身(this)调用问题,导致事务失效
5.1、解决
获取代理,利用代理类调用自己的方法
AopContext.currentProxy(). 或者实现ApplicationContextAware获取getbean
6、未被 spring 管理
通常情况下,通过 @Controller、@Service、@Component、@Repository 等注解,可以自动实现 bean 实例化和依赖注入的功能。
如果忘记加这些注解,这个类就不会交给 spring 管理,就事务失效
7、未开启事务
spring 是需要通过 xml 去配置的。springboot 是默认开启事务的。
8、错误使用传播机制
如果事务的传播特性设置错了,事务也会失效。
如下:propagation = Propagation.NEVER这种类型的传播特性不支持事务,如果有事务会抛出异常。
9、代码没有开启事务、数据库不支持事务
10、多线程事务失效
指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
10.1、解决
TransactionTemplate.execute方法
重写TransactionCallbackWithoutResult方法解决 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue()); executor.execute(()->{ transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { Response response = clientCollectOrderBusinessService.executeCancel(dto) } }); });