怎么使用事务 1 2 <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
注意事项 在一个类内部的方法之间相互调用, 不会触发事务, 在类之间相互调用才会触发事务
只要在方法的最外层开始事务, 方法里面的执行体只要使用的是同样的session, 都会在方法的事务里面
eg:
method2不会触发事务
1 2 3 4 5 6 7 8 9 10 11 class A { method1() { method2(); } @Transactional method2(){ } }
method2 会触发事务
1 2 3 4 5 6 7 8 9 10 11 12 13 class A{ B b; method1() { b.method2(); } } class B{ @Transactional method2() {} }
method1, method2 会触发事务, 且method2会使用method1的session, 因为transactional的默认传播属性propagation是REQUIRED
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class A{ B b; @Transactional method1() { b.method2(); } } class B{ @Transactional method2() {} } class C { A a; @Transactional method3() { a.method1(); } }
支付事务使用分析 在掉外部支付接口时 1, 初始化支付状态 2, 调用外部接口 3, 更新本地数据状态
第1, 3步分别保证各自的内部一致性, 需要使用事务 第2步如果失败要记日志, 推动第三步更新状态
嵌套事务的使用 如果被调用方是一个完整的业务B, 自己要记录其开始, 失败, 成功. 不能因为记录失败的时候失败就让该完整的事务回滚. 比如说先掉开始创建, 再调用失败, 记录成功出现异常, 结果导致创建也回滚了, 这种情况是不被允许的. 这个时候应该catch住. 然后记失败. 然后完成整个事务.
这个时候 业务B也不能因为调用方A的失败就失败, 不然B也没有记录了
所以B 应该是Required_new的传播属性, 而且通过返回参数给A返回状态.
综上, 在涉及到事务数据变更的地方, 都要考虑通过返回参数告诉调用方方法的执行情况, 而不是通过异常 同时, 子业务应该启动新的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 // 如果有事务,那么加入事务,没有的话新建一个(不写的情况下) @Transactional(propagation=Propagation.REQUIRED) // 容器不为这个方法开启事务 @Transactional(propagation=Propagation.NOT_SUPPORTED) // 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 @Transactional(propagation=Propagation.REQUIRES_NEW) // 必须在一个已有的事务中执行,否则抛出异常 @Transactional(propagation=Propagation.MANDATORY) // 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) @Transactional(propagation=Propagation.NEVER) // 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务. @Transactional(propagation=Propagation.SUPPORTS)
单个数据实体 最外层不用事务, 成功/失败的unit使用事务
Transaction rolled back because it has been marked as rollback-only 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class A { B b; @Transactional method1 { try { b.method2() } catch(Exception e) { } } } class B { @Transactional method2(){ } } class C { A a; method3 { a.method1() }
在class C中调用a的方法, 如果在B中出现了异常, 再抛出异常, 这时候事务会由A发起的事务会 marked as rollback-only, 这个时候 method1的提交事务的时候就会抛出异常Transaction rolled back because it has been marked as rollback-only. 原因是由于spring的传播属性, b.method2 并没有开启新的事务, 依然在a.method1的事务里面。 在加了Transactional情况下, method2失败, 会标记事务已经回滚. 在method1 commit的时候自然就发现事务已经回滚了, 就会报错
resolve1: 这个时候可以在method2开启子事务, 这样就可以达到想要的目的. 但是要确认下是否能承受A 和 B不在同一个事务里的情况
1 2 @Transactional(propagation=Propagation.REQUIRES_NEW)
resolve2:去掉A中的事务注解, 这个时候A中就没有事务了
resolve3:去掉B中的事务注解, 这个时候B中就没有事务了
子事务引起的可重复读问题(希望不可重复读) 可重复读的时候, 针对同一笔订单, 其他事务已经提交了更新, 但是该事务查询大的订单还是事务提交前的记录http://blog.csdn.net/v123411739/article/details/39298127 http://www.cnblogs.com/zhoujinyi/p/3437475.html
原理 为什么会有以上的生效情况
什么情况下需要用事务呢 ? 读事务 单表事务 多表事务 分布式事务