簡體   English   中英

在 Spring Boot 的 for 循環中使用 REQUIRES_NEW 的事務問題

[英]Transaction problem using REQUIRES_NEW in a for loop with Spring Boot

我需要處理一個元素列表,如果發生異常(或運行時異常),它無法撤消之前完成的工作。 它必須只在那個時間撤消數據庫操作,其他元素必須繼續處理。

我的策略是用一個 for 循環創建一個帶有傳播 = Propagation.REQUIRED的類,在它內部,我會調用另一個帶有傳播 = Propagation.REQUIRES_NEW 的方法。

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

在此策略中,如果事務 2 中發生異常,它將被回滾,事務 3 將正常繼續。

問題:如果事務 2 發生異常,則不會回滾,事務 3 繼續正常進行。 事務 1 不受影響。

如果我在 Service2 的 catch 塊中添加一個throw e並且它在事務 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);
        }
    }
}

而這個service1調用了一個service2,因為我知道Spring Boot AOP是基於代理的,那么我需要另一個bean來切換我的事務的傳播:

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

日志服務:

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

}

基於@JBNizet 的幫助,為了解決我需要的問題:

1) 從 m1() 中刪除 @Transactional(transactionManager = "transactionManager",propagation = Propagation.REQUIRED)。

2) 將m2() 中的REQUIRES_NEW 改為REQUIRED 並從m2() 中移除try catch 塊並在m1() 中添加一個tryc catch 塊,因為它需要在不停止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);
            }
        }
    }

和 m2():

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

要回滾 m2,您的異常必須跨越 service2 的 aop 包裝器。

反過來,它最終會像m1那樣冒泡。 如果你想繼續 j=3,你仍然有責任編寫正確的 java 代碼......

像往常一樣在 for 循環中捕獲它。 你所有的@Transactional 都是正確的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM