롤백으로 만 표시된 트랜잭션 : 원인을 어떻게 찾습니까?


93

내 @Transactional 메서드 내에서 트랜잭션을 커밋하는 데 문제가 있습니다.

methodA() {
    methodB()
}

@Transactional
methodB() {
    ...
    em.persist();
    ...
    em.flush();
    log("OK");
}

methodA ()에서 methodB ()를 호출하면 메서드가 성공적으로 전달되고 로그에 "OK"가 표시됩니다. 하지만 나는

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    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 methodA()...
  1. methodB의 컨텍스트는 예외에서 완전히 누락되어 있습니다.
  2. methodB () 내의 무언가가 트랜잭션을 롤백으로 만 표시 했습니까? 어떻게 알 수 있습니까? 예를 들어 다음과 같은 것을 확인하는 방법이 있습니까 getCurrentTransaction().isRollbackOnly()?-이와 같이 방법을 단계별로 수행하고 원인을 찾을 수 있습니다.



주목해야 할 흥미로운 점은 데이터베이스 테이블이 존재하지 않으면 언젠가이 오류도 표시된다는 것입니다.
Ng Sek Long

답변:


101

당신은 귀하의 방법을 표시하면 @Transactional, 당신의 방법 내부 예외의 발생은 롤백 전용 (당신이 그들을 잡을 경우에도)로 주변 TX를 표시합니다. @Transactional주석의 다른 속성을 사용 하여 다음과 같은 롤백을 방지 할 수 있습니다 .

@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)

6
글쎄,을 사용하려고했지만 noRollbackFor=Exception.class효과가없는 것 같습니다. 상속 된 예외에 대해 작동합니까?
Vojtěch

6
네 그렇습니다. 자신의 답변을 보면 맞습니다 ( methodC첫 번째 게시물에서 제공하지 않았습니다 ). 모두 methodBmethodC같은 TX를 사용하여 항상 가장 특정의 @Transactional너무 때, 주석이 사용됩니다 methodCTX가 롤백 전용으로 표시됩니다 주변 예외가 발생합니다. 이를 방지하기 위해 다른 전파 마커를 사용할 수도 있습니다.
Ean V

@Ean 메서드 내부의 예외는 주변 TX를 롤백 전용으로 표시합니다. 이것은 읽기 전용 트랜잭션에도 적용됩니까?
Marko Vranjkovic 2014-06-11

1
@lolotron @Ean 실제로 읽기 전용 트랜잭션에 적용될 것임을 확인할 수 있습니다. 내 방법은 EmptyResultDataAccessException읽기 전용 트랜잭션 에서 예외를 throw 하고 동일한 오류가 발생했습니다. 내 주석을 변경 @Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)하여 문제를 해결했습니다.
cbmeeks

5
이 대답은 틀 렸습니다. Spring은 @Transactional프록시 래퍼 를 통과하는 예외 , 즉 uncaught 에 대해서만 알고 있습니다. 전체 이야기는 Vojtěch의 다른 답변을 참조하십시오. @Transactional트랜잭션 롤백 전용으로 표시 할 수있는 중첩 된 메서드 가있을 수 있습니다 .
Yaroslav Stavnichiy

68

마침내 문제를 이해했습니다.

methodA() {
    methodB()
}

@Transactional(noRollbackFor = Exception.class)
methodB() {
    ...
    try {
        methodC()
    } catch (...) {...}
    log("OK");
}

@Transactional
methodC() {
    throw new ...();
}

무슨 일이 일어나는 methodB가는에 올바른 주석이 있어도 methodC그렇지 않다는 것입니다. 예외가 발생하면 두 번째 @Transactional는 첫 번째 트랜잭션을 어쨌든 롤백으로 만 표시합니다.


5
트랜잭션의 상태는 스레드 로컬 변수에 저장됩니다. 스프링이 methodC를 가로 채고 플래그를 rollback으로 설정하면 트랜잭션이 이미 롤백으로 표시됩니다. 마지막이 발생 커밋 할 때 도움이, 당신이 오류를 얻을 수 없기 때문에 예외의 더 이상의 억제
생활

@ Vojtěch 어떤 방법 가설 경우 methodC는propagation=requires_new다음 methodB 롤백하지 않습니다?
deFreitas

4
methodC다른 Spring 빈 / 서비스에 있거나 어떻게 든 Spring 프록시를 통해 액세스해야합니다. 그렇지 않으면 Spring은 예외에 대해 알 가능성이 없습니다. @Transactional어노테이션 을 통과하는 예외 만 트랜잭션을 롤백 전용으로 표시 할 수 있습니다.
Yaroslav Stavnichiy

43

다시 코딩하거나 다시 빌드 할 필요없이 원인이되는 예외를 빠르게 가져 오려면 중단 점을

org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3

스택에서 보통 인터셉터로 올라갑니다. 거기에서 일부 catch 블록에서 발생하는 예외를 읽을 수 있습니다.


6
최대 절전 모드 4.3.11, 그것은이다org.hibernate.jpa.internal.TransactionImpl
빔 Deblauwe

아주 좋은 친구!
Rafael Andrade

감사! 최신 버전의 Hibernate (5.4.17)에서 클래스는 org.hibernate.engine.transaction.internal.TransactionImpl이고 메서드는 setRollbackOnly.
Peter Catalin

11

내 응용 프로그램을 실행하는 동안이 예외로 고생했습니다.

마지막으로 문제는 SQL 쿼리에있었습니다. 있었습니다. 나는 쿼리가 잘못되었음을 의미합니다.

쿼리를 확인하십시오. 이것은 내 제안입니다


1
명확히하기 위해 : 1. SQL 구문에 오류가있는 경우 2. 예외에 대해 롤백하도록 설정되어있는 경우 3. readOnly 트랜잭션이있는 경우 SQL 구문이 "롤백을 트리거하는 예외를 유발하기 때문에이 오류가 발생합니다. 읽기 전용 "모드.
Dave

7

던져지고 잡히는 예외를 찾으십시오. ...코드 섹션 . 런타임 및 롤백 애플리케이션 예외로 인해 다른 장소에서 발견 된 경우에도 비즈니스 메소드에서 폐기 될 때 롤백이 발생합니다.

컨텍스트를 사용하여 트랜잭션이 롤백으로 표시되었는지 여부를 확인할 수 있습니다.

@Resource
private SessionContext context;

context.getRollbackOnly();

1
나는 원인을 찾은 것 같지만 왜 이런 일이 발생하는지 이해하지 못합니다. 내부 메서드는 예외를 throw합니다. 예외를 잡아서 기록하고 무시합니다. 그러나 트랜잭션은 어쨌든 롤백으로 표시됩니다. 어떻게 예방할 수 있습니까? 트랜잭션이 내가 제대로 포착 한 예외의 영향을받는 것을 원하지 않습니다.
Vojtěch

SessionContext봄의 표준 클래스는? 나에게 그것은 오히려 EJB3이고 Spring Application에 포함되어 있지 않은 것 같습니다.
Vojtěch

3
나의 나쁜 나는 그것이 봄에 관한 사실을 놓쳤다. 어쨌든 TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()사용 가능한 것과 같은 것이 있어야합니다 .
Mareen

2

솔루션으로 좋은 설명을 찾았습니다 : https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1) 실제로 트랜잭션 제어가 필요하지 않은 경우 중첩 된 메서드에서 @Transacional을 제거합니다. 따라서 예외가 있어도 거품이 발생하고 트랜잭션에 영향을 미치지 않습니다.

또는:

2) 중첩 된 메서드에 트랜잭션 제어가 필요한 경우 전파 정책에 대해 REQUIRE_NEW로 설정하여 예외를 throw하고 롤백 전용으로 표시하더라도 호출자에게 영향을주지 않습니다.


1

Bean.xml에서 transactionmanager를 비활성화하십시오.

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

이 줄을 주석 처리하면 롤백을 유발하는 예외가 표시됩니다.)


0

productRepository에 아래 코드를 적용하십시오.

@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);

junit 테스트에서 코드 아래에 적용

@Test
public void updateData()
{
  int i=productRepository.updateMyData("Iphone",102);

  System.out.println("successfully updated ... ");
  assertTrue(i!=0);

}

내 코드에서 잘 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.