Spring事务
1.简介
Spring提供两种事务管理方式,分为编程式和声明式。
编程式
:通过编码的方式手动启用、提交或回滚事务,粒度更细,但更麻烦。声明式
:通过在方法或类或接口上添加注解进行包装,无侵入地实现事务,更方便,但粒度更大。
需要注意的是,使用的数据库需要支持事务,否则事务将不起作用。如MySql的MyIsam引擎就不支持事务。
2.实例
业务代码
在下面的实例中,我要先创建申请,再创建申请处理。如果创建申请处理失败,应该回滚。这里是没加事务的。为了演示,我们先注掉第7行,模拟出错。
@Override
public boolean saveTransaction(CertApply certApply) {
certApply.setState(1); //初审中
save(certApply);
ApplyDeal applyDeal = new ApplyDeal();
applyDeal.setState(1); //初审
applyDeal.setApplyId(certApply.getId());
applyDealService.save(applyDeal);
return true;
}
运行测试
显然出现了我们要看到的问题
接下来开始使用事务进行管理
3.解决问题
依赖(Spring)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
开启事务管理
@SpringBootApplication
@EnableAspectJAutoProxy
@EnableTransactionManagement
@MapperScan("com.example.course_system.mapper")
public class CourseSystemApplication {
public static void main(String[] args) {
SpringApplication.run(CourseSystemApplication.class, args);
}
}
在方法上加上@Transaction注解
@Override
@Transactional
public boolean saveTransaction(CertApply certApply) {
certApply.setState(1); //初审中
save(certApply);
ApplyDeal applyDeal = new ApplyDeal();
applyDeal.setState(1); //初审
// applyDeal.setApplyId(certApply.getId());
applyDealService.save(applyDeal);
return true;
}
测试
没有新的记录加入。结果正确!
4.SpringBoot实现声明式事务的几种方式
-
基于动态代理支持@Transaction。
就是上面的那种方法。但这种方法是有局限性的。在有些情况下会失效。
- 同一个类中,方法A实现了事务,方法B中没实现事务,B中调用A。事务失效(没有经过代理类,俗称自调用问题)
- 数据库引擎不支持事务
- 没有被 Spring 管理
- 方法不是 public 的
- 异常被吃了 (高频)异常类型错误(一定要抛出RuntimeException,或者配置识别的事务类型)
-
基于AspectJ编译期织入来支持@Transactional
- 用来解决上面的方法不是public的时候动态代理失效的问题
5.基于AspectJ编译期织入来支持@Transactional
我们可以采用“编译期织入”或“类加载期织入”的方式,在运行代码前,将我们的目标类的方法增强,无需管用“动态代理”实现时的种种限制。本文就接下来就讲下,如何使用AspectJ来实现在编译期对@Transactional注解的方法进行织入。
5.1.导包
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
5.2.配置
将@EnableTransactionManagement中的mode设置为AdviceMode.ASPECTJ(默认为AdviceMode.PROXY,也就是我们的动态代理~)
然后就可以啦。这里就不测试,用兴趣的同学可以去实验下。
6.一些补充知识
6.1Transaction注解的源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* Alias for {@link #transactionManager}.
* @see #transactionManager
*/
@AliasFor("transactionManager")
String value() default "";
/**
* 用于指定事务管理器。
*/
@AliasFor("value")
String transactionManager() default "";
/**
* 事务传播级别
* 可选的值有:
* Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
* 可以理解为和外部事物融为一体,共存亡。即使外层事务trycatch了内层事务的异常,两个事务还是一起回滚
* Propagation.SUPPORTS如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
* 简单理解:有事务就加入,没有就算了。
* Propagation.MANDATORY如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
* 简单理解:需要一个有事务的大哥来带他走,他自己就报错。很符合mandatory的意思 托管
* Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,挂起当前的事务。
* 简单理解:外层出事不影响该事务。内层出事了,异常会抛给外层
* Propagation.NOT_SUPPORTED以非事务的方式运行,如果当前存在事务,暂停当前的事务。
* 简单理解:机动车道上的非机动车,自己出问题不会回滚,但会害和他一道的事务回滚
* Propagation.NEVER以非事务的方式运行,如果当前存在事务,则抛出异常。
* Propagation.NESTED和 Propagation.REQUIRED 有细微差别。如果当前存在事务,则作为该事务的子事务,如果当前不存在事 * 务,则创建一个新的事务。外层事务trycatch了内层事务的异常,只有内存事务会回滚
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* 事务隔离级别
* Isolation.DEFAULT:使用底层数据库默认的隔离级别。
* Isolation.READ_UNCOMMITTED
* Isolation.READ_COMMITTED
* Isolation.REPEATABLE_READ
* Isolation.SERIALIZABLE
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* 事务超时时间。超过时间没完成,自动回滚。默认-1
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* 是否为只读事务。可以帮助事务优化。只是一个暗示,对最终事务的结果没有影响。
*/
boolean readOnly() default false;
/**
* 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* 如上
*/
String[] rollbackForClassName() default {};
/**
* 不回滚的异常类型
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 如上
*/
String[] noRollbackForClassName() default {};
}
参考:事务传播机制 https://blog.csdn.net/qq_35493807/article/details/105756761
/**
* 不回滚的异常类型
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 如上
*/
String[] noRollbackForClassName() default {};
}
参考:事务传播机制 https://blog.csdn.net/qq_35493807/article/details/105756761