聊聊spring @Transactional 事务无法使用的可能原因

 更新时间:2021年07月17日 11:50:04   作者:居十四  
这篇文章主要介绍了spring @Transactional 事务无法使用的可能原因,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

spring transaction

建议

Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,

而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,

但是这将只能当你设置了基于接口的代理时它才生效。

因为注解是不能继承的,

这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,

而且对象也将不会被事务代理所包装(将被确认为严重的)。

因此请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

事务无法使用的可能原因

导入spring的事务注解

应该是

org.springframework.transaction.annotation.Transactional

而不是

javax.transaction.Transactional

是否开启了对注解的解析:

xml 文件配置

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

springboot

注解开启自动扫描

@EnableTransactionManagement

spring是否扫描到你使用注解事务的这个类所在的包

配置xml

<context:component-scan base-package="com.xxx.xxx" ></context:component-scan>

springboot 开启事务

@EnableTransactionManagement

数据库引擎要支持事务

如果是mysql,注意表要使用支持事务的引擎,比如InnoDB,如果是myisam,事务是不起作用的

springboot的配置

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

检查方法是不是public的

@Transactional 仅仅在 public 方法,才能进行事务管理。

photo_1

这是因为在使用 Spring AOP 代理时,

Spring 在调用在图中的 TransactionInterceptor 在目标方法执行前后进行拦截之前(图中是cglib代理)

DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource ,而会去调用computeTransactionAttribute 方法。

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
   return null;
  }
 }

这个方法会判断如果不是 public 则会返回 null

异常类型是不是unchecked异常

默认,只有unchecked异常时才回滚该事务

spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,

也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚).

而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,

包括checked异常。也可以明确定义那些异常抛出时不回滚事务。

如果想让checked异常也回滚,在注解上面写明异常类型即可:

@Transactional (rollbackFor=Exception.class)

noRollbackFor 自定义不回滚的异常

异常是不是被catch住了

Service层捕捉异常后,发现事务不生效。

Service层手工捕捉并处理了异常(try…catch)等于把异常吃掉了,

Spring自然不知道这里有错,更不会主动去回滚数据。推荐做法是在Service层统一抛出异常,

然后在Controll层统一处理。

下面代码事务是无法生效的

//在类上@Transactional 说明,所以public都是有事务的
@Service
@Transactional
public class StudentService {

	@Autowired
	private GroupRepository groupRepository;
	@Autowired
	private InstituteRepository instituteRepository;

	public void initStudent() {
		Institute institute = Institute.builder().build();
		institute.setCode("TEST4");
		instituteRepository.save(institute);
		
		// 这里自己处理异常,spring不会知道存在异常,无法进行事务回滚
		try {
			throw new RuntimeException("运行时异常----------看事务是否起作用");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

修改成如下代码

public void initStudent() throws Exception{
  Institute institute = Institute.builder().build();
  institute.setCode("TEST4");
  instituteRepository.save(institute);
  groupRepository.save(group);
  
  //不进行异常处理,而是把异常抛出
  throw new RuntimeException("运行时异常----------看事务是否起作用");
 }

避免 Spring 的 AOP 的自调用问题

检查是不是同一个类中的方法调用(如a方法调用同一个类中的b方法),从而避免 Spring AOP 的自调用问题

这是因为在 Spring AOP 代理下,只有目标方法由外部调用,

目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。

若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,

@Transactional 注解的方法的事务被忽略,不会发生回滚。

@Service
public class StudentService {

 @Autowired
 private GroupRepository groupRepository;
 @Autowired
 private InstituteRepository instituteRepository;
 
 //initStudent() 加上@Transactional(),则会回滚
 public void initStudent() throws Exception{
  Institute institute = Institute.builder().build();
  institute.setCode("TEST4");
  instituteRepository.save(institute);
  
  //虽然 initGroup() 有 @Transactional() 但是事务还是没起作用
  initGroup();
  throw new RuntimeException("运行时异常----------看事务是否起作用");
 }
 
 @Transactional()
 public void initGroup() {
  Group group = Group.builder().academic_year(2015).build();
  group.setCode("ELSE1");
  groupRepository.save(group);
 }
}

AspectJ 取代 Spring AOP 代理

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot之使用Redis实现分布式锁(秒杀系统)

    SpringBoot之使用Redis实现分布式锁(秒杀系统)

    这篇文章主要介绍了SpringBoot之使用Redis实现分布式锁(秒杀系统),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • IDEA解决Java:程序包xxxx不存在的问题

    IDEA解决Java:程序包xxxx不存在的问题

    这篇文章主要介绍了IDEA解决Java:程序包xxxx不存在的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • SpringMVC通过注解获得参数的实例

    SpringMVC通过注解获得参数的实例

    下面小编就为大家带来一篇SpringMVC通过注解获得参数的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • SpringBoot实现国际化的教程

    SpringBoot实现国际化的教程

    这篇文章主要介绍了SpringBoot实现国际化的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • SpringBoot整合Shiro实现权限控制的代码实现

    SpringBoot整合Shiro实现权限控制的代码实现

    Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理,今天通过本文给大家介绍SpringBoot整合Shiro实现权限控制的方法,感兴趣的朋友一起看看吧
    2021-07-07
  • SpringMVC一步到位精通拦截器

    SpringMVC一步到位精通拦截器

    拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。本文将详细讲讲SpringMVC中拦截器的概念及入门案例,感兴趣的可以尝试一下
    2022-12-12
  • java9中gc log参数迁移

    java9中gc log参数迁移

    本篇文章给大家详细讲述了java9中gc log参数迁移的相关知识点,对此有需要的朋友可以参考学习下。
    2018-03-03
  • mybatis-plus版本不兼容问题的解决

    mybatis-plus版本不兼容问题的解决

    本文主要介绍了mybatis-plus与spring-boot3版本不兼容导致的BeanDefinitionStoreException问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • 阿里的一道Java并发面试题详解

    阿里的一道Java并发面试题详解

    这篇文章主要介绍了阿里的一道Java并发面试题详解,网络、并发相关的知识,相对其他一些编程知识点更难一些,主要是不好调试并且涉及内容太多 !,需要的朋友可以参考下
    2019-06-06
  • Java的位图和布隆过滤器深入详细讲解

    Java的位图和布隆过滤器深入详细讲解

    这篇文章主要介绍了Java的位图和布隆过滤器,在学习之前的数据结构的时候,我们使用的数据量都不是很大,但是在生活中,我们常常面临着要处理大量数据结果并得出最终结果,那么有没有什么数据结构可以帮助我们实现这样的功能呢,想要继续了解的朋友可以参考下
    2024-10-10

最新评论