事务管理是系统开发中必不可少的一步,通常我们使用的的Spring框架
为事务管理提供了丰富的功能支持。
Spring事务管理
分为编程式
和声明式
的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于AOP
,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml
)中做相关的事务规则声明,另一种是基于@Transactional
注解的方式在Spring Boot
流行的时下,基于xml
中做相关的事务规则声明注定成为过去式。
Spring Boot开启事务只需要一个简单的注解@Transactional
,因为@EnableTransactionManagement
在springboot1.4
以后可以不写。框架在初始化的时候已经默认给我们注入了两个事务管理器的Bean(JDBC的DataSourceTransactionManager和JPA的JpaTransactionManager)
,其实这就包含了我们最常用的Mybatis
和Hibeanate
了。当然如果不是AutoConfig
的而是自己自定义的,如果是其它的orm框架
如beetlsql
,则需要自行配置事务管理器。然后使用@EnableTransactionManagement
开启事务管理。Spring Boot自动配置
只需要在方法上加上
@Transactional
注解即可,使用默认配置,抛出异常之后,事务会自动回滚,数据不会插入到数据库。
@Transactional
@Override
public void save() {
//操作A表
throw new RuntimeException("操作A表抛异常了");
//操作B表
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* 当在配置文件中有多个TransactionManager,可以用该属性指定选择哪个事务管理器。
*/
@AliasFor("transactionManager")
String value() default "";
/**
* 当在配置文件中有多个TransactionManager,可以用该属性指定选择哪个事务管理器。
*/
String transactionManager() default "";
/**
* 该属性用于设置事务的传播行为
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* 该属性用于设置事务的超时秒数,默认值为-1表示永不超时
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false
*/
boolean readOnly() default false;
/**
* 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚
* 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
* 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚
* 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")
* 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})
*/
String[] rollbackForClassName() default {};
/**
* 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。
* 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
* 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。
* 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException")
* 指定多个异常类名称:@Transactional(noRollbackForClassName={"RuntimeException","Exception"})
*/
String[] noRollbackForClassName() default {};
}
public enum Propagation {
/**
* 如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**
* 如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/**
* 如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/**
* 重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**
* 以非事务的方式运行,如果当前存在事务,暂停当前的事务
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/**
* 以非事务的方式运行,如果当前存在事务,则抛出异常。
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/**
* 和 Propagation.REQUIRED 效果一样。
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
}
public enum Isolation {
/**
* 使用底层数据库默认的隔离级别
*/
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
/**
* 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
*/
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
/**
* 读取已提交数据(会出现不可重复读和幻读)
*/
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
/**
* 可重复读(会出现幻读)
*/
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
/**
* 串行化
*/
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
}
mysql
,引擎MyISAM
是不支持事务操作的。需要改成InnoDB
才能支持@Transactional
注解的方法必须是public
,否则事务不起作用。private
方法,final
方法和static
方法不能添加事务,加了也不生效@Transactional
@Override
public void save() {
try {
//操作A表
throw new RuntimeException("操作A表抛异常了");
} catch (Exception e) {
e.printStackTrace();
}
//操作B表
}
@Transactional
注解事务管理也是不生效的(因为spring的事务实现原理为AOP,只有通过代理对象调用方法才能被拦截,事务才能生效)@Transactional
@Override
public void save(Person person) {
new Thread(() -> {
saveError(person);//事务不生效
System.out.println(1 / 0);
}).start();
}
@Override
public void add(Person person) {
//一些操作
create(person);
}
@Override
@Transactional
public void create(Person person) {
this.save(person);
int i = 1 / 0;
}
当外部调用add()
方法,因为add()
方法是没有事务的,所以create()
的事务是不会生效的。@Transactional
的事务开启,是基于接口的或者是基于类的代理被创建
@Override
public void add(Person person) {
//一些操作
personService.create(person);
}
@Override
@Transactional
public void create(Person person) {
this.save(person);
int i = 1 / 0;
}
在调用@Transactional
注解了的方法时,Spring Framework
默认使用AOP
代理,在代码运行时生成一个代理对象,根据@Transactional
的属性配置信息,这个代理对象决定该声明@Transactional
了的目标方法是否由拦截器来使用拦截,在TransactionInterceptor
拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager
操作数据源DataSource
提交或回滚事务。
Spring AOP
代理有 CglibAopProxy
和 JdkDynamicAopProxy
两种,以 CglibAopProxy
为例,对于CglibAopProxy
,需要调用其内部类的 DynamicAdvisedInterceptor
的 intercept
方法。对于 JdkDynamicAopProxy
,需要调用其 invoke
方法。