必须在事务上下文之外调用方法 - Spring @Transactional
Method must be called outside of transactional context - Spring @Transactional
希望你一切都好。
我想找到确保在事务外调用服务方法的最佳方法。它将如下所示:
假设我们有一个以下形式的方法:
@Transactional
public void insertEntity(Entity entity){
persistence.save(entity);
}
现在,假设我们正在调用此方法,但我们需要确保它不会在已经是事务性代码的内部调用。以下是错误的:
@Transactional
public void enclosingTransaction() {
//Perform long process transaction
service.insertEntity(entity);
}
让我们的方法 "insertEntity" 知道正在 运行 事务中调用并抛出错误的最佳选择是什么?
谢谢!
您可以调用 TransactionAspectSupport.currentTransactionStatus().isNewTransaction()
方法来了解当前交易是否是新的(即它不是从另一个 @Transactional
方法传播的):
@Transactional
public void insertEntity(Entity entity){
if (!TransactionAspectSupport.currentTransactionStatus().isNewTransaction()) {
throw new IllegalStateException("Transaction is not new!");
}
persistence.save(entity);
}
静态方法TransactionAspectSupport.currentTransactionStatus()
returns一个TransactionStatus
对象表示当前方法调用的事务状态.
我写了一个最小的 Spring MVC webapp 来测试你的场景(我省略了配置 类 和文件,以及 import
和 package
s 声明):
TestController.java
@RestController
public class TestController {
private static final Logger log = LoggerFactory.getLogger(TestController.class);
@Autowired
private ServiceOne serviceOne;
@Autowired
private ServiceTwo serviceTwo;
@GetMapping(path = "/test-transactions")
public String testTransactions() {
log.info("*** TestController.testTransactions() ***");
log.info("* Invoking serviceOne.methodOne()...");
try {
serviceOne.methodOne();
}
catch (IllegalStateException e) {
log.error("* {} invoking serviceOne.methodOne()!", e.getClass().getSimpleName());
}
log.info("* Invoking serviceTwo.methodTwo()...");
try {
serviceTwo.methodTwo();
}
catch (IllegalStateException e) {
log.error("* {} invoking serviceTwo.methodTwo()!", e.getClass().getSimpleName());
}
return "OK";
}
}
ServiceOneImpl.java
@Service
public class ServiceOneImpl implements ServiceOne {
private static final Logger log = LoggerFactory.getLogger(ServiceOneImpl.class);
@Autowired
private ServiceTwo serviceTwo;
@PersistenceContext
private EntityManager em;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void methodOne() {
log.info("*** ServiceOne.methodOne() ***");
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
log.info("Query result={}", em.createNativeQuery("SELECT 1").getResultList());
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
serviceTwo.methodTwo();
}
}
ServiceTwoImpl.java
@Service
public class ServiceTwoImpl implements ServiceTwo {
private static final Logger log = LoggerFactory.getLogger(ServiceTwoImpl.class);
@PersistenceContext
private EntityManager em;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void methodTwo() {
log.info("*** ServiceTwo.methodTwo() ***");
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
if (!TransactionAspectSupport.currentTransactionStatus().isNewTransaction()) {
log.warn("Throwing exception because transaction is not new...");
throw new IllegalStateException("Transaction is not new!");
}
log.info("Query result={}", em.createNativeQuery("SELECT 2").getResultList());
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
}
}
这是执行的 log:
INFO test.transactions.web.TestController - *** TestController.testTransactions() ***
INFO test.transactions.web.TestController - * Invoking serviceOne.methodOne()...
INFO test.transactions.service.ServiceOneImpl - *** ServiceOne.methodOne() ***
INFO test.transactions.service.ServiceOneImpl - getCurrentTransactionName=test.transactions.service.ServiceOneImpl.methodOne
INFO test.transactions.service.ServiceOneImpl - isNewTransaction=true
INFO test.transactions.service.ServiceOneImpl - Query result=[1]
INFO test.transactions.service.ServiceOneImpl - getCurrentTransactionName=test.transactions.service.ServiceOneImpl.methodOne
INFO test.transactions.service.ServiceOneImpl - isNewTransaction=true
INFO test.transactions.service.ServiceTwoImpl - *** ServiceTwo.methodTwo() ***
INFO test.transactions.service.ServiceTwoImpl - getCurrentTransactionName=test.transactions.service.ServiceOneImpl.methodOne
INFO test.transactions.service.ServiceTwoImpl - isNewTransaction=false
WARN test.transactions.service.ServiceTwoImpl - Throwing exception because transaction is not new...
ERROR test.transactions.web.TestController - * IllegalStateException invoking serviceOne.methodOne()!
INFO test.transactions.web.TestController - * Invoking serviceTwo.methodTwo()...
INFO test.transactions.service.ServiceTwoImpl - *** ServiceTwo.methodTwo() ***
INFO test.transactions.service.ServiceTwoImpl - getCurrentTransactionName=test.transactions.service.ServiceTwoImpl.methodTwo
INFO test.transactions.service.ServiceTwoImpl - isNewTransaction=true
INFO test.transactions.service.ServiceTwoImpl - Query result=[2]
INFO test.transactions.service.ServiceTwoImpl - getCurrentTransactionName=test.transactions.service.ServiceTwoImpl.methodTwo
INFO test.transactions.service.ServiceTwoImpl - isNewTransaction=true
希望你一切都好。
我想找到确保在事务外调用服务方法的最佳方法。它将如下所示:
假设我们有一个以下形式的方法:
@Transactional
public void insertEntity(Entity entity){
persistence.save(entity);
}
现在,假设我们正在调用此方法,但我们需要确保它不会在已经是事务性代码的内部调用。以下是错误的:
@Transactional
public void enclosingTransaction() {
//Perform long process transaction
service.insertEntity(entity);
}
让我们的方法 "insertEntity" 知道正在 运行 事务中调用并抛出错误的最佳选择是什么?
谢谢!
您可以调用 TransactionAspectSupport.currentTransactionStatus().isNewTransaction()
方法来了解当前交易是否是新的(即它不是从另一个 @Transactional
方法传播的):
@Transactional
public void insertEntity(Entity entity){
if (!TransactionAspectSupport.currentTransactionStatus().isNewTransaction()) {
throw new IllegalStateException("Transaction is not new!");
}
persistence.save(entity);
}
静态方法TransactionAspectSupport.currentTransactionStatus()
returns一个TransactionStatus
对象表示当前方法调用的事务状态.
我写了一个最小的 Spring MVC webapp 来测试你的场景(我省略了配置 类 和文件,以及 import
和 package
s 声明):
TestController.java
@RestController
public class TestController {
private static final Logger log = LoggerFactory.getLogger(TestController.class);
@Autowired
private ServiceOne serviceOne;
@Autowired
private ServiceTwo serviceTwo;
@GetMapping(path = "/test-transactions")
public String testTransactions() {
log.info("*** TestController.testTransactions() ***");
log.info("* Invoking serviceOne.methodOne()...");
try {
serviceOne.methodOne();
}
catch (IllegalStateException e) {
log.error("* {} invoking serviceOne.methodOne()!", e.getClass().getSimpleName());
}
log.info("* Invoking serviceTwo.methodTwo()...");
try {
serviceTwo.methodTwo();
}
catch (IllegalStateException e) {
log.error("* {} invoking serviceTwo.methodTwo()!", e.getClass().getSimpleName());
}
return "OK";
}
}
ServiceOneImpl.java
@Service
public class ServiceOneImpl implements ServiceOne {
private static final Logger log = LoggerFactory.getLogger(ServiceOneImpl.class);
@Autowired
private ServiceTwo serviceTwo;
@PersistenceContext
private EntityManager em;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void methodOne() {
log.info("*** ServiceOne.methodOne() ***");
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
log.info("Query result={}", em.createNativeQuery("SELECT 1").getResultList());
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
serviceTwo.methodTwo();
}
}
ServiceTwoImpl.java
@Service
public class ServiceTwoImpl implements ServiceTwo {
private static final Logger log = LoggerFactory.getLogger(ServiceTwoImpl.class);
@PersistenceContext
private EntityManager em;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void methodTwo() {
log.info("*** ServiceTwo.methodTwo() ***");
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
if (!TransactionAspectSupport.currentTransactionStatus().isNewTransaction()) {
log.warn("Throwing exception because transaction is not new...");
throw new IllegalStateException("Transaction is not new!");
}
log.info("Query result={}", em.createNativeQuery("SELECT 2").getResultList());
log.info("getCurrentTransactionName={}", TransactionSynchronizationManager.getCurrentTransactionName());
log.info("isNewTransaction={}", TransactionAspectSupport.currentTransactionStatus().isNewTransaction());
}
}
这是执行的 log:
INFO test.transactions.web.TestController - *** TestController.testTransactions() ***
INFO test.transactions.web.TestController - * Invoking serviceOne.methodOne()...
INFO test.transactions.service.ServiceOneImpl - *** ServiceOne.methodOne() ***
INFO test.transactions.service.ServiceOneImpl - getCurrentTransactionName=test.transactions.service.ServiceOneImpl.methodOne
INFO test.transactions.service.ServiceOneImpl - isNewTransaction=true
INFO test.transactions.service.ServiceOneImpl - Query result=[1]
INFO test.transactions.service.ServiceOneImpl - getCurrentTransactionName=test.transactions.service.ServiceOneImpl.methodOne
INFO test.transactions.service.ServiceOneImpl - isNewTransaction=true
INFO test.transactions.service.ServiceTwoImpl - *** ServiceTwo.methodTwo() ***
INFO test.transactions.service.ServiceTwoImpl - getCurrentTransactionName=test.transactions.service.ServiceOneImpl.methodOne
INFO test.transactions.service.ServiceTwoImpl - isNewTransaction=false
WARN test.transactions.service.ServiceTwoImpl - Throwing exception because transaction is not new...
ERROR test.transactions.web.TestController - * IllegalStateException invoking serviceOne.methodOne()!
INFO test.transactions.web.TestController - * Invoking serviceTwo.methodTwo()...
INFO test.transactions.service.ServiceTwoImpl - *** ServiceTwo.methodTwo() ***
INFO test.transactions.service.ServiceTwoImpl - getCurrentTransactionName=test.transactions.service.ServiceTwoImpl.methodTwo
INFO test.transactions.service.ServiceTwoImpl - isNewTransaction=true
INFO test.transactions.service.ServiceTwoImpl - Query result=[2]
INFO test.transactions.service.ServiceTwoImpl - getCurrentTransactionName=test.transactions.service.ServiceTwoImpl.methodTwo
INFO test.transactions.service.ServiceTwoImpl - isNewTransaction=true