下面就来详细讲解“Spring中12种@Transactional的失效场景(小结)”。
首先,需要明确的是,@Transactional是用来控制事务的注解,它可以应用于方法、类或接口上,用来确保在执行该方法时开启了一个事务,并在方法结束时提交或回滚事务。但是,在某些情况下,@Transactional注解可能会失效。下面分别讲解12种@Transactional的失效场景:
- 在同一个类中调用@Transactional方法
在同一个类中直接调用被@Transactional注解的方法时,事务不会起作用。这是因为@Transactional是通过动态代理去实现的,而如果在同一个类中调用,动态代理并不会被触发。所以,我们需要通过自我注入的方式调用需要使用事务的方法,这样动态代理就会生效。示例代码如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserService userService;
@Transactional
public void addUserAndLog() {
userService.addUser();
// 记录日志
}
@Transactional
public void addUser() {
// 添加用户
}
}
- 在非public的方法上使用@Transactional
在许多情况下,我们可能会在一个类中定义私有的或受保护的方法。然而,如果我们在这些方法上使用@Transactional注解,那么事务也会失效。因为,Spring只会拦截public方法,而不会拦截非public方法。示例代码如下:
public class UserServiceImpl implements UserService {
@Transactional
public void addUser() {
// 添加用户
addUserLog();
}
@Transactional
private void addUserLog() {
// 添加用户日志
}
}
- 在非事务方法中调用@Transactional方法
如果一个方法没有被@Transactional注解标记,而它又调用了一个被@Transactional注解标记的方法,那么被调用的方法的事务将不会开启。这是因为,被调用的方法已经失去了@Transactional注解的作用。示例代码如下:
@Service
public class UserServiceImpl implements UserService {
@Transactional
public void addUser() {
// 添加用户
addUserLog();
}
@Transactional
public void addUserLog() {
// 添加用户日志
}
public void addUserAndLog() {
addUser();
}
}
- 在静态方法上使用@Transactional
由于@Transactional注解是作用在实例方法上的,所以它不能用于静态方法中,否则会失效。示例代码如下:
@Service
public class UserServiceImpl implements UserService {
@Transactional
public void addUser() {
User user = User.builder().username("test").password("123456").build();
User.save(user);
}
@Transactional
public static void save(User user) {
// 保存用户
}
}
- @Transactional标注方法的异常被try/catch
如果我们在@Transactional标注的方法中使用try/catch捕获了异常,那么就会导致事务无法回滚。因为一旦我们捕获了异常,它就不会再被传递到Spring事务管理器中,事务管理器就无法识别到异常并进行回滚。示例代码如下:
@Transactional
public void addUser() {
try {
// 添加用户
} catch (Exception e) {
// 异常捕获
}
}
- @Transactional标注方法的异常类为RuntimeException
如果@Transactional标注的方法中抛出了RuntimeException及其子类的异常,默认情况下事务会回滚,可以通过在异常类上使用@Transaction注解的noRollbackFor属性,来使得事务不回滚。示例代码如下:
@Transactional
public void addUser() {
throw new RuntimeException("测试异常");
}
@Transactional(noRollbackFor = RuntimeException.class)
public void addUser() {
throw new RuntimeException("测试异常");
}
- @Transactional标注方法和catch块中都抛出异常
如果@Transactional标注的方法中发生了异常,并在catch块中继续抛出了异常,那么事务也会失效。因为catch块中的异常会覆盖原来产生的异常。示例代码如下:
@Transactional
public void addUser() {
try {
// 添加用户
} catch (Exception e) {
// 处理异常
throw new RuntimeException("处理异常时出错");
}
}
- @Transactional标注方法所在的类没有被Spring容器管理
@Transactional注解只有在Spring容器管辖的类中才会生效,所以如果@Transactional注解标注在一个未被Spring容器管理的类中,那么它将不起作用。示例代码如下:
public class UserServiceImpl implements UserService {
@Transactional
public void addUser() {
// 添加用户
}
}
- @Transactional标注在类级别上中间加入了非@Transactional的方法,造成事务失效
如果在一个类中,@Transactional注解是针对类级别的,在类中间加了一个没有@Transactional注解的方法,这个非@Transactional方法就会出现事务失效。这是因为,Spring在检测到事务注解时,会动态创建一个代理类进行事务处理,而在代理类中,会通过在方法前后添加事务通知来管理事务。但是,如果我们在被代理的类中间插入了一个方法,Spring就无法保证在代理类中添加事务通知的顺序和正确性了。示例代码如下:
@Service
@Transactional
public class UserServiceImpl implements UserService {
public void test() {
// 测试方法
}
public void addUser() {
// 添加用户
}
}
- 使用自调用的方式调用@Transactional方法
如果调用@Transactional方法时使用了自调用的方式,事务也会失效。这是因为自调用会绕过Spring的动态代理。示例代码如下:
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserService userService;
public void addUserAndLog() {
addUser();
this.addUserLog();
}
@Transactional
public void addUser() {
// 添加用户
}
@Transactional
public void addUserLog() {
// 添加用户日志
}
}
- 同一个方法内,同时调用多个@Transactional注解的方法,传播属性设置不当
如果在同一个方法内,同时调用多个@Transactional注解的方法,并在这些方法之间使用事务传播属性时,需要特别注意传播属性的设置,否则可能会出现一系列问题,比如事务无法回滚、出现死锁等。示例代码如下:
@Transactional
public void addUser() {
try {
// 添加用户
addUserLog1();
addUserLog2();
} catch (Exception e) {
// 异常处理
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserLog1() {
// 添加用户日志1
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserLog2() {
// 添加用户日志2
}
- 在子线程中调用@Transactional标注的方法
在子线程中调用@Transactional标注的方法是不会生效的,因为Spring的事务管理是基于ThreadLocal来实现的,所以在子线程中,Spring的事务管理器无法将事务的上下文传递到新的线程中。所以,如果需要在子线程中使用事务,可以使用TransactionTemplate或者手动同步事务。示例代码如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private TransactionTemplate transactionTemplate;
@Transactional
public void addUser() {
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
// 在子线程中使用事务
return null;
}
});
}
}
以上就是“Spring中12种@Transactional的失效场景(小结)”的详细攻略。希望对大家有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring中12种@Transactional的失效场景(小结) - Python技术站