![](/img/trans.png)
[英]Spring transaction REQUIRED vs REQUIRES_NEW : Rollback Transaction
[英]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.