【发布时间】:2020-05-05 09:47:55
【问题描述】:
我需要处理一个元素列表,如果发生异常(或 RuntimeException),它无法撤消之前完成的工作。它只能撤消该时间的数据库操作,并且必须继续处理其他元素。
我的策略是用 propagation = Propagation.REQUIRED 创建一个带有 for 循环的类,并在其中调用另一个方法 propagation = 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来切换我的事务的Propagation:
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> {
}
【问题讨论】:
-
m2 永远不会抛出异常,因此 Spring 永远不会回滚其事务。 catch 必须在 m1 内,在 for 循环中。不在 m2() 中。
-
@JBNizet 如果我在 m2() 的 catch 块中添加“throw e”,则事务回滚(到目前为止一切顺利),但原始事务也是如此! (我的例子的交易1)。在我看来,异常只会停止内部事务,而不是 m1() 的原始。有意义吗?
-
如果你捕捉到 m2() 抛出的异常,m1 的事务将不会被回滚。删除 m2 中的 try/catch,并将其放入 m1() 的 for 循环中(正如我在第一条评论中已经说过的)
-
顺便说一句,m1 根本不需要是事务性的。它只是一个超过 2 个整数的循环。您不需要为此进行交易。
-
因为 m1 是事务性的,所以对 m2 使用 REQUIRES 将简单地在为 m1 创建的唯一单个事务中完成 m2 的所有工作。 m2 抛出的任何异常都会回滚唯一的 m1 事务。如果 m1 不是事务性的(并且它的调用者都不是),则 m2 可以使用 REQUIRES,因为 REQUIRES 和 REQUIRES_NEW 都具有为 m2 启动新事务的相同效果。
标签: java spring-boot transactions