代理
在您的服务中,您创建了 2 个方法,均为 @Transactional。当您创建 bean 时,spring 将创建一个代理,以便在运行时为您添加事务方法的行为。让我们更深入地了解:
该代理由图像说明。 任何来自外部世界的呼叫者都不会直接与您交谈,而是与您的代理交谈。然后,代理会调用你来执行你的服务代码。
现在,“任何来自外界的呼叫者都不会直接与您通话”非常重要。如果您进行内部调用,就像您在调用persistLineManager 的persistEmployee 中所做的那样,那么您不会通过代理。你直接调用你的方法,NO PROXY。因此,persistLineManager 方法顶部的注释不会被读取。
所以,当persistLineManager 抛出RuntimeException 时,异常被你的调用者persistEmployee 直接捕获,你直接进入你的catch。由于没有代理,所以没有回滚,因为事务代理没有捕获异常。
如果你只这样做,你会发生回滚:
@Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
persistLineManager();
// Don't catch and the exception will be catched by the transaction proxy, which will rollback
}
public void persistLineManager() {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
}
默认情况下,@Transactional 回滚 RuntimeException
交易模板
假设您仍然希望这两种方法都是独立的事务性方法,您可以使用TransactionTemplate。这是一个例子:
class MyService {
// Have a field of type TransactionTemplate
private TransactionTemplate template;
// In the constructor, Spring will inject the correct bean
public MyService(PlatformTransactionManager transactionManager) {
template = new TransactionTemplate(transactionManager);
// Set this here if you always want this behaviour for your programmatic transaction
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
}
// Here you start your first transaction when arriving from the outside
@Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
// Inner call
try {
persistLineManager();
} catch (RuntimeException e) {
// Do what you want
}
}
public void persistLineManager() {
// Here, ask to your transactionTemplate to execute your code.
template.execute(status -> {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
return null;
});
}
}
我还没有测试所有东西,你可能会遇到一些错误,但我希望你能明白。
传播
让我添加最后一部分关于 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 之间的区别:
PROPAGATION_REQUIRED:
- 要么我没有交易,然后我创建一个
- 或者有一个正在运行的事务,我加入它。
传播要求:
- 在任何情况下,无论事务是否正在运行,我都会创建一个新事务。
例子:
- 客户正在使用 PROPAGATION_REQUIRED 进入我的事务方法。它创建一个事务名称“TA”。
- 此事务性方法调用一个方法,该方法也是事务性的,但 PROPAGATION_REQUIRES_NEW。它创建名为“TB”的第二个事务
- 现在有了:“client”->“TA”->“TB”
- 但是第二种方法会触发回滚。在这种情况下,只有“TB”会被回滚,因为“TA”和“TB”是两个不同的事务。
- 因此,在 DB 中,我将保留在“TA”中进行的所有操作,而不是在“TB”中。
希望对你有帮助