spring事务失效的原因,spring事务使用场景

  spring事务失效的原因,spring事务使用场景

  一、访问权限Java的访问权限主要有:私有、默认、受保护和公共,它们的权限依次变大。

  如果我们在开发时定义了错误的访问权限,就会在事务中造成问题。

  @服务

  公共类演示服务{

  @事务性

  私有void查询(演示){

  }

  }

  我们可以看一下源代码,可以理解spring transaction的实现在AbstractAllbackTransactionAttribute源类的computeTransactionAttribute方法中有一个判断。如果目标方法不是公共的,则TransactionAttribute返回null,即不支持事务。

  第二,方法是用final修饰的。我们在学习的时候都知道,当一个方法不想被子类继承的时候,就会加上关键字final。这种书写方法通常是好的,但是如果该方法用于事务,它就不起作用。

  @服务

  公共类演示服务{

  @事务性

  公共最终无效查询(演示)

  }

  }这会导致事务失效,因为spring transaction的底层实现使用了proxy,aop,proxy类是通过jdk的动态代理或者cglib生成的,事务功能是在proxy类中实现的。如果方法被final修改,无法重写,则不能添加事务函数。

  正如idea在这里所建议的,类似地,如果方法是静态的,它就不会工作。

  因为静态方法属于类,不是对象,不能被重写,所以不可能实现事务。

  第三,方法的内部调用调用同一个类的服务中的其他事务方法。

  @服务

  公共类演示服务{

  @事务性

  公共void查询(演示){

  保存(演示);

  }

  @事务性

  公共作废保存(演示演示){

  }

  }可以看到,query方法调用save方法。由于spring的事务实现是由于aop生成代理,直接调用这个对象,所以不会生成任何事务。

  解决方法:

  1.添加一个服务,将一个事务的方法移到新添加的服务方法中,然后注入并再次调用它。

  @服务

  公共类DemoTwoService {

  @事务性

  公共作废保存(演示演示){

  }

  }

  @服务

  公共类演示服务{

  @自动连线

  DemoTwoService demoTwoService

  @事务性

  公共void查询(演示){

  demoTwoService.save(演示);

  }

  }2.把自己注入自己的班级。

  @服务

  公共类演示服务{

  @自动连线

  DemoService demoService

  @事务性

  公共void查询(演示){

  demoService.save(演示);

  }

  @事务性

  公共作废保存(演示演示){

  }

  }因为这种基于spring的L3缓存的编写方式不会导致循环依赖的问题。

  3.传递AopContentent

  @服务

  公共类演示服务{

  @事务性

  公共void查询(演示){

  demo service demo service=(demo service)AOP context . current proxy();

  demoService.save(演示);

  }

  @事务性

  公共作废保存(演示演示){

  }

  }四。不受spring管理。使用spring事务时,对象需要由spring管理,也就是需要创建beans。一般我们会添加@Controller、@Service、@Component、@Repository等注释。可以自动实现bean实例化和依赖注入的功能。如果忘记添加,也会导致交易无效。

  动词(verb的缩写)多线程调用@Service

  公共类演示服务{

  @自动连线

  DemoTwoService demoTwoService

  @事务性

  公共void查询(演示){

  新线程(()- {

  demoTwoService.save(演示);

  }).start();

  }

  }从上面的例子我们可以知道,query调用了事务方法save,但是事务方法是在另一个线程中调用的。这会导致两个方法在不同的线程中,获得不同的数据库连接,所以会是两个不同的事务。如果save方法抛出异常,query就不可能回滚。看了spring的源代码,我们可以知道,spring的事务是通过连接数据库来实现的。当前线程保存一个映射、键数据源和值数据库连接。事务实际上指向同一个连接。只有使用相同的数据库连接,才能同时提交和回滚。如果不同线程中的数据库连接不相同,则事务也不相同。

  私有静态最终线程本地映射对象,对象资源=

  新命名的ThreadLocal(“事务性资源”);不及物动词设计的表不支持事务。众所周知,MyISAM是mysql5之前的默认数据库引擎。也许一些老项目还在使用,但它不支持事务。

  七。交易未开始。如果没有创建springboot项目,可能会导致这样的问题,因为springboot项目有自动组装的类DatasourceTransactionManagerOutConfiguration,并且默认情况下已经启动了事务。只需配置spring.datasource参数。如果是spring项目,需要在applicationContext.xml中配置,否则事务不会生效。

  !-配置事务管理器-

  bean id=transactionManager

  属性名= data source ref= data source /属性

  /bean

  tx:建议id=建议事务管理器=事务管理器

  tx:属性

  tx:方法名=* 传播=必需/

  /tx:属性

  /tx:建议

  !-用切入点切入生意-

  aop:配置

  aop:切入点表达式=execution(* com.demo.*。*(.)) id=pointcut/

  AOP:advisor advice-ref= advice pointcut-ref= pointcut /

  /aop:config,而切入点的不匹配也会造成事务失效。

  八。错误事务传播我们可以在使用@Transactional注释时指定传播参数。

  该参数的功能是指定事务的传播特征,

  Spring目前支持七个传播特性:

  如果当前上下文中有事务,则需要加入该事务;如果没有事务,则创建一个事务,这是传播属性的默认值。

  支持如果当前上下文中有事务,则支持该事务加入该事务;如果没有事务,则以非事务方式执行。

  如果当前上下文中有事务,则Mandatory将引发异常。

  Requires _ new每次创建一个新的事务,同时在上下文中挂起该事务。在当前新创建的事务完成之后,上下文事务被恢复,然后被执行。

  Not _ supported如果当前上下文中有事务,则当前事务被挂起,然后在没有事务的环境中执行新方法。

  如果当前上下文中有事务,则从不引发异常,否则在无事务的环境中执行代码。

  嵌套如果当前上下文中有事务,则执行嵌套事务;如果没有事务,则创建一个新事务。

  如果我们在手动设置传播参数时设置了错误的传播属性

  @服务

  公共类演示服务{

  @Transactional(传播=传播。从不)

  公共void查询(演示){

  }

  }我们可以看到query的事务传播设置为propagation。这种类型的传播不支持事务,并且会抛出异常,

  目前,有三种传播特性支持事务:REQUIRED、REQUIRES_NEW和NESTED。

  九。我们捕获了异常事务,并且没有回滚它。也许我们手动尝试了…在我们写代码的时候在代码中捕捉它。

  @事务性

  公共void查询(演示){

  尝试{

  保存(演示);

  } catch(异常e) {

  system . out . println(“exception”);

  }

  }在这种情况下,spring事务将不会回滚,因为我们手动捕获了异常,然后没有手动抛出它。如果我们想要spring事务的正常回滚,我们必须抛出它能处理的异常。如果没有抛出异常,spring会认为程序没有问题。

  X.手动抛出其他异常。我们没有手动捕获异常,但是如果抛出的异常不正确,spring事务将不会回滚。

  @事务性

  公共void查询(演示)引发异常{

  尝试{

  保存(演示);

  } catch(异常e) {

  抛出新的异常(e);

  }

  }以上,我们捕捉到了异常,然后手动抛出异常,事务不会再回滚,因为spring事务默认情况下不会回滚异常(非RuntimeException),只回滚运行时异常(Runtime Exception)和错误(Error)。

  XI。自定义回滚异常在使用@Transactional注释声明事务时,有时我们希望自定义回滚异常,spring也支持。您可以通过设置rollbackFor参数来完成此功能。

  但是如果这个参数的值设置错了,就会导致一些问题。

  @ Transactional(roll back for=business exception . class)

  公共void查询(演示)引发异常{

  保存(演示);

  }以上是我们的自定义业务例外。如果程序在执行上述代码并保存和更新数据时报告错误,就会抛出SqlException和DuplicateKeyException等异常。而BusinessException是我们自定义的异常,上报的异常不属于BusinessException,所以不会回滚事务。即使rollbackFor有默认值,也需要开发者在阿里巴巴的开发者规范中重新指定这个参数。

  因为如果使用默认值,一旦程序抛出异常,事务就无法回滚,会造成很大的bug。因此,一般情况下建议将该参数设置为:Exception或Throwable。

  十二。嵌套事务回滚太多@Service

  公共类演示服务{

  @自动连线

  private DemoTwoService DemoTwoService;

  @自动连线

  私人DemoDao demoDao

  公众的

  @ Transactional(roll back for=exception . class)

  公共void查询(演示)引发异常{

  demoDao.save(演示);

  demoTwoService.save(演示);

  }

  }本来只是想回滚demoServise.save(),而不是demo Dao . save(demo);但在这种情况下两者都将回滚,因为demotowservice . save(demo);没有捕获到异常,它被抛出,导致query回滚,所以两个同时回滚。

  解决方法:

  @服务

  公共类演示服务{

  @自动连线

  private DemoTwoService DemoTwoService;

  @自动连线

  私人DemoDao demoDao

  公众的

  @ Transactional(roll back for=exception . class)

  公共void查询(演示)引发异常{

  demoDao.save(演示);

  尝试{

  demoTwoService.save(演示);

  }最后{

  }

  }

  }你可以把内部嵌套的事务放在try/catch里,不要继续抛出异常。这确保了如果内部嵌套事务中发生异常,只有内部事务将被回滚,而不会影响外部事务。

  十三。编程式事务的事务失效基于@Transactional注释。我们称这种事务为:声明式事务。

  事实上,spring还提供了另一种创建事务的方式,即通过手工编写代码实现的事务,我们称之为程序化事务。

  @服务

  公共类演示服务{

  @自动连线

  私有transaction template transaction template;

  @ Transactional(roll back for=exception . class)

  公共void查询(演示)引发异常{

  transactionTemplate.execute((状态- {

  保存(演示);

  返回布尔值。真实;

  }));

  }

  @事务性

  公共作废保存(演示演示){

  }

  }在spring中,为了支持程序化事务,专门提供了一个类TransactionTemplate。在其执行方法中,实现了事务功能。

  使用TransactionTemplate的编程事务可以

  避免spring aop导致的事务失效问题。

  可以用更小的粒度来控制事务的范围。

  温馨提示:本文中的查询加事务不符合开发的要求,但是有点懒就不改了。

  版权归作者所有:原创作品来自博主、程序员,转载授权请联系作者,否则将追究法律责任。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: