首页 > 其他分享 >spring 的相关的事务失效的问题总结

spring 的相关的事务失效的问题总结

时间:2022-10-09 10:03:55浏览次数:61  
标签:事务 spring Transactional entity new catch 失效


  这一个问题,真的是很有意思。之前不了解的时候觉得会用就行了。但是真的不知道里边这么多坑。不过这些坑都真的很有意思。它推着你去了解底层。推着你看源码,了解实现原理。

  也是因为我在最近的开发中遇到了一些问题,自己进行排查,然后谷歌了很多很多的文章。这篇文章算是一个总结吧。希望可以做到的是,关于事务失效的问题看这一篇文章就够了。

  尽可能的做到全一点,篇幅可能会大一点。这里为了省事,一些demo就不自己敲了,借鉴一下比人的。

  ~ps 如果你是因为事务失效问题,看到这篇文章没有解决的,亲请给我留言,我可能会对你失效的原因很感兴趣,我们一块探讨探讨,然后争取解决了放在这里。

 

 

# # 事务失效分类、

  首先事务失效的原因有这几个层面:

  • 代码层面
  • 数据库层面
  • 框架层面

 

# #  代码层面(这个也是有很多个原因)

   这里先列一下,然后再逐个展开:

  • 没有加@Transactional注解
  • 因为try catch原因
  • 因为没加@Service,没有被 spring 管理
  • 因为方法不是public d
  • 因为事务的传播行为导致事务失效。

 

  • 比方说我们没有加 @Transactional注解,注意这个注解,就是加在 service 层的,不要乱加,再注意一点,如果能确定加在方法上就不要加在类上。因为根据底层的实现。你给加在类上,就意味着给全部的方法都加事务,再根据底层的实现,事务是用 AOP 实现的,动态代码要损失一点性能。

         解决方案:注意这是 service 层的方法,之前不懂事,我也加在 dao层过。

           

spring 的相关的事务失效的问题总结_回滚

 

  • ry catch 吞掉了异常,接下来看一个例子。这个例子是事务失效的例子

   注意这里的例子,我持久层用的是 jpa,这里的 save 就相当于是一个insert操作、

@Transactional
public void transactionalTest() {
try {
TmcFinanceServiceFeeCheckLogEntity entity = new TmcFinanceServiceFeeCheckLogEntity();
entity.setCompanyNo("555666");
entity.setDefineNo("6666666");
entity.setServiceType("03");
entity.setServiceTypeDetail("031");
entity.setServiceNumTotal(1);
entity.setTotalNo("2222");
entity.setStatisticsTimeStart(new Timestamp(System.currentTimeMillis()));
entity.setStatisticsTimeStop(new Timestamp(System.currentTimeMillis()));
entity.setCheckType("1");
entity.setCheckStatus("2");
entity.setStartTime(new Timestamp(System.currentTimeMillis()));
entity.setStatisticsInsideDate("20200405");
tmcFinanceServiceFeeCheckLogRepository.save(entity);
int temp = 1/0;
}catch (Exception e){
e.printStackTrace();
}
}

 

    有的人,特别热衷于 try catch ,像例子里边的 try catch 就是非常多余的。我们程序应该是配置全局异常。而不是在这里手动的去补货异常。甚至,这个try catch 起到了画蛇添足的作用。因为它把异常吞掉了,而这个加了事务注解的方法就看不到异常了。spring 的事务 @Transactional不做任何配置 默认是对抛出的unchecked异常回滚,checked异常不会回滚。显然,这里你 catch,已经是检查异常了,既然你 catch,那你就自己做处理好了。我事务不管了。所以  try catch会让事务失效。

  解决方案:try catch 在这里是多余的,仅仅需要去掉就可以了。就是能不用则不用。 那如果遇到了一定要用的情况怎么办?那就是自己杀的自己埋。既然你都已经捕获了,就自己处理。在 catch里边 自己进行事务的回滚。代码如下:注意看 catch里边的就可以了,这是手动回滚事务的操作。

@Transactional
public void transactionalTest() {
try {
TmcFinanceServiceFeeCheckLogEntity entity = new TmcFinanceServiceFeeCheckLogEntity();
entity.setCompanyNo("555666");
entity.setDefineNo("6666666");
entity.setServiceType("03");
entity.setServiceTypeDetail("031");
entity.setServiceNumTotal(1);
entity.setTotalNo("2222");
entity.setStatisticsTimeStart(new Timestamp(System.currentTimeMillis()));
entity.setStatisticsTimeStop(new Timestamp(System.currentTimeMillis()));
entity.setCheckType("1");
entity.setCheckStatus("2");
entity.setStartTime(new Timestamp(System.currentTimeMillis()));
entity.setStatisticsInsideDate("20200405");
tmcFinanceServiceFeeCheckLogRepository.save(entity);
int temp = 1/0;
}catch (Exception e){
// 重点看这里
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();
}
}

  

  • 没有加 @service

  例子如下:

// @Service
public class OrderServiceImpl implements OrderService {

@Transactional
public void updateOrder(Order order) {
// update order
}

}

   这个问题,说白了,事务是由spring帮我们实现的,那么想要使用,就必须把类放在spring 容器里边来管理。如果你的类没有加 @service 这就相当于没有买人家的保险,还想让人家赔钱,这是不可能的。 

 

  • 因为方法不是public 的

关于@Transactional的说明,Spring 有一段描述关于方法可见性:

Method visibility and @Transactional
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

@Transactional注解只对代理类时的public方法有效,被protected、private、package-visible修饰的方法使用@Transactional注解无效,对这类方法使用事务注解,推荐使用AspectJ进行事务管理。Spring框架虽然提升了效率,偶尔也会产生意外的问题,且行且研究。

 

  • 因为事务的传播行为导致事务失效。

关于这一点Spring Transactional官方说明如下:

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.

在代理下(默认或当配置为proxy-target-class="true"),只有当前代理类的外部方法调用注解方法时代理才会被拦截。事实上,这意味着:一个目标对象的方法调用该目标对象的另外一个方法,即使被调用的方法已使用了@Transactional注解标记,事务也不会有效执行。

 

  看一个因为事务传播原因导致的事务失效的例子:

  描述transactionalTest ()方法调用了 methodTest (),methodTest 已经加了事务注解。我们想当然的以为。调用的方法加了事务,里边出现了错误应该会发生回滚的。 而实际上不会回滚。

@Override
public void transactionalTest() {
//调用一个加了事务注解,但是会在运行过程中报错
methodTest();


}
@Transactional
public void methodTest(){
// 故意写一个异常,看会不会回滚
TmcFinanceServiceFeeCheckLogEntity entity = new TmcFinanceServiceFeeCheckLogEntity();
entity.setCompanyNo("666666");
entity.setDefineNo("6666666");
entity.setServiceType("03");
entity.setServiceTypeDetail("031");
entity.setServiceNumTotal(1);
entity.setTotalNo("2222");
entity.setStatisticsTimeStart(new Timestamp(System.currentTimeMillis()));
entity.setStatisticsTimeStop(new Timestamp(System.currentTimeMillis()));
entity.setCheckType("1");
entity.setCheckStatus("2");
entity.setStartTime(new Timestamp(System.currentTimeMillis()));
entity.setStatisticsInsideDate("20200405");
tmcFinanceServiceFeeCheckLogRepository.save(entity);
int temp = 1/0;

}

 

 对于这个失效的处理方式就是,在调用的方法上也添加一个@Transactional ,也就是调用者之前没有事务,而被调用者有事务,没有事务的调用者调用有事务的被调用者,会发生事务失效。 

  这问题,我看别人的文章说,两个地方都添加事务注解,下边的被调用的方法的事务,仍然会失效,但是从我的测试用例来看,并没有失效。这里大家遇到可以咱们沟通一下,看看是不是因为事务管理器的差异造成的。

 

# # 数据库层面的原因

  这个比较好排查,只需要知道自己使用的数据库(引擎)是否支持事务。举个例子:MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。

  使用其他的数据库的自查。

 

# # 框架层面的

  这个是我遇到的问题,就是上边的情况,都不是我遇到的。

标签:事务,spring,Transactional,entity,new,catch,失效
From: https://blog.51cto.com/u_15812686/5739753

相关文章

  • spring boot项目使用mybatis-plus代码生成实例
    前言mybatis-plus官方地址https://baomidou.commybatis-plus是mybatis的增强,不对mybatis做任何改变,涵盖了代码生成,自定义ID生成器,快速实现CRUD,自动分页,逻辑删除等功......
  • SpringFramework笔记
    代码下载地址: https://gitee.com/chengylaoshi/spring-framework.git Spring1.什么是框架Thymeleaf就是一个环境(框架!)框架在项目中的具体表现就是一个jar包!每一种框......
  • Spring、springboot、springMVC、JPA、SpringData、springCloud的概述
    https://blog.csdn.net/qq_51308214/article/details/125165747Spring框架是Java平台上的一种开源应用框架, Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了......
  • SpringBoot框架中的DAO层、Entity层、Service层、Controller层
    https://blog.csdn.net/qq_40963664/article/details/123900763一般的项目模块中都有DAO、Entity、Service、Controller层。Entity层:实体层数据库在项目中的类DAO层:......
  • SpringBoot整合fastdfs
    1、背景在前一节中,我们搭建了一个单机版的fastdfs服务,此处我们将fastdfs与springboot进行整合,实现文件的上传和下载。2、整合步骤2.1、引入依赖<dependency><group......
  • SpringBoot整合fastdfs
    目录1、背景2、整合步骤2.1、引入依赖2.2、引入fastdfs配置2.3编写文件上传和下载接口2.4测试文件上传2.5文件下载3、参考文档1、背景在前一节中,我们搭建了一个单机版......
  • day08-MySQL事务
    MySQL事务先来看一个例子有一张balance表:需求:将tom的100块钱转到King账户中执行的操作是:updatebalancesetmoney=money-100whereid=100updatebalanc......
  • Spring事务管理
    Spring事务管理1.1、什么是事务(Transaction)是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,sql能将逻辑相关......
  • Spring Boot的价值
    从SpringXML到JavaConfig实现无配置化注入,虽然在Bean的装载方式在形式上发生了变化,但是本质问题仍然没有解决IthinkthatSpring'swebapplicationarchitecturecanb......
  • SpringBoot整合QQ邮箱发送
    邮件发送的基本过程与概念邮件服务器:类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中电子邮箱:用户在邮件服务器上申请的......