spring-aop解决数据库事务问题
问题描述
- 在操作数据库时有数据一致性的要求,对一个请求可能会同时写入不同表,这就要求数据库操作时需要用到事务,要么所有步骤都成功、要么都失败。
- 如果自己管理事务,一个明显的问题就是要写很多代码判断什么情况回滚、什么情况提交,这样也不能充分利用到spring框架的好处了。
- 在spring框架中,数据库操作默认是自动提交的,因此一次数据库写入就会提交了。当然spring有@transactional注解,加入这个注解后可以让一段代码拥有事务能力。但是该注解有一个缺陷,默认方法中抛出runtime类型异常才会触发回滚,或者需要自己指定异常类型。这意味着上层调用者需要捕获这个异常并进行处理。
- 问题3的延续,如果数据库操作不需要依赖异常进行回滚,而是根据函数返回值进行判定需不需要进行回滚,此时@transactional就不能满足要求了。
解决方法
- 为了少写代码,想方法中自行管理事务显然是不行的,写了几个方法就会叫苦了。采用@transactional的方法又要面对两个问题,如果能解决这两个问题,采用注解的方法是非常漂亮的。因此考虑自定义注解替代@transactional注解。
- 自定义注解@Transation, 对于用该注解注释的方法,如果抛出异常、或者返回值的status非0则进行回滚。
- 引入spring-aop,采用切面的设计方式,完成注解@Transation的功能。切面有多种通知方式,其中around方式可以满足要求。
代码示例
注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 事务注解,通过该注解的方法会被 TransactionAspect 切面代理处理事务。注解的方法返回值应为 {@link Response},例如
* public Response addUser(){
* ...
* }
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transaction {
}
切面类实现
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotati