当前位置: 首页 > 知识库问答 >
问题:

ExpectiontedrollbackException:事务被回滚,因为它已被标记为仅回滚

唐宇定
2023-03-14

我有这样的场景:

  1. 从IncomingMessage表中获取(读取和删除)记录
  2. 读取记录内容
  3. 在某些表中插入某些内容
  4. 如果在步骤1-3中出现错误(任何异常),请将错误记录插入OutgoingMessage表
  5. 否则,将成功记录插入OutgoingMessage表

因此步骤1、2、3、4应该在事务中,或者步骤1、2、3、5

public class ReceiveMessagesJob implements ScheduledJob {
// ...
    @Override
    public void run() {
        try {
            processMessageMediator.processNextRegistrationMessage();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
// ...
}
public class ProcessMessageMediatorImpl implements ProcessMessageMediator {
// ...
    @Override
    @Transactional
    public void processNextRegistrationMessage() throws ProcessIncomingMessageException {
        String refrenceId = null;
        MessageTypeEnum registrationMessageType = MessageTypeEnum.REGISTRATION;
        try {
            String messageContent = incomingMessageService.fetchNextMessageContent(registrationMessageType);
            if (messageContent == null) {
                return;
            }
            IncomingXmlModel incomingXmlModel = incomingXmlDeserializer.fromXml(messageContent);
            refrenceId = incomingXmlModel.getRefrenceId();
            if (!StringUtil.hasText(refrenceId)) {
                throw new ProcessIncomingMessageException(
                        "Can not proceed processing incoming-message. refrence-code field is null.");
            }
            sqlCommandHandlerService.persist(incomingXmlModel);
        } catch (Exception e) {
            if (e instanceof ProcessIncomingMessageException) {
                throw (ProcessIncomingMessageException) e;
            }
            e.printStackTrace();
            // send error outgoing-message
            OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,
                    ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
            saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
            return;
        }
        // send success outgoing-message
        OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId, ProcessResultStateEnum.SUCCEED.getCode());
        saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
    }

    private void saveOutgoingMessage(OutgoingXmlModel outgoingXmlModel, MessageTypeEnum messageType)
            throws ProcessIncomingMessageException {
        String xml = outgoingXmlSerializer.toXml(outgoingXmlModel, messageType);
        OutgoingMessageEntity entity = new OutgoingMessageEntity(messageType.getCode(), new Date());
        try {
            outgoingMessageService.save(entity, xml);
        } catch (SaveOutgoingMessageException e) {
            throw new ProcessIncomingMessageException("Can not proceed processing incoming-message.", e);
        }
    }
// ...
}
catch (Exception e) {
    if (e instanceof ProcessIncomingMessageException) {
        throw (ProcessIncomingMessageException) e;
    }
    e.printStackTrace();
    //send error outgoing-message
    OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
    saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
    return;
}
public class SqlCommandHandlerServiceImpl implements SqlCommandHandlerService {
// ...
    @Override
    @Transactional
    public void persist(IncomingXmlModel incomingXmlModel) {
        Collections.sort(incomingXmlModel.getTables());
        List<ParametricQuery> queries = generateSqlQueries(incomingXmlModel.getTables());
        for (ParametricQuery query : queries) {
            queryExecuter.executeQuery(query);
        }
    }
// ...
}

但是当SqlCommandHandlerService.persist()抛出异常(这里是org.hibernate.exception.constraintViolationException异常)时,在OutgoingMessage表中插入错误记录后,当事务要提交时,我会得到ExpectiontedRollBackException。我想不出我的问题在哪里:

Exception in thread "null#0" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
    at ir.tamin.branch.insuranceregistration.services.schedular.ReceiveMessagesJob$$EnhancerByCGLIB$$63524c6b.run(<generated>)
    at ir.asta.wise.core.util.timer.JobScheduler$ScheduledJobThread.run(JobScheduler.java:132)

我使用的是Hibernate-4.1.0-final,我的数据库是oracle,下面是我的事务管理器bean:

<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager"
    proxy-target-class="true" />

提前谢了。

共有1个答案

有权
2023-03-14

这是正常行为,原因是您的SQLCommandHandlerService.persist方法在执行时需要一个TX(因为它被标记为@transactional注释)。但是当在ProcessNextRegistrationMessage内调用它时,因为有一个可用的TX,容器不会创建新的TX,而是使用现有的TX。因此,如果sqlcommandhandlerservice.persist方法中出现任何异常,它会导致将TX设置为rollbackonly(即使您在调用方中捕获异常并忽略它)。

为了克服这一点,可以对事务使用传播级别。看看这个,找出哪种传播最适合您的要求。

嗯,在一位同事向我提出了几个关于类似情况的问题后,我觉得这需要澄清一下。
尽管传播可以解决这些问题,但在使用它们时应该非常小心,除非你完全理解它们的意思和它们是如何工作的,否则不要使用它们。您最终可能会html" target="_blank">持久化一些数据,并回滚一些其他数据,而这些数据并不期望它们以这种方式工作,结果可能会出现严重的错误。

 类似资料:
  • 问题内容: 我在@Transactional方法中提交事务时遇到问题: 当我从methodA()调用methodB()时,该方法成功通过,并且可以在日志中看到“确定”。但后来我明白了 在异常中完全缺少methodB的上下文-我想可以吗? methodB()中的某些内容将事务标记为仅回滚?我如何找到它?例如,有没有一种方法可以检查类似的东西-这样,我可以逐步检查方法并找到原因。 问题答案: 我终于明

  • 问题内容: 我在@Transactional方法中提交事务时遇到问题: 当我从methodA()调用methodB()时,该方法成功通过,并且可以在日志中看到“确定”。但后来我明白了 在异常中完全缺少methodB的上下文-我想这可以吗? methodB()中的某些内容将事务标记为仅回滚?我如何找到它?例如,是否有一种检查类似方法的方法-这样,我可以逐步检查方法并找到原因。 问题答案: 我终于明白

  • 问题内容: 我在@Transactional方法中提交事务时遇到问题: 当我从methodA()调用methodB()时,该方法成功通过,并且可以在日志中看到“确定”。但后来我明白了 在异常中完全缺少methodB的上下文-我想这可以吗? methodB()中的某些内容将事务标记为仅回滚?我如何找到它?例如,是否有一种检查类似方法的方法-这样,我可以逐步检查方法并找到原因。 问题答案: 当你将方法

  • 我在@transactional方法中提交事务时遇到问题: 当我从methodA()调用methodB()时,该方法成功传递,并且我可以在日志中看到“OK”。但我得到 异常中完全缺少methodB的上下文-我想这可以吗? methodB()中的内容将事务标记为仅回滚?我怎么才能查出来?例如,是否有一种方法来检查类似这样,我可以逐级遍历该方法并找到原因。

  • 我在我正在开发的一个应用程序中使用了Spring和Hibernate,我在处理事务时遇到了一个问题。 我有一个服务类,它从数据库中加载一些实体,修改它们的一些值,然后(当一切都有效时)将这些更改提交给数据库。详细地说: 我得到的错误是这样的: Main.java: RegionService.java:

  • 我有一个类,它有一个标记为@Transactional的方法。从该方法中,会调用多个标记为@Transactional的方法。所以,从层次上讲,这就像有一个父事务和多个子事务。当我调用从数据库获取数据的方法,然后最终调用一个应该将数据存储在数据库中的方法时,就会出现问题,然后事务提交失败,并用“