Метод должен вызываться вне контекста транзакции - Spring @Transactional - PullRequest
0 голосов
/ 29 июня 2018

Надеюсь, ты в порядке.

Я хотел бы найти лучший способ обеспечить вызов метода службы вне транзакции. Это было бы следующим образом:

Допустим, у нас есть метод в виде:

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

Теперь предположим, что мы вызываем этот метод, но нам нужно убедиться, что он не вызывается внутри кода, который уже является транзакционным. Следующее было бы неправильно:

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

Каков наилучший вариант, чтобы наш метод "insertEntity" знал, что вызывается внутри запущенной транзакции, и выдает ошибку?

Спасибо!

1 Ответ

0 голосов
/ 01 июля 2018

Вы можете вызвать метод 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 для проверки вашего сценария (я опускаю классы и файлы конфигурации, а также объявления 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());
    }
}

А вот и журнал казни:

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
...