背景简单介绍
在Spring开发中,为了使得业务的顺利开发,我们免不了需要使用事务,而在使用时,应该注意下面这一点
内容讲解
在Spring框架中,我们不能通过非事务方法去调用事务方法
代码演示
/**
* 这个类中进行测试方法的调用
*/
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public void testTransactionMethod() {
try {
transactionMethod();
} catch (Exception e) {
throw new RuntimeException();
}
}
@Override
public void addUser() {
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = RuntimeException.class)
public void transactionMethod() {
try {
userMapper.insert(new User());
} catch (Exception e) {
throw new RuntimeException();
}
}
}
编写一个测试类,进行我们定义方法的测试
public class UserServiceImplTest {
@Test
void testTransactionMethod() {
UserServiceImpl userService = new UserServiceImpl();
userService.testTransactionMethod();
}
}
代码执行结果
java.lang.RuntimeException
at com.service.impl.UserServiceImpl.testTransactionMethod(UserServiceImpl.java:20)
at com.service.impl.UserServiceImplTest.testTransactionMethod(UserServiceImplTest.java:12)
那么可以发现,我们的方法调用报错了,那么接下类我们就来分析下这到底是咋回事?
原理说明
在Spring中,我们对于事务传播的控制在TransactionDefinition这个类中
int PROPAGATION_REQUIRED = 0; // 支持当前事务,如果有就加入当前事务中;如果当前方法没有事务,就新建一个事务
int PROPAGATION_SUPPORTS = 1; // 持当前事务,如果有就加入当前事务中;如果当前方法没有事务,就以非事务的方式执行;
int PROPAGATION_MANDATORY = 2; // 支持当前事务,如果有就加入当前事务中;如果当前没有事务,就抛出异常;
int PROPAGATION_REQUIRES_NEW = 3; // 新建事务,如果当前存在事务,就把当前事务挂起;如果当前方法没有事务,就新建事务;
int PROPAGATION_NOT_SUPPORTED = 4; // 以非事务方式执行,如果当前方法存在事务就挂起当前事务;如果当前方法不存在事务,就以非事务方式执行;
int PROPAGATION_NEVER = 5; // 以非事务方式执行,如果当前方法存在事务就抛出异常;如果当前方法不存在事务,就以非事务方式执行;
int PROPAGATION_NESTED = 6; // 如果当前方法有事务,则在嵌套事务内执行;如果当前方法没有事务,则与required操作类似;
// 下面是Spring事务对应的几种隔离级别,具体可以参考Mysql中的隔离级别
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
default int getPropagationBehavior() {
return 0;
}
default int getIsolationLevel() {
return -1;
}
default int getTimeout() {
return -1;
}
default boolean isReadOnly() {
return false;
}
- 我们知道在Spring框架中,是基于xml配置文件进行各种技术的实现的,事务也不例外,在事务配置文件中,我们通过
<tx:annotation-driven
为开头的配置来开启事务的相关定义。而该标签在Spring中,会通过Spring中的AnnotationDrivenBeanDefinitionParser类的parse()方法进行解析,具体实现解析过程,大家可以进行源码阅读学习,这里不再说明。- 而对于Spring的Bean加载中,Spring会进行检测加载的该bean中方法上是否有@Transaction注解,如果有的话,Spring会给该bean创建一个子类代理类,当别处进行调用该bean下的注解方法时,Spring默认会使用该代理类进行方法调用,而在该bean中的方法进行注解方法内部调用时,就不会产生代理类,也就会出现注解失效的问题。
注解失效了,怎么办???
解决方案
一、将事务方法专门抽取出来放到一个类中进行统一管理;
二、获取本对象的代理对象,再通过该代理对象进行方法调用,使用该功能,需要在配置文件中开启一下注解<aop:aspectj-autoproxy expose-proxy="true"/>