Неполадка транзакции с использованием REQUIRES_NEW в for for l oop с Spring Boot - PullRequest
0 голосов
/ 19 января 2020

Мне нужно обработать список элементов, и, если возникает исключение (или RuntimeException), оно не может отменить работу, выполненную ранее. Это должно только отменить операцию базы данных в это время, и другие элементы должны продолжить обрабатываться.

Моя стратегия состояла в том, чтобы создать класс с распространение = распространение. ТРЕБУЕТСЯ с для для * oop, и внутри него я бы вызвал другой метод с распространение = распространение .REQUIRES_NEW .

Transaction 1->
    loop -> transaction 2
         -> transaction 3
         -> ...
         -> transaction N
end of transaction 1

В этой стратегии, если в транзакции 2 возникнет исключение, он будет откатан и транзакция 3 продолжится в обычном режиме.

Проблема: если в транзакции 2 возникает исключение, он не откатывается и транзакция 3 продолжается в обычном режиме. На транзакцию 1 это не влияет.

Если я добавлю throw e в блоке перехвата Service2 и произойдет исключение в транзакции 3, она будет откатана, а транзакция 2 не затрагивается (пока все хорошо), но транзакция 1 получает исключение, и процесс останавливается, не обрабатывая оставшиеся элементы.

Что я делаю не так? = /

Код:

package test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DefaultController {

    @Autowired
    private Service1 service1;

    @ResponseBody
    @RequestMapping(method = RequestMethod.GET, path = "/")
    public ResponseEntity<?> test() throws Exception {
        service1.m1();
        return new ResponseEntity<>(HttpStatus.OK);
    }

}

Этот контроллер вызывает сервис:

package test.controller;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class Service1 {

    @Autowired
    private Service2 service2;

    @Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRED)
    public void m1() {

        List<Integer> list = Arrays.asList(1, 2);

        for (Integer j : list) {
            service2.m2(j);
            System.out.println("Exception for j = " + j);
        }
    }
}

И этот сервис1 вызывает сервис2, потому что я знал, что Spring Boot AOP основан на прокси-сервере, затем мне нужен был другой компонент для переключения Распространения моих транзакций:

package test.controller;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import test.model.domain.Log;
import test.service.LogService;

@Service
public class Service2 {

    @Autowired
    private LogService logger;

    @Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRES_NEW)
    public void m2(int i) {

        try {
            Log log = new Log();
            log.setDataMensagem(new Date());
            log.setDescricaoEnvioRecebimento("TEST");
            log.setDescricaoMensagem("TEST1");
            log.setIdMensagem("TEST2");
            log.setNomeFilaServico("TESTE3");
            logger.save(log);

            if (i == 2) {
                throw new RuntimeException();
            }
        } catch (Exception e) {
            System.out.println("RuntimeException in i = " + i);
        }
    }
}

LogService:

package test.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import test.indicador.LogFilaServicoIndicador;
import test.model.domain.Log;
import test.repository.LogRepository;

@Service
@Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRED)
public class LogService {

    private final LogRepository repository;

    public LogService(LogRepository repository) {
        this.repository = repository;
    }

    public Log save(Log logFilaServico) {
        return repository.save(logFilaServico);
    }
}

Репозиторий:

package test.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import test.model.domain.Log;

@Repository
public interface LogRepository extends CrudRepository<Log, Long> {

}

Ответы [ 2 ]

0 голосов
/ 19 января 2020

Для отката m2 ваше исключение должно пересекать aop-обертку service2.

Это, в свою очередь, в конечном итоге будет пузыриться, как и m1. Вы по-прежнему несете ответственность за написание правильного java кода, если хотите продолжать j = 3 ...

Поймать его в for для l oop, как вы это обычно делаете. Все ваши @Transactional верны.

0 голосов
/ 19 января 2020

Исходя из справки @JBNizet, для решения проблемы мне нужно было:

1) Удалить @Transactional (транзакцияManager = "actionManager ", распространение = Propagation.REQUIRED) из m1 ().

2) Измените REQUIRES_NEW на REQUIRED в m2 () и удалите блок try catch из m2 () и добавьте блок try c catch в m1 (), потому что он должен обрабатывать исключение, не останавливая m1 () выполнение.

Итак, у меня есть:

public void m1() {

        List<Integer> list = Arrays.asList(1, 2);

        for (Integer j : list) {
            try {
                service2.m2(j);

            } catch (Exception e) {
                System.out.println("Exception for j = " + j);
            }
        }
    }

И м2 ():

@Transactional(transactionManager = "transactionManager", propagation = Propagation.REQUIRED)
public void m2(int i) {

    Log log = new Log();
    log.setDataMensagem(new Date());
    log.setDescricaoEnvioRecebimento("TEST");
    log.setDescricaoMensagem("TEST1");
    log.setIdMensagem("TEST2");
    log.setNomeFilaServico("TESTE3");
    logger.save(log);

    if (i == 2) {
        throw new RuntimeException();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...