Spring @Transactional 隔离传播策略

3

是否可以在同一事务中更改隔离级别?

我有一个用例,希望使用SpringDataJpa的saveAndFlush持久化的未提交数据可以在不同的事务中使用,或者使内部事务提交数据,但在外部事务发生任何异常时应该能够回滚。

这是必需的,因为我想要更新资源,并在锁定表中具有条目以避免并发更新。数据库中的锁表在更新事务完成之前没有得到更新,因此我想要将数据提交到锁表,并在更新操作期间出现任何异常的情况下回滚。

  1. Service1方法具有@Transactional注解,将调用Service2的方法。 Service2具有@Transactional(isolation = Isolation.READ_UNCOMMITTED),然后会调用存储库。

Service2的READ_UNCOMMITED隔离级别会优先还是默认隔离级别?

更改隔离级别是否会反映在从Service1传播的同一事务中?

场景1:

@Service
class Service1{
@Autowired
Service2 service2;
@Transactional
public void method1(){
Foo foo=new Foo();
foo.setId("f123");
service2.saveValue(foo);
}
}

@Service
@Transactional(isolation=Isolation.READ_UNCOMMITTED)
class Service2{
@Autowired
FooRepository fooRepository;

public void saveValue(Foo value){
fooRepository.saveAndFlush(value);
}
}

public interface FooRepository extends JpaRepository<Foo, String>{
}

场景2:

@Service
class Service1{
@Autowired
Service2 service2;

@Transactional
public void method1(){

Foo foo=new Foo();
foo.setId("f123");
service2.saveValue(foo);

try{
updateOperation()
}catch(Throwable e){   // does Spring @Transactional revert on ERRORS, by default it rollback on RuntimeException and Exception(in case we explicitly mention)?
  service2.deleteByFooId(foo.getId());
  throw e;
}


}

private void updateOperation(){
 /* update logic for resource */- Not a DB update 

}



@Service
@Transactional(propagation=Propagation.REQUIRES_NEW)
class Service2{
@Autowired
FooRepository fooRepository;

public void saveValue(Foo value){
fooRepository.saveAndFlush(value);
}

public void delete(String id){
     deleteByFooId(id);
}
}

public interface FooRepository extends JpaRepository<Foo, String>{
}
  1. 让Thread1启动TX1,Thread2启动TX2。

如果TX1已经执行了saveAndFlush但尚未提交到数据库(因为TX1尚未完成),那么TX2能够访问未提交的数据吗?

  1. 一旦事务启动,隔离级别就无法更改。

是否有一种使用Propagation或Isolation(或其他任何方式)的方法,使内部事务可以单独提交,但在外部事务发生任何异常的情况下也可以回滚?

Service2方法上使用PROPAGATION_REQUIRES_NEW - 将提交数据,但在Service1中出现任何异常时不会回滚

Service2方法上使用PROPAGATION_NESTED - 仅在Service1 tx提交时才会提交数据

是否有任何方法可以实现上述加粗的用例?

  1. 我现在尝试的解决方案是手动处理更新操作中的任何异常,然后手动回滚DB锁操作。如果我们需要跟踪多个DB提交并撤消它们,这将变得很繁琐。有关伪代码,请参见Scenario2。

不,您不能更改正在运行的事务的隔离级别。 - M. Deinum
@M.Deinum - 感谢您的快速回复,您能否检查我的更新并针对我在粗体中突出显示的用例提出任何解决方案? - Raghav
一旦一个线程触及到 @Transactional,内部的 @Transactional 将被完全忽略。 - Grim
1个回答

0

我使用了场景2中的Propagation Require_New。如果在父方法运行时出现任何运行时异常,我已经在try catch中处理了该异常,并恢复了在新事务中更新的锁并在catch块中抛出相同的异常,以便父事务也被恢复。

如果您需要单独还原许多dB状态,则此方法可能会更加困难,但目前足够。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接