一、Spring中如何使用事务
1、编程式事务
@Autowired
private PlatformTransactionManager txManager;
public void addRole(Role role) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
//执行业务代码
//提交事务
txManager.commit(status);
} catch (Exception e) {
//回滚事务
txManager.rollback(status);
}
}
2、声明式事务
@Transactional
public void addRole(Role role) {
//执行业务代码
}
@Transactional
的配置项:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
//定义事务管理器,它是Spring IOC容器里的一个Bean id,这个Bean需要实现接口PlatformTransactionManager
@AliasFor("transactionManager")
String value() default "";
//同value
@AliasFor("value")
String transactionManager() default "";
//传播行为,默认值为Propagation.REQUIRED,当方法调用时,如果不存在当前事务,那么就创建事务;如果之前的方法已经存在事务了,那么就沿用之前的事务
Propagation propagation() default Propagation.REQUIRED;
//隔离级别,默认值为数据库默认隔离级别
Isolation isolation() default Isolation.DEFAULT;
//超时时间,单位为秒,当超时时,会引发异常,默认会导致事务回滚
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//是否开启只读事务,只有当方法产生所定义异常时,才回滚事务,否则就提交事务
boolean readOnly() default false;
//回滚事务的异常类定义
Class<? extends Throwable>[] rollbackFor() default {};
//作用同rollbackFor,只是通过类名定义
String[] rollbackForClassName() default {};
//当产生哪些异常不回滚事务
Class<? extends Throwable>[] noRollbackFor() default {};
//作用同noRollbackFor,只是通过类名定义
String[] noRollbackForClassName() default {};
}
3、事务的传播行为
传播行为是指方法之间的调用事务策略的问题
传播行为 | 含义 | 备注 |
---|---|---|
REQUIRED | 当方法调用时,如果不存在当前事务,那么就创建事务;如果之前当方法已经存在事务了,那么就沿用之前的事务 | Spring默认的传播行为 |
SUPPORTS | 当方法调用时,如果不存在当前事务,那么不启用事务;如果存在当前事务,那么就沿用当前事务 | |
MANDATORY | 方法必须在事务内运行 | 如果不存在当前事务,那么就抛出异常 |
REQUIRES_NEW | 无论是否存在当前事务,方法都会在新的事务中运行 | 事务管理器会打开新的事务运行该方法 |
NOT_SUPPORTED | 不支持事务,如果不存在当前事务也不会创建事务;如果存在当前事务,则挂起它,直至该方法结束后才恢复当前事务 | |
NEVER | 不支持事务,只有在没有事务的环境中才能运行它 | 如果方法存在当前事务,则抛出异常 |
NESTED | 嵌套事务,也就是调用方法如果抛出异常只回滚自己内部执行的SQL,而不回滚主方法的SQL |
二、Spring事务使用注意事项
1、@Transactional自调用失效问题
@Transactional
的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理。这就意味着对于静态方法和非public方法,@Transactional
是失效的。而且自调用也会导致事务失效的问题,若同一类中的其他没有@Transactional
注解的方法内部调用有@Transactional
注解的方法,有@Transactional
注解的方法的事务被忽略,不会发生回滚
2、过长时间占用事务
假设在插入角色后还需要操作一个文件,于是对insertRole方法进行了改造
@Autowired
private RoleMapper roleMapper;
@Autowired
private FileService fileService;
@Transactional
public void addRole(Role role) {
roleMapper.addRole(role);
fileService.doSomethingForFile();
}
当insertRole方法结束后Spring才会释放数据库事务资源,也就是说在运行doSomethingForFile方法时,Spring并没有释放数据库事务资源,而等到doSomethingForFile方法运行完成后,才会关闭数据库资源。当操作文件这步占用较长时间时,数据库事务将长期得不到释放。对此应该在Controller中操作文件
@RestController
@RequestMapping("/api/role")
public class RoleController {
@Autowired
private RoleService roleService;
@Autowired
private FileService fileService;
@PostMapping("/add")
public Role addRole(Role role) {
roleService.addRole(role);
fileService.doSomethingForFile();
return role;
}
}
3、错误捕获异常
@Autowired
private RoleMapper roleMapper;
@Transactional
public void addRole(Role role) {
try {
roleMapper.addRole(role);
} catch (Exception e) {
//记录异常日志
log.error(e.getMessage());
}
}
Spring中事务异常回滚的机制就是靠一层一层向上抛出的异常,直到该异常被抛出到最先调用的地方。如果在异常被抛出的过程中被捕获,那么在抛出过程中影响到的方法产生的独立事务(被调用的方法产生的独立的事务,可以理解为嵌套事务)将会回滚,但是由于异常在往上抛出的过程中被catch住,将不会影响到被catch住异常的方法所在的事务(除非在catch块中throw了一个可以被回滚的异常对象),这也是编码中会影响到事务回滚失效的原因之一
@Autowired
private RoleMapper roleMapper;
@Transactional
public void addRole(Role role) {
try {
roleMapper.addRole(role);
} catch (Exception e) {
//记录异常日志
log.error(e.getMessage());
//自行抛出异常,让Spring事务管理流程获取异常,进行事务管理
throw new RuntimeException(e);
}
}
2)、手动回滚
@Autowired
private RoleMapper roleMapper;
@Transactional
public void addRole(Role role) {
try {
roleMapper.addRole(role);
} catch (Exception e) {
//记录异常日志
log.error(e.getMessage());
//手动调用API回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
三、Spring事务实现原理
在应用系统调用声明@Transactional
的目标方法时,Spring默认使用AOP代理,在代码运行时生成一个代理对象,根据@Transactional
的属性配置信息,这个代理对象决定该声明@Transactional
的目标方法是否由拦截器 TransactionInterceptor来使用拦截,在TransactionInterceptor拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager操作数据源DataSource提交或回滚事务
PlatformTransactionManager接口源码如下:
public interface PlatformTransactionManager {
//获取事务状态
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
//提交事务
void commit(TransactionStatus status) throws TransactionException;
//回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
事务管理的框架是由抽象事务管理器AbstractPlatformTransactionManager来提供的,而具体的底层事务处理实现,由 PlatformTransactionManager的具体实现类来实现。不同的事务管理器管理不同的数据资源DataSource,比如 DataSourceTransactionManager管理JDBC的Connection
参考:
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html