有人能通过真实的例子解释@Transactional注释中的隔离和传播参数是用于什么吗?
基本上,我应该在什么时候以及为什么选择更改它们的默认值。
有人能通过真实的例子解释@Transactional注释中的隔离和传播参数是用于什么吗?
基本上,我应该在什么时候以及为什么选择更改它们的默认值。
当前回答
我们可以为此添加:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
其他回答
隔离级别定义了一个事务对某个数据存储库所做的更改如何影响其他同时并发的事务,以及更改后的数据如何以及何时对其他事务可用。当我们使用Spring框架定义事务时,我们还可以配置在哪个隔离级别中执行同一事务。
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
READ_UNCOMMITTED隔离级别表示事务可以读取其他事务尚未提交的数据。
READ_COMMITTED隔离级别表示事务不能读取其他事务尚未提交的数据。
REPEATABLE_READ隔离级别表示,如果一个事务多次从数据库读取一条记录,那么所有这些读取操作的结果必须始终相同。
SERIALIZABLE隔离级别是所有隔离级别中限制最严格的。事务在所有级别上(读、范围和写锁定)都是带锁执行的,因此它们看起来好像是以序列化的方式执行的。
传播是决定如何在逻辑事务或物理事务中封装业务方法的能力。
Spring REQUIRED行为意味着如果在当前bean方法执行上下文中已经打开了一个事务,那么将使用相同的事务。
REQUIRES_NEW行为意味着容器总是会创建一个新的物理事务。
NESTED行为使得嵌套Spring事务使用相同的物理事务,但是在嵌套调用之间设置保存点,这样内部事务也可以独立于外部事务回滚。
MANDATORY行为声明现有的已打开事务必须已经存在。否则容器将引发异常。
NEVER行为表示现有的已打开事务必须不存在。如果事务存在,容器将抛出异常。
NOT_SUPPORTED行为将在任何事务的作用域之外执行。如果一个打开的事务已经存在,它将被暂停。
如果已打开的事务已经存在,SUPPORTS行为将在事务范围内执行。如果没有已经打开的事务,该方法将以非事务的方式执行。
好问题,虽然不是一个微不足道的问题。
传播
定义事务如何相互关联。常见的选项:
REQUIRED:代码总是在事务中运行。创建一个新的事务或重用一个可用的事务。 REQUIRES_NEW:代码总是在一个新的事务中运行。如果存在当前事务,则暂停当前事务。
@Transactional的默认值是REQUIRED,这通常是您想要的。
隔离
定义事务之间的数据契约。
ISOLATION_READ_UNCOMMITTED:允许脏读。 ISOLATION_READ_COMMITTED:不允许脏读。 ISOLATION_REPEATABLE_READ:如果在同一个事务中读取一行两次,结果总是相同的。 ISOLATION_SERIALIZABLE:按顺序执行所有事务。
在多线程应用程序中,不同的级别具有不同的性能特征。我认为如果你理解了脏读的概念,你就能选择一个好的选择。
缺省值在不同数据库之间可能有所不同。例如,对于MariaDB,它是REPEATABLE READ。
可以发生脏读的示例:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
因此,一个合理的默认值(如果可以声明的话)可以是ISOLATION_READ_COMMITTED,它只允许您读取已经由其他正在运行的事务提交的值,并结合传播级别REQUIRED。然后,如果您的应用程序有其他需求,您就可以从那里开始工作。
一个实际的例子,一个新的事务总是在进入provideService例程时创建,并在离开时完成:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
如果我们改为使用REQUIRED,那么如果事务在进入例程时已经打开,那么事务将保持打开状态。 还要注意,回滚的结果可能不同,因为多个执行可能参与同一个事务。
我们可以很容易地用一个测试来验证行为,看看结果在传播级别上有什么不同:
@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()没有回滚,因为它创建了自己的子事务。 REQUIRED:我们期望所有东西都回滚了,备份存储没有改变。
我们可以为此添加:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
You almost never want to use Read Uncommited since it's not really ACID compliant. Read Commmited is a good default starting place. Repeatable Read is probably only needed in reporting, rollup or aggregation scenarios. Note that many DBs, postgres included don't actually support Repeatable Read, you have to use Serializable instead. Serializable is useful for things that you know have to happen completely independently of anything else; think of it like synchronized in Java. Serializable goes hand in hand with REQUIRES_NEW propagation.
我对所有运行UPDATE或DELETE查询的函数以及“服务”级函数都使用require。对于只运行select的DAO级函数,我使用SUPPORTS,如果一个已经启动(即从服务函数调用),它将参与TX。
你可以这样用:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
你也可以用这个东西:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}