有人可以通过真实示例解释 @Transactional
注释中的 isolation 和 propagation 参数是什么吗?
基本上什么时候以及为什么我应该选择更改它们的默认值。
好问题,虽然不是一个容易回答的问题。
定义事务如何相互关联。常用选项:
REQUIRED
:代码将始终在事务中运行。创建一个新事务或重复使用一个(如果有)。
REQUIRES_NEW
:代码将始终在新事务中运行。如果存在,则暂停当前事务。
@Transactional
的默认值是 REQUIRED
,这通常是您想要的。
定义交易之间的数据契约。
ISOLATION_READ_UNCOMMITTED
:允许脏读。
ISOLATION_READ_COMMITTED
:不允许脏读。
ISOLATION_REPEATABLE_READ
:如果一行在同一个事务中被读取两次,结果总是一样的。
ISOLATION_SERIALIZABLE
:按顺序执行所有事务。
在多线程应用程序中,不同的级别具有不同的性能特征。我认为如果您了解 脏读 概念,您将能够选择一个好的选项。
默认值可能因不同的数据库而异。例如,对于 MariaDB它是可重复阅读
。
可能发生脏读的示例:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
所以一个合理的默认值(如果可以声明的话)可以是 ISOLATION_READ_COMMITTED
,它只允许您读取已经被其他正在运行的事务提交的值,结合传播级别 必需的
。然后,如果您的应用程序有其他需求,您可以从那里开始工作。
一个实际示例,说明在进入 provideService
例程时始终会创建新事务并在离开时完成:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
如果我们改为使用 REQUIRED
,事务 would remain open如果在进入例程时事务已经打开。另请注意,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 ...
}
传播级别为
我是一名优秀的程序员,十分优秀!