Spring @Transactional 隔离传播

Posted

技术标签:

【中文标题】Spring @Transactional 隔离传播【英文标题】:Spring @Transactional Isolation propagation 【发布时间】:2018-05-14 10:48:29 【问题描述】:

同一事务之间的隔离级别是否可以更改

我有一个用例,我希望使用 SpringDataJpa 的 saveAndFlush 保存的未提交数据在不同的事务中可用,或者 使内部事务提交数据但应该能够在出现任何异常的情况下回滚外部交易

这是需要的,因为我想更新资源并且在锁表中有一个条目以避免并发更新。数据库中的锁表在更新事务完成之前不会更新,因此我想将数据提交到锁表,同时在更新操作过程中出现任何异常时应该回滚。

    具有@Transactional 的Service1 方法将调用Service2 的方法。 Service2 有 @Transactional(isolation=Isolation.READ_UNCOMMITTED) 反过来会调用存储库。

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

这种隔离变化是否反映在从 Service1 传播的同一个事务中?

场景一:

@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>

场景二:

@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>

    让 Thread1 启动 TX1, Thread2 启动 TX2。

如果 TX1 已执行 saveAndFlush 但尚未提交到 DB(因为 TX1 尚未完成),TX2 是否可以访问未提交的数据?

    如果在启动事务后无法更改隔离

有没有一种使用传播或隔离(或任何其他方式)的方法,使用它可以单独提交内部事务,但也可以在外部事务中出现任何异常时回滚?

Service2 方法上的 PROPAGATION_REQUIRES_NEW - 将提交数据,但如果 Service1 中出现任何异常,它不会回滚

Service2 方法上的 PROPAGATION_NESTED - 只有在 Service1 tx 提交时才会提交数据

有什么方法可以实现顶部以粗体突出显示的用例吗?

    我现在尝试的解决方案是在更新时必须处理任何异常,然后手动恢复数据库锁定操作。如果我们需要跟踪许多数据库提交并恢复相同,这将是乏味的。伪代码参考 Scenario2

【问题讨论】:

不,您不能更改正在运行的事务的隔离级别。 @M.Deinum - 感谢您的快速回复,您能否检查我的更新并为我以粗体突出显示的用例提出任何解决方案 一旦线程接触到@Transactional,内部@Transactional 将被完全忽略。 【参考方案1】:

..Propagation Require_New ..中的场景 2 是我使用的。如果在父方法期间出现任何运行时异常,我已经在 try catch 中处理了该异常,并恢复了作为新事务的一部分在数据库中更新的锁,并在 catch 块中抛出了相同的异常,以便父事务得到恢复也是。

如果您有许多 dB 状态需要您单独恢复,这种方法会很困难,但现在就足够了

【讨论】:

以上是关于Spring @Transactional 隔离传播的主要内容,如果未能解决你的问题,请参考以下文章

Spring声明式事务@Transactional 详解,事务隔离级别和传播行为

为啥使用 Spring Data JPA 更新实体时@Transactional 隔离级别不起作用?

事务隔离属性spring传播属性 @Transactional注解

数据库事务中的隔离级别和锁+spring Transactional注解

Spring 之注解事务 @Transactional

Spring 注解@Transactional readOnly=true