Spring支持两种事务方式:
- 编程式事务:使用的是TransactionTemplate(或者org.springframework.transaction.PlatformTransactionManager接口)
- 声明式事务:使用Transactional注解或者xml配置,建立在AOP之上的。本质就是对方法前后进行拦截,然后在目标方法开始之前创建或者添加一个事物,在执行完目标方法之后根据执行情况提交或者回滚事物
使用@Transactional注解时需要记住两个点:
第一点:@Transactional 只能应用到 public 方法才有效
只有@Transactional 注解应用到 public 方法,才能进行事务管理。这是因为在使用 Spring AOP 代理时,Spring 在调用TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取@Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法
protected TransactionAttribute computeTransactionAttribute(Method method,Class<?> targetClass) {
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}
第二点:避免 Spring 的 AOP 的自调用问题
在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚
原因:调用的是原生方法,并不是代理对象的方法
@Service
public class DemoService {
private void method1() {
insertOrder();
}
//当method2()被method1调用时,@Transactional失效
@Transactional
public void method2() {
//so some thing
}}
当然上面两个问题也是能解决的,使用 AspectJ 取代 Spring AOP 代理
1.认识@Transactional 注解的属性
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default ""; //当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
@AliasFor("value")
String transactionManager() default ""; //当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
Propagation propagation() default Propagation.REQUIRED; //事务的传播行为
Isolation isolation() default Isolation.DEFAULT; //事务的隔离级别
int timeout() default -1; //事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务
boolean readOnly() default false; //指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true
Class<? extends Throwable>[] rollbackFor() default {}; //用于指定能够触发事务回滚的异常类型,可以指定多个异常类型
String[] rollbackForClassName() default {}; //类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组,和rollbackFor对应
Class<? extends Throwable>[] noRollbackFor() default {}; //抛出指定的异常类型,不回滚事务,也可以指定多个异常类型
String[] noRollbackForClassName() default {}; //类名数组,必须继承自Throwable 导致事务不回滚的异常类名字数组,和noRollbackFor对应
}
2.propagation 属性,isolation 属性
事务的传播行为(TransactionDefinition定义):
Propagation.REQUIRED 如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
Propagation.REQUIRES_NEW 重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
Propagation.SUPPORTS 如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
//下面使用场景较少
Propagation.MANDATORY 如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
Propagation.NOT_SUPPORTED 以非事务的方式运行,如果当前存在事务,暂停当前的事务。
Propagation.NEVER 以非事务的方式运行,如果当前存在事务,则抛出异常。
Propagation.NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED
事务的隔离级别(TransactionDefinition定义):
ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是ISOLATION_READ_COMMITTED。
ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
Spring定义的TransactionDefinition接口:
3.场景Demo测试
实体类Company
@Data
@Builder
public class Company {
private String companyId;//公司编码
private String companyName;//公司名称
private String memo;//备注
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class MytransactionApplicationTests {
@Autowired
private OneCompanyServiceImpl oneCompanyservice;
@Autowired
private TwoCompanyServiceImpl twoCompanyservice;
@Test
//测试Propagation.REQUIRED
public void test1() {
Company company = Company.builder().companyId("123").companyName("阿里巴巴").memo("杭州").build();
oneCompanyservice.multiInsert1(company);
}
@Test
//测试Propagation.REQUIRES_NEW
public void test2() {
Company company = Company.builder().companyId("123").companyName("腾讯").memo("深圳").build();
oneCompanyservice.multiInsert2(company);
}
}
当前 oneCompanyService调用另一个employeeService
1.测试 Propagation.REQUIRED
@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class)
public void multiInsert1(Company company) {
oneComPanyDao.insert(company);
Employee employee = Employee.builder().employeeId("123").employeeName("马云").memo("我对钱不感兴趣").build();
employeeService.insert(employee);//@Transactional的Propagation为默认REQUIRED,加入当前事务
}
@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void insert(Employee employee) {
employeeDao.insert(employee);
int flag = 1/0;
}
结果:company和employee都没有插入数据库
2.测试 Propagation.REQUIRES_NEW
@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class)
public void multiInsert2(Company company) {
oneComPanyDao.insert(company);
Employee employee = Employee.builder().employeeId("456").employeeName("马化腾").memo("我只是住的房子大一点").build();
employeeService.insertOther(employee);// @Transactional的Propagation设置为REQUIRES_NEW,另启事务
int flag = 1/0;//这里发生异常insertOther事务已经提交,不会回滚,insert方法回滚
}
@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void insertOther(Employee employee) {
employeeDao.insert(employee);
}
结果:company没有插入数据库,employee插入数据库
还有其他种Propagation行为,平时使用场景也比较少,不赘述