예기치 않은 롤백예외:트랜잭션이 롤백 전용으로 표시되어 롤백되었습니다.
저는 다음과 같은 시나리오를 가지고 있습니다.
- 수신 메시지 테이블에서 레코드 가져오기(읽기 및 삭제)
- 레코드 내용 읽기
- 어떤 테이블에 무엇을 끼우다
- 1-3단계에서 오류(예외)가 발생한 경우 Excuting Message 테이블에 error-record를 삽입합니다.
- 그렇지 않으면 Excuting Message 테이블에 성공 레코드를 삽입합니다.
따라서 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;
}
Sql CommandHandler Service Impl입니다.지속 () 메서드:
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);
}
}
// ...
}
하지만 sql CommandHandler Service의 경우.persist () throws 예외(여기서는 org. hibernate).예외.제약 조건 위반예외), OutgoingMessage 테이블에 오류 레코드를 삽입한 후 트랜잭션을 커밋하려는 경우 예기치 않은 롤백이 표시됩니다.예외.어디가 문제인지 알 수가 없네요.
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 x.y.z.ReceiveMessagesJob$$EnhancerByCGLIB$$63524c6b.run(<generated>)
at x.y.z.JobScheduler$ScheduledJobThread.run(JobScheduler.java:132)
hibernate-4.1.0-Final을 사용하고 있으며 데이터베이스는 Oracle이며 트랜잭션 관리자는 다음과 같습니다.
<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" />
그의 입니다.sqlCommandHandlerService.persist
때때 TX다입니다)).@Transactional
주석). 그러나 그것이 안으로 불려질 때라고 불릴 때.processNextRegistrationMessage
사용 는 새 합니다.한 TX TX 를 TX 를합니다 TX 를합니다.한다면,sqlCommandHandlerService.persist
하면 TX가드, TX다로 rollBackOnly
(발신자에서 예외를 포착하고 무시하더라도)
이를 극복하기 위해 트랜잭션에 전파 수준을 사용할 수 있습니다.요구 사항에 가장 적합한 전파 방법을 알아보려면 이 항목을 살펴보십시오.
업데이트; 읽어보세요!
한 동료가 비슷한 상황에 대해 몇 가지 질문을 하고 나서, 저는 이것에 대해 좀 더 해명이 필요하다고 생각합니다.
비록 전파가 그러한 문제들을 해결하지만, 여러분은 그것들을 사용하는 것에 매우 조심해야 하고, 그것들이 의미하는 것과 그것들이 어떻게 작용하는지를 절대적으로 이해하지 않는 한 그것들을 사용하지 말아야 합니다.데이터가 그런 식으로 작동하지 않을 것으로 예상되는 일부 데이터를 유지하고 다른 데이터를 롤백하게 되어 일이 크게 잘못될 수 있습니다.
편집 현재 버전의 설명서 링크
Shyam의 대답이 옳았습니다.저는 이미 전에 이 문제에 직면했습니다.그건 문제가 아니라 스프링 기능입니다."롤백 전용으로 표시되었기 때문에 트랜잭션 롤백"을 사용할 수 있습니다.
결론
- 예외 이전에 수행한 작업을 커밋하려면 USE REQUIRES_NEW(로컬 커밋)
- 모든 프로세스가 완료된 경우에만 커밋하려면 Use REQUIRED(사용 필수) 그리고 "Transaction rollback(롤백 전용으로 표시되었기 때문에 트랜잭션 롤백)" 예외만 무시하면 됩니다.그러나 의미 로그를 가지려면 호출자 프로세스NextRegistrationMessage() 밖에서 시도해봐야 합니다.
좀 더 자세히 설명해 드리겠습니다.
질문:.거래가 얼마나 됩니까?답변:단 하나.
@Transaction persist()이 caller-processNextRegistrationMessage()와 동일한 트랜잭션을 사용하도록 PROPATION을 구성하기 때문에 PROPATION은 PROPATION_REQUARED입니다.실제로 예외가 발생하면 Spring에서 트랜잭션 관리자에 대해 rollBackOnly를 설정하여 Spring에서 트랜잭션 하나만 롤백합니다.
질문:.하지만 외부에서 시도해 볼 수 있는 기회가 있습니다. () 왜 이런 예외가 발생하는 것일까요?고유한 트랜잭션으로 인한 답변
- persist() 메서드에 예외가 있는 경우
밖에 있는 잡이로 가세요.
Spring will set the rollBackOnly to true -> it determine we must rollback the caller (processNextRegistrationMessage) also.
persist()가 먼저 롤백합니다.
- 예기치 않은 롤백 던지기그 사실을 알리기 위해서는 발신자도 롤백해야 합니다.
- run()에서 Try-catch가 Unexpected Rollback을 잡습니다.스택 트레이스를 예외적으로 인쇄합니다.
질문:.전파를 REQUIRES_NEW로 변경하는 이유는 무엇입니까?
답변: 이제 프로세스 NextRegistrationMessage()와 persist()가 다른 트랜잭션에 있으므로 트랜잭션만 롤백하기 때문입니다.
감사해요.
언급URL : https://stackoverflow.com/questions/19349898/unexpectedrollbackexception-transaction-rolled-back-because-it-has-been-marked
'itsource' 카테고리의 다른 글
오류: window powershell을 사용하여 새 각도 프로젝트를 만들 때 "운영 체제에서 작업이 거부되었습니다." (0) | 2023.09.19 |
---|---|
숫자 배열이 '\0' 또는 null 문자로 끝나지 않는 이유는 무엇입니까? (0) | 2023.09.19 |
MySQL에서 병합 문을 사용할 수 있는지 여부 (0) | 2023.09.19 |
인터넷 익스플로러 8의 jQuery 문제 (0) | 2023.09.19 |
워드프레스에서 부모 페이지 ID와 현재 페이지의 제목을 얻는 방법은? (0) | 2023.09.19 |