我有这种情况:
所以步骤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();
}
}
// ...
}
我在ProcessMessageMediator中的主要功能(processNextRegistrationMessage):
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);
}
}
// ...
}
正如我所说的如果步骤1-3中发生任何异常,我想插入错误记录:
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;
}
这是SqlCommandHandlerServiceImpl.persist()方法:
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表中插入错误记录后,当事务想要提交时,我得到UnexpectedRollbackException。 我无法弄清楚我的问题在哪里:
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" />
提前致谢。
答案 0 :(得分:37)
这是正常行为,原因是您的sqlCommandHandlerService.persist
方法在执行时需要TX(因为它标有@Transactional
注释)。但是当它在processNextRegistrationMessage
内被调用时,因为有一个TX可用,所以容器不会创建新的并使用现有的TX。因此,如果sqlCommandHandlerService.persist
方法中发生任何异常,则会导致TX设置为rollBackOnly
(即使您在调用者中捕获异常并忽略它)。
要解决此问题,您可以使用传播级别进行交易。请查看this,了解哪种传播最符合您的要求。
在一位同事向我提出有关类似情况的几个问题之后,我觉得这需要一些澄清。
尽管传播解决了这些问题,但您应该非常小心使用它们,除非绝对明白它们的含义以及它们的工作原理,否则不要使用它们。你最终可能会持久保存一些数据并回滚其他一些你并不期望它们以这种方式工作的数据,而且事情可能会出现严重错误。
<小时/> 编辑 Link to current version of the documentation
答案 1 :(得分:3)
Shyam的答案是对的。我之前已经遇到过这个问题。这不是问题,它是一个SPRING功能。 “事务已回滚,因为它已被标记为仅回滚”是可以接受的。
<强> 结论 强>
让我解释一下更多细节:
问题:我们有多少笔交易? Anser:只有一个
因为你配置PROPAGATION是PROPAGATION_REQUIRED,所以@Transaction persist()使用与caller-processNextRegistrationMessage()相同的事务。实际上,当我们得到异常时,Spring会为TransactionManager设置 rollBackOnly ,因此Spring只会回滚一个Transaction。
问题:但是我们在外面尝试了一下(),为什么会发生这种异常呢? 答案由于独特的交易
转到外面的渔获物
Spring will set the rollBaclOnly to true -> it determine we must
rollback the caller (processNextRegistrationMessage) also.
persist()将首先回滚。
问题:为什么我们将PROPAGATION更改为REQUIRES_NEW,它有效吗?
Anser:因为现在processNextRegistrationMessage()和persist()在不同的事务中,所以他们只回滚他们的事务。
由于