首页 > 编程知识 正文

transactional不生效的场景,springboot手动提交事务

时间:2023-05-05 19:11:57 阅读:150569 作者:3605

注释` @Transactional `的属性参数` propagation `事务的传播机制` isolation '事务的隔离级别公共数据库的缺省隔离级别` readOnly ' 写入性事务的只读概念应用场景` timeout `超时时间` roll back for rollbackforclassname `遇到时回滚` noRollbackFor `和` norolllbacke ` 指定使用的事务管理器` spring `事务无效的原因数据库引擎不支持事务` @Tranang spring `容器的` bean '方法为` public ` 在不是的数据源上未配置事务管理器事务。确保` propagation `传播机制设置错误` catch `语句没有抛出异常抛出的异常类型错误的业务与事务入口相同

注释@Transactional的属性参数属性类型说明valueString选项、事务管理器propagation enum : propagation选项、事务传播行为isolue 指定事务隔离级别readOnlyboolean的读/写或只读事务,缺省读/写time out int (insecondsgranularity )事务时间Throwable导致事务回滚的异常类数组rollbackForClassName类名称数组,Throwable导致事务回滚的异常类名称数组noRollbackForClass对象必须从不会通过Throwable进行事务回滚的异常类数组noRollbackForClassName继承

事务传播机制:如果在开始当前事务之前已经存在事务上下文,则提供了几个选项来指定事务方法的执行行为

枚举Propagation定义了七种传播机制的值

Propagation.REQUIRED :如果当前没有事务,请创建一个新事务,然后将其添加到该事务中(如果已经存在)。 spring的默认传播机制Propagation.SUPPORTS。 保留当前事务,如果当前有事务,则将其作为事务执行。 如果当前没有事务,请将Propagation.MANDATORY作为非事务运行。 必须使用当前事务并在现有事务中执行。 如果当前没有事务,则抛出异常Propagation.REQUIRES_NEW。 无论是否存在事务,都会创建一个新事务。 原始挂起,新事务继续执行旧事务Propagation.NOT_SUPPORTED :作为非事务执行,如果当前有事务,则Propagation.NEVER : 如果没有事务,则抛出异常Propagation.NEVER如果当前没有事务,则执行与Propagation.REQUIRED类似操作的isolation事务的隔离级别。 并发事务之间的隔离级别。 开发时主要涉及的方案有脏引线、重复引线、幻引线

枚举Isolation定义了五个表示隔离级别的值

Isolation.DEFAULT :使用每个数据库的缺省隔离级别。 spring的默认隔离级别。 Isolation.READ_UNCOMMITTED :读取未提交的数据(Isolation.READ_COMMITTED )读取提交的数据(不可重复的读取和可重复读取)发生幻像读取(Isolation.SERIALIZABLE ) )可重复读取)幻像读取EADSQLSERVER :默认为READ_COMMITTEDOracle :默认为设置为true的含义:此方法使用只读操作,而执行其他非读取操作将引发异常

事务的只读概念从此时设置的时间点开始。 时间点a。 在此事务结束之前,其他事务提交的数据将不再可见! 这意味着其他人在时间点a之后提交的数据不会出现在查询中

应用方案如果一次执行一个查询语句,则无需启用事务只读支持。 缺省情况下,数据库支持SQL运行期间的读取一致性。 一次执行多个查询语句,如统计查询、报表查询等。 在此方案中,多个查询SQL需要保证整体读取的一致性。 否则,如果其他用户在上一次SQL查询之后和下一次SQL查询之前修改了数据,则整个统计查询将处于读取数据不匹配的状态。 在这种情况下,必须启用对事务的只读支持。 必须一次运行多次查询以聚集特定信息。 在这种情况下,使用只读事务来确保整个数据的完整性

timeout超时时间用于设置事务的时间长度,以避免长时间的系统阻塞和系统资源消耗

,单位为秒如果超时设置事务回滚,并抛出 TransactionTimedOutException 异常 rollbackFor 和 rollbackForClassName 遇到时回滚 用来指明回滚的条件是哪些异常类或者异常类名spring 默认情况下会对运行期异常 RunTimeException 进行事务回滚,如果遇到 checked 异常就不回滚 noRollbackFor 和 noRollbackForClassName 遇到时不回滚

用来指明不回滚的条件是哪些异常类或者异常类名

value 指定使用的事务管理器 value 主要用来指定不同的事务管理器,主要用来满足在同一个系统中,存在不同的事务管理器的场景需要比如,在 spring 中声明了两种事务管理器 txManager1,txManager2。然后用户可以根据需要,修改这个参数来指定特定的 txManage

存在多个事务管理器的情况:在一个系统中,需要访问多个数据源,则必然会配置多个事务管理器

spring 事务不生效的原因

spring 团队建议在具体的 类或类的方法上 使用 @Transactional 注解,而不要使用在 类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能kddhf设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装

对 spring 来说,@Transactional 注解所标注的方法或类是主体对象,spring 会从二者身上获取合适的事务增强器和事务属性,如果获取不到合适的增强器和事务属性,那么事务就会失效

数据库引擎不支持事务

比如我们常用的 mysql,从 mysql 5.5.5 开始的默认存储引擎是 InnoDB,之前默认的都是 MyISAM,引擎 MyISAM 是不支持事务操作的,需要改成 InnoDB 才能支持。所以这点要值得注意,底层引擎不支持事务再怎么搞都是白搭

@Transactional 所在类非 spring 容器的 bean // @Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { // update order }}

如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 bean,那这个类就不会被 spring 管理了,事务自然就失效了

方法不是 public 的

spring 事务官方文档

Method visibility and @TransactionalWhen you use proxies, you should apply the @Transactional annotation only to methods withpublic 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 theconfigured transactional settings. If you need to annotate non-public methods, consider usingAspectJ (described later).

@Transactional 只能用于 public 的方法上,否则事务会失效;即使方法是 public 的,但是如果被 private 的方法调用,事务同样也会失效

可以查看 spring 事务源码:spring事务源码中的提取事务注解信息

数据源没有配置事务管理器 @Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource);}

当前数据源如果没有配置事务管理器,那事务是不会生效的

事务的 propagation 传播机制设置错误 @Servicepublic class OrderServiceImpl implements OrderService { @Transactional public void update(Order order) { updateOrder(order); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void updateOrder(Order order) { // update order }}

Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起

catch 语句没有抛出异常 @Servicepublic class ClassServiceImpl implements ClassService { @Override @Transactional public void insertClassByException(ClassDo classDo) { classMapper.insertClass(classDo); try { int i = 1 / 0; } catch (Exception e) { e.printStackTrace(); } }}

把异常吃了,然后又不抛出来,事务也不会回滚!

抛出的异常类型错误 @Servicepublic class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { try { // update order } catch { throw new Exception("更新错误"); } }}

@Transactional 默认回滚的是 RuntimeException 和 Error,而 Exception 是 RuntimeException 的父类,事务不生效的

如果你想触发其他异常的回滚,需要在注解上配置一下,如

@Transactional(rollbackFor = Exception.class) 确保业务和事务入口在同一个线程 @Transactional@Overridepublic void save(User user1, User user2) { new Thread(() -> { saveError(user1, user2); System.out.println(1 / 0); }).start();} 自身调用问题 案列一

@Transactional 的事务开启,或者是基于接口的或者是基于类的代理被创建。所以 在同一个类中一个无事务的方法调用另一个有事务的方法,事务是不会起作用的


addInfo()上没有事务注解,spring 获取不到其事务增强器,也就是事务切面不会生效

案列二

事务生效


由于 spring 事务默认的传播机制是 Propagation.REQUIRED,create() 方法的事务会加入到 addInfo() 方法的事务之中;而所在的类是可以产生代理对象的

案列三

事务生效


由于 spring 事务默认的传播机制是 Propagation.REQUIRED,create() 方法的事务会加入到 addInfo() 方法的事务之中;而所在的类是可以产生代理对象的

案列四

事务生效

案列五

事务生效


这里虽然是方法内部调用,但是事务切入了 addInfo() 方法,所以即使内部抛出异常,也是可以生效的

案列六

事务不生效

案列七

事务生效


这是我们解决方法内部调用事务不生效的最常用方法之一:这个类中维护一个注入自己的 bean,然后使用这个 bean 来调用方法,如下所示

@Autowiredprivate PeriodService periodService

针对这个案列的详细分析,可参考:https://blog.csdn.net/qq_33589510/article/details/120387044

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。