方法必须在事务上下文之外调用-Spring @Transactional

时间:2018-06-29 16:30:02

标签: spring timeout transactional

我希望你一切都好。

我想找到最好的方法来确保在事务外部调用服务方法。如下:

让我们说我们有一种形式为:

的方法
@Transactional
public void insertEntity(Entity entity){
    persistence.save(entity);
}

现在,可以说我们正在调用此方法,但是我们需要确保不会在已经具有事务性的内部代码中调用该方法。以下是错误的:

@Transactional
public void enclosingTransaction() {
    //Perform long process transaction
    service.insertEntity(entity);
}

什么是使我们的方法“ insertEntity”了解正在运行的事务内被调用并引发错误的最佳选择?

谢谢!

1 个答案:

答案 0 :(得分:0)

您可以调用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()返回一个TransactionStatus对象,该对象表示当前方法调用的事务状态


我编写了一个最小的Spring MVC Web应用程序来测试您的情况(我省略了配置类和文件以及importpackage的声明):

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());
    }
}

这是执行的日志

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