有人可以通过实际示例解释注释中的 隔离 和 传播 参数吗?@Transactional
@Transactional
基本上什么时候以及为什么我应该选择更改它们的默认值。
好问题,虽然不是一个微不足道的问题。
api/org/springframework/transaction/annotation/Propagation.html)
定义事务如何相互关联。常用选项:
REQUIRED
REQUIRES_NEW
的默认值为,这通常是您想要的@Transactional。REQUIRED
api/org/springframework/transaction/TransactionDefinition.html)
定义事务之间的数据契约。
ISOLATION_READ_UNCOMMITTED
ISOLATION_READ_COMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
不同级别在多线程应用程序中具有不同的性能特征。我认为,如果您了解 脏读 概念,您将能够选择一个不错的选择。
默认值可能因不同的数据库而异。例如,对于MariaDB,它是REPEATABLE READ.
REPEATABLE READ
可能发生脏读的示例:
thread 1 thread 2 | | write(x) | | | | read(x) | | rollback | v v value (x) is now dirty (incorrect)
因此,合理的默认值(如果可以声明的话)可能是ISOLATION_READ_COMMITTED,它只允许您读取已经由其他正在运行的事务提交的值,并结合传播级别REQUIRED. 然后,如果您的应用程序有其他需求,您可以从那里开始工作。
一个在进入例程时总是创建新事务provideService并在离开时完成的实际示例:
provideService
public class FooService { private Repository repo1; private Repository repo2; @Transactional(propagation=Propagation.REQUIRES_NEW) public void provideService() { repo1.retrieveFoo(); repo2.retrieveFoo(); } }
如果我们改为使用REQUIRED,如果在进入例程时事务已经打开,则该事务将保持打开状态。另请注意, a 的结果rollback可能不同,因为多个执行可能参与同一事务。
rollback
我们可以通过测试轻松验证行为,并查看结果与传播级别有何不同:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:/fooService.xml") public class FooServiceTests { private @Autowired TransactionManager transactionManager; private @Autowired FooService fooService; @Test public void testProvideService() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); fooService.provideService(); transactionManager.rollback(status); // assert repository values are unchanged ... }
传播水平为
REQUIRES_NEW:我们预计 不会 回滚,因为它创建了自己的子交易fooService.provideService()。 __
fooService.provideService()
REQUIRED:我们希望一切都回滚并且后备存储没有改变。