java-内部事务(REQUIRES_NEW)引发异常时的外部事务回滚
作者:互联网
我有方法:
@Transactional
public void importChargesRequest() {
...
for (Charge charge : charges) {
try {
Charge savedCharge = saveCharge(charge);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
为了持久保存每个Charge我调用内部方法:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Charge saveCharge(Charge charge) {
return chargesRepository.saveAndFlush(charge);
}
如果saveCharge方法抛出异常(在我的情况下为约束异常),我想写日志,并继续持久化另一个实体.但是,当我捕获异常时,我的外部事务回滚并出现错误:
Transaction was marked for rollback only; cannot commit; nested exception is org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit
我需要打开事务并开始保存每个实体.如果某个实体无法例外保存-我需要记录此例外并继续保存另一个实体.当所有实体都将保存(或记录)时,我需要提交外部事务.但是现在回滚了.我该如何解决?
编辑:
我接受了评论并将REQUIRES_NEW事务移至另一个bean:
@Service
public class TestService {
private final TestDao testDao;
public TestService(TestDao testDao) {
this.testDao = testDao;
}
@Transactional
public void saveTest() {
for (int i = 0; i < 100; i++) {
Test test = new Test();
if (i == 10 || i == 20) {
test.setName("123");
} else {
test.setName(UUID.randomUUID().toString());
}
testDao.save(test);
}
}
}
每个内部事务还有另一个bean:
@Slf4j
@Component
@Repository
public class TestDao {
@PersistenceContext
private EntityManager entityManager;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Test test) {
entityManager.persist(test);
}
}
第一次尝试保存时,数据库中有20行.然后每次保存,我得到10行.名称具有约束.当我收到错误消息时-事务已提交且无法继续.每次保存后我要等待98行.
解决方法:
如果saveCharge是与importChargesRequest处于同一bean中的方法,则@Transactional批注将被忽略,并且saveAndFlush在同一(外部)事务中工作.
(我确定当您使用代理/拦截器来管理事务时就是这种情况.我相当确定在使用基于Aspectj的事务拦截时也是如此).
通常,只有在异常一直蔓延到外部方法(标为@Transaction的气泡)的情况下,才将事务标记为回滚,但是我怀疑存储库或事务管理器本身(休眠会话)直接将事务标记为回滚,原因是由于约束违反.
解决方案是将saveCharge移至另一个bean,并使用importChargesRequest方法将该bean注入bean.
@Service
public class ChargesDataService{
@Autowire
private ChargesRepository chargesRepository;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Charge saveCharge(Charge charge) {
return chargesRepository.saveAndFlush(charge);
}
}
@RestController
public class ChargesController{
@Autowire
private ChargesDataService chargesDataService;
@Transactional
public void importChargesRequest() {
for (Charge charge : charges) {
try {
Charge savedCharge = chargesDataService.saveCharge(charge);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
}
附录:
注释将被忽略,因为您不再通过bean实例的代理,这意味着不会调用任何拦截器,这意味着没有地方可以处理新事务.
您可以通过在saveCharge方法中设置一个断点并查看stacktrace ^来检查是否是这种情况.寻找类似事务拦截器的invokeWithinTransaction之类的方法.
^您还可以创建一个新的异常,调用fillInStacktrace,然后记录/打印该异常及其堆栈跟踪.
标签:spring-transactions,spring-data-jpa,spring,java 来源: https://codeday.me/bug/20191025/1925373.html