최대 절전 모드-일괄 업데이트가 업데이트에서 예기치 않은 행 수를 반환했습니다. 0 실제 행 수 : 0 예상 : 1


141

최대 절전 모드 오류가 발생합니다. 문제를 일으키는 기능을 식별 할 수 있습니다. 불행히도 함수에는 여러 DB 호출이 있습니다. 최대 절전 모드에서 트랜잭션 종료시 세션을 플러시하므로 문제를 일으키는 줄을 찾을 수 없습니다. 아래에 언급 된 최대 절전 모드 오류는 일반적인 오류처럼 보입니다. 어떤 Bean이 문제를 일으키는 지 언급하지 않았습니다. 이 최대 절전 모드 오류에 익숙한 사람이 있습니까?

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
        at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:93)
        at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransacti
onManager.java:500)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManag
er.java:473)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(Transaction
AspectSupport.java:267)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)

@Peter Mortensen에게 감사합니다. 이메일을 업데이트했습니다.
Sujee

나도 같은 문제가있어. 매우 드물게 발생하기 때문에 큰 문제는 아닙니다. 이 동작을 재현하려면 수백만 개의 트랜잭션이 필요하므로 show_sql을 사용하는 것은 실용적이지 않습니다. 그러나 이것은 시스템 테스트 중에 반복적으로 발생하기 때문에 (거래가 많은 경우) 특정 이유가 있다고 생각합니다.
daniel_or_else

나는이 업데이트 행을 시도하는 동안이 문제가 발생 NO의 변경. 차이가 없으면 무언가를 업데이트하지 마십시오.
fifman

답변:


62

트랜잭션에 대한 코드 및 매핑이 없으면 문제를 조사하는 것이 불가능할 것입니다.

그러나 문제의 원인을 더 잘 처리하려면 다음을 시도하십시오.

  • 최대 절전 모드 구성에서 hibernate.show_sql을 true로 설정하십시오. 실행되고 문제를 일으키는 SQL이 표시됩니다.
  • Spring과 Hibernate에 대한 로그 레벨을 DEBUG로 설정하면 다시 어느 라인이 문제를 일으키는 지 더 잘 알 수 있습니다.
  • Spring에서 트랜잭션 관리자를 구성하지 않고 문제점을 복제하는 단위 테스트를 작성하십시오. 이렇게하면 문제가되는 코드 줄을 더 잘 이해할 수 있습니다.

희망이 도움이됩니다.


21
hibernate.show_sql> 로그 카테고리 org.hibernate.SQL 레벨을 DEBUG로 설정하는 것이 좋습니다. 이렇게하면 로깅을 위해 최대 절전 모드 구성을 수정할 필요가 없습니다.
Thierry

"show_sql"의 사용은 매우 장황 할 수 있으며 프로덕션에서는 실용적이지 않습니다. 이를 위해 BatchingBatcher 클래스의 74 행에서 catch 절을 수정하여 ps.toString ()으로 명령문을 인쇄하여 문제점이있는 명령문 만 갖도록했습니다.
Orden

71

전혀 존재하지 않는 ID로 레코드를 삭제하는 동안 동일한 예외가 발생했습니다. 따라서 업데이트 / 삭제중인 레코드가 실제로 DB에 존재하는지 확인하십시오.


12
부모-자식 관계에서 자식을 제거하고 부모 (자식을 삭제)를 저장 한 다음 자식을 수동으로 삭제하려고 할 때이 문제가 발생했습니다.
dave thieben

이것은 내 문제를 해결했습니다. 레코드가 존재하지 않고 서비스에서 실제로 createOrUpdateAll () 메소드를 호출해야하는 동안 updateAll () 메소드를 호출했습니다. 감사.
Mital Pritmani

3
문제를 해결하는 방법? 기록을 얻은 다음 삭제하면; 그러나 시스템이 삭제하기 전에 이미 시스템을 삭제하면 다른 애플리케이션에서 예외가 발생합니다.
Stony

@ davethieben, 이것은 내가 필요한 정보였습니다. 삭제시 부모-자식 관계가 지속되는 것을 몰랐습니다.
눈이 내림

해결책은 레코드를 가져와 행을 잠글 때 업데이트에 select를 사용하여 행을 잠그기 전에 다른 방법으로 삭제할 수 없다는 것입니다.
Matthew 읽기

55

솔루션 : id 속성에 대한 최대 절전 모드 매핑 파일에서 생성기 클래스를 사용하는 경우 해당 속성에 대해 setter 메서드를 사용하여 명시 적으로 값을 설정해서는 안됩니다.

Id 속성 값을 명시 적으로 설정하면 위의 오류가 발생합니다. 이 오류를 피하려면 이것을 확인하십시오. 또는 맵핑 파일에서 field generator = "native"또는 "incremental"필드를 언급하고 DATABASE에서 맵핑 된 테이블이 자동 증가하지 않습니다. 해결책 : DATABASE로 이동하여 테이블을 업데이트하여 auto_increment를 설정하십시오.


2
확실히 맞아. 정답이어야합니다. 감사합니다 @ Rēda Biramanē
Kuldeep Verma

1
그러나 ID 값을 '0'으로 설정하면 최대 절전 모드에서 자유롭게 덮어 쓸 수 있습니다
forresthopkinsa

1
감사! 왜 이것이 가장 높은 답변이 아닌지 잘 모르겠습니다.
Stuart McIntyre

18

일부 객체에 특정 ID를 할당 (테스트)하고 데이터베이스에 저장하려고 할 때 우연히 한 번 발생했습니다. 문제는 데이터베이스에 개체의 ID를 설정하기위한 특정 정책이 있다는 것입니다. 그냥 하지 않습니다 당신이 최대 절전 모드 수준에서 정책이있을 경우 ID를 할당합니다.


15

필자의 경우 비슷한 두 가지 경우 에이 예외가 발생했습니다.

  • 주석이 달린 방법에서 @Transactional 에서 다른 서비스에 대한 호출이있었습니다 (긴 응답 시간). 이 메소드는 엔티티의 일부 특성을 업데이트합니다 (메소드 후에 엔티티가 여전히 데이터베이스에 존재 함). 트랜잭션 메소드에서 두 번째 종료 할 때 사용자가 메소드를 두 번 요청하면 (처음으로 작동하지 않는다고 생각하는 경우) Hibernate는 트랜잭션 시작에서 상태를 이미 변경 한 엔티티를 업데이트하려고 시도합니다. 최대 절전 모드에서는 상태의 엔터티를 검색하고 동일한 엔터티를 찾았지만 첫 번째 요청으로 이미 변경되었으므로 엔터티를 업데이트 할 수 없으므로 예외가 발생합니다. GIT의 충돌과 같습니다.
  • 엔티티를 업데이트하는 자동 요청 (플랫폼 모니터링)과 몇 초 후에 수동 롤백이있었습니다. 그러나이 플랫폼은 이미 테스트 팀에서 사용하고 있습니다. 테스터가 자동 요청과 동일한 엔티티에서 테스트를 수행하면 (100 분의 1 밀리 초 이내) 예외가 발생합니다. 이전 경우와 마찬가지로, 두 번째 트랜잭션을 종료 할 때 이전에 가져온 엔티티가 이미 변경되었습니다.

결론 : 제 경우에는 코드에서 찾을 수있는 문제가 아닙니다. 이 예외는 Hibernate가 현재 트랜잭션 중에 데이터베이스에서 처음 가져온 엔티티가 변경된 것을 발견했을 때 발생 하므로 Hibernate가 엔티티의 올바른 버전을 알 수 없으므로 데이터베이스로 플러시 할 수 없습니다 : 현재 버전 처음에 트랜잭션 가져 오기; 또는 이미 데이터베이스에 저장된 것입니다.

해결 방법 : 문제를 해결하기 위해, 당신은 최대 절전 모드로 플레이해야합니다 의 LockMode 가장 귀하의 요구 사항에 맞는 하나를 찾을 수 있습니다.


안녕하세요, 도움을 주셔서 감사합니다. 오류를 제거하기 위해 결국 어떤 잠금 모드를 사용했는지 알려주십시오. 사용자가 실수로 두 번 클릭했기 때문에 API가 다른 밀리 초에 연속적으로 충돌 할 때 비슷한 문제에 직면하고 있습니다. 비관적 잠금은 성능을 저하시킵니다. 그래서 당신이 결국 무엇을 갔는지 알고 관심이 있습니까?
Madhur Bhaiya

1
문제가 발생한 지 오랜 시간이 걸렸으며 내가 배치 한 정확한 솔루션을 잘 기억하지 못하지만 LockMode.READ또는 이었습니다 LockMode.OPTIMISTIC.
Sergio Lema

13

방금이 문제가 발생하여 레코드를 삭제하고 나중에 Hibernate 트랜잭션에서 업데이트하려고한다는 것을 알았습니다.


9

이는 트리거가 행 수에 영향을주는 추가 DML (데이터 수정) 쿼리를 실행할 때 발생할 수 있습니다. 내 솔루션은 트리거의 맨 위에 다음을 추가하는 것이 었습니다.

SET NOCOUNT ON;

1
이 대답은 올바른 방향으로 나를 보냈습니다. 트리거가있는 레거시 데이터베이스를 사용하고 있었지만 운 좋게도 트리거가 코드로 수행 한 작업을 대체했기 때문에 삭제할 수있었습니다.
S. Baggy

2
+1 그것은 나의 문제이기도했다. 나는 행 개수 검사를 전환 할 @SQLInsert 주석을 사용하기 때문에 필요 PostgreSQL을 사용하던 : technology-ebay.de/the-teams/mobile-de/blog/...을
s1mm0t

NOCOUNT OFF *
G. Ciardini

7

나는 같은 문제에 직면했다. 코드는 테스트 환경에서 작동했습니다. 그러나 준비 환경에서는 작동하지 않았습니다.

org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 3; expected: 1

문제는 테이블이 DB 테이블 테스트에서 각 기본 키에 대해 단일 항목을 가지고 있다는 것입니다. 그러나 스테이징 DB에는 동일한 기본 키에 대한 여러 항목이있었습니다. (테이블 준비에 문제가있어 테이블에 기본 키 제약 조건이 없었으며 여러 항목이있었습니다.)

따라서 업데이트 작업을 할 때마다 실패합니다. 단일 레코드를 업데이트하려고 시도하고 업데이트 횟수를 1로 예상합니다. 그러나 동일한 기본 키에 대해 3 개의 레코드가 테이블에 있었으므로 결과 업데이트 횟수는 3입니다. 예상 업데이트 횟수와 실제 결과 업데이트 수가 일치하지 않기 때문에 예외가 발생하고 롤백됩니다.

그런 다음 기본 키가 중복되고 기본 키 제약 조건이 추가 된 모든 레코드를 제거했습니다. 잘 작동합니다.

Hibernate - Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1

실제 행 개수 : 0 // 업데이트를
업데이트 할 레코드가 없음을 의미합니다. 0 // 레코드를 찾지 못해 업데이트가 없음을 의미합니다
. 1 // db 테이블에 키가있는 레코드가 하나 이상 있어야합니다.

여기서 문제는 쿼리가 일부 키의 레코드를 업데이트하려고하지만 최대 절전 모드에서 키가있는 레코드를 찾지 못했다는 것입니다.


안녕 @ParagFlume, 나는 같은 오류 "배치 업데이트가 업데이트에서 예기치 않은 행 수를 반환했습니다 : 0 실제 행 수 : 0 예상 : 1"내 데이터에서 하나의 개체를 선택하려고하면 문제가 생산에만 존재합니다. 이 문제를 해결하는 방법에 대한 아이디어, 나는 위험한 상황에 처해 있습니다. 안부!
제임스

쿼리가 db에서 레코드를 찾지 못했다고 생각합니다. db에서 레코드를 하나 업데이트하려고합니다. 레코드가 실제로 DB에 존재하는지 수동 / 쿼리 브라우저를 확인할 수 있습니다.
ParagFlume

예, 레코드가 있지만 내 문제는 최대 절전 모드로 업데이트하려고하는 이유입니다. 데이터베이스에서 객체를 가져 오기 위해 선택 서비스를 사용하고 있습니다!
제임스

객체의 상태를 변경하려고 할 수 있습니다. 세션은 여전히 ​​열려 있습니다.
ParagFlume

서비스를 개발 한 사람이 아니기 때문에 어떻게이 behaivor를 확인할 수 있습니까?
James

5

줄리어스 업데이트가 자식이 삭제 된 개체에 발생했을 때에 이런 일이 발생했다. (아마도 전체 아버지 대상에 대한 업데이트가 필요했기 때문에 아버지가 가질 수있는 다른 업데이트와 함께 어린이를 삭제하고 아버지에 다시 삽입하는 것을 선호합니다 (새롭고 오래된 것은 중요하지 않음) 그래서 ...이 작업을 수행하려면 (자식을 childrenList.clear()통해 루프하지 말고 일부 childDAO.delete(childrenList.get(i).delete()))를 설정하고 각각을 삭제하여 자식을 (트랜잭션 내에서) 삭제하십시오.@OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)아버지 대상의 측면에. 그런 다음 아버지 (fatherDAO.update (father))를 업데이트하십시오. (모든 아버지 대상에 대해 반복) 결과적으로 어린이는 아버지와의 연결이 끊어지고 프레임 워크에 의해 고아로 제거됩니다.


5

우리가 일대 다 관계를 맺고있는이 문제에 직면했습니다.

마스터에 대한 최대 절전 모드 hbm 매핑 파일에서 유형 배열이 설정된 객체에 대해 추가 cascade="save-update"되었으며 정상적으로 작동했습니다.

이것이 없으면 기본적으로 최대 절전 모드는 존재하지 않는 레코드를 업데이트하려고 시도하여 대신 삽입합니다.


4

당신이하려고 할 때 또한 발생할 수 PRIMARY KEY .UPDATE


3

동일한 문제가 발생했으며 자동 증가 기본 키로 인해 이것이 발생할 수 있음을 확인했습니다. 이 문제를 해결하려면 데이터 세트에 자동 증분 값을 삽입하지 마십시오. 기본 키없이 데이터를 삽입하십시오.


3

이 오류를 얻는 또 다른 방법은 컬렉션에 null 항목이있는 경우입니다.


2

동일한 객체를 삭제 한 다음 동일한 객체를 다시 업데이트하려고하면 삭제 후 이것을 사용하십시오.

session.clear ();


2

내 ID가 Long이므로 뷰에서 값 0을 받고 있었으므로 데이터베이스에 저장하려고 할 때이 오류가 발생하여 id를 null로 설정하여 수정했습니다.


1

주석이 달린 메소드 내부에서 트랜잭션을 수동으로 시작하고 커밋 할 때이 문제가 발생했습니다 @Transactional. 활성 트랜잭션이 이미 존재하는지 감지하여 문제를 해결했습니다.

//Detect underlying transaction
if (session.getTransaction() != null && session.getTransaction().isActive()) {
    myTransaction = session.getTransaction();
    preExistingTransaction = true;
} else {
    myTransaction = session.beginTransaction();
}

그런 다음 Spring이 트랜잭션 커밋을 처리하도록 허용했습니다.

private void finishTransaction() {
    if (!preExistingTransaction) {
        try {
            tx.commit();
        } catch (HibernateException he) {
            if (tx != null) {
                tx.rollback();
            }
            log.error(he);
        } finally {
            if (newSessionOpened) {
                SessionFactoryUtils.closeSession(session);
                newSessionOpened = false;
                maxResults = 0;
            }
        }
    }
}

1

이는 JSF 관리 Bean을 다음과 같이 선언했을 때 발생합니다.

@RequestScoped;

다음과 같이 선언해야 할 때

@SessionScoped;

문안 인사;


1

데이터베이스에 존재하지 않는 ID로 객체를 업데이트하려고 할 때이 오류가 발생했습니다. 내 실수의 이유는 이름이 'id'인 속성을 클라이언트 측 JSON 표현에 수동으로 할당 한 다음 서버 측에서 객체를 직렬화 해제하면이 'id'속성이 인스턴스 변수를 덮어 썼기 때문입니다 ( Hibernate가 생성하기로 한 'id'라고도 함). 따라서 최대 절전 모드를 사용하여 식별자를 생성하는 경우 이름 충돌을주의하십시오.


1

나는 또한 같은 도전에 직면했다. 제 경우에는을 사용하여 존재하지 않는 객체를 업데이트하고있었습니다 hibernateTemplate.

실제로 내 응용 프로그램에서 업데이트 할 DB 객체를 얻었습니다. 그리고 값을 업데이트하는 동안 실수로 ID를 업데이트하고 업데이트를 진행하여 해당 오류가 발생했습니다.

hibernateTemplateCRUD 작업에 사용 하고 있습니다.


1

모든 답변을 읽은 후에는 동면의 역 속성에 대해 이야기 할 사람을 찾지 못했습니다.

내 의견으로는 당신은 관계 키워드에서 키워드가 적절하게 설정 되어 있는지 확인해야합니다 . 키워드는 관계를 유지할 소유자를 정의합니다. 업데이트 및 삽입 절차는이 속성에 따라 다릅니다.

두 개의 테이블이 있다고 가정 해 봅시다.

principal_table , middle_table

일대 다 관계 . 동면 매핑 클래스는 각각 프린시 펄미들 입니다.

따라서 Principal 클래스에는 Middle 개체 집합이 있습니다. xml 매핑 파일은 다음과 같아야합니다.

<hibernate-mapping>
    <class name="path.to.class.Principal" table="principal_table" ...>
    ...
    <set name="middleObjects" table="middle_table" inverse="true" fetch="select">
        <key>
            <column name="PRINCIPAL_ID" not-null="true" />
        </key>
        <one-to-many class="path.to.class.Middel" />
    </set>
    ...

inverse가 "true"로 설정되면 "중간"클래스가 관계 소유자이므로 주체 클래스는 업데이트되지 않습니다. 관계를 .

따라서 업데이트 절차는 다음과 같이 구현할 수 있습니다.

session.beginTransaction();

Principal principal = new Principal();
principal.setSomething("1");
principal.setSomethingElse("2");


Middle middleObject = new Middle();
middleObject.setSomething("1");

middleObject.setPrincipal(principal);
principal.getMiddleObjects().add(middleObject);

session.saveOrUpdate(principal);
session.saveOrUpdate(middleObject); // NOTICE: you will need to save it manually

session.getTransaction().commit();

이것은 나를 위해 일했지만 솔루션을 개선하기 위해 일부 에디션을 제안 할 수 있습니다. 그렇게하면 우리 모두가 배우게 될 것입니다.


1

우리의 경우 마침내 StaleStateException의 근본 원인을 발견했습니다.

실제로 우리는 단일 최대 절전 모드 세션에서 행을 두 번 삭제했습니다. 이전에는 ojdbc6 lib를 사용했으며이 버전에서는 문제가 없었습니다.

그러나 odjc7 또는 ojdbc8로 업그레이드했을 때 레코드를 두 번 삭제하면 예외가 발생했습니다. 코드에 두 번 삭제하는 버그가 있었지만 ojdbc6에서는 분명하지 않았습니다.

이 코드 조각으로 재현 할 수있었습니다 :

Detail detail = getDetail(Long.valueOf(1396451));
session.delete(detail);
session.flush();
session.delete(detail);
session.flush();

처음에는 최대 절전 모드에서 데이터베이스가 변경됩니다. 두 번째 플러시 동면 동안 최대 절전 모드는 세션의 객체를 실제 테이블의 레코드와 비교하지만 찾을 수 없으므로 예외입니다.


1

이 문제는 주로 실행중인 세션에 의해 이미 메모리로 가져온 객체를 저장하거나 업데이트하려고 할 때 발생합니다. 세션에서 객체를 가져 왔고 데이터베이스에서 업데이트하려고하면이 예외가 발생할 수 있습니다.

나는 session.evict ()를 사용했다; 최대 절전 모드로 저장된 캐시를 먼저 제거하거나 데이터를 잃어 버릴 위험이 없다면 데이터 임시 저장을 위해 다른 객체를 만드는 것이 좋습니다.

     try
    {
        if(!session.isOpen())
        {
            session=EmployeyDao.getSessionFactory().openSession();
        }
            tx=session.beginTransaction();

        session.evict(e);
        session.saveOrUpdate(e);
        tx.commit();;
        EmployeyDao.shutDown(session);
    }
    catch(HibernateException exc)
    {
        exc.printStackTrace();
        tx.rollback();
    }

1

최대 절전 모드 5.4.1 및 HHH-12878 문제

최대 절전 모드 5.4.1 이전에는 낙관적 잠금 실패 예외 (예 : StaleStateException또는 OptimisticLockException)에 실패한 명령문이 포함되지 않았습니다.

HHH-12878 낙관적 잠금 예외가 발생하면, JDBC의 있도록 문제는 최대 절전 모드를 개선하기 위해 만들어졌습니다 PreparedStatement구현이 아니라 기록됩니다

if ( expectedRowCount > rowCount ) {
    throw new StaleStateException(
            "Batch update returned unexpected row count from update ["
                    + batchPosition + "]; actual row count: " + rowCount
                    + "; expected: " + expectedRowCount + "; statement executed: "
                    + statement
    );
}

테스트 시간

BatchingOptimisticLockingTest고성능 Java Persistence GitHub 리포지토리에서 새 동작의 작동 방식을 보여주기 위해를 만들었습니다 .

먼저 속성 Post을 정의하는 엔티티를 정의 하여 암시적인 낙관적 잠금 메커니즘을@Version 활성화 합니다 .

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String title;

    @Version
    private short version;

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public short getVersion() {
        return version;
    }
}

다음 3 가지 구성 속성을 사용하여 JDBC 일괄 처리를 활성화합니다.

properties.put("hibernate.jdbc.batch_size", "5");
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");

우리는 3 개의 Post엔티티 를 만들 것입니다 :

doInJPA(entityManager -> {
    for (int i = 1; i <= 3; i++) {
        entityManager.persist(
            new Post()
                .setTitle(String.format("Post no. %d", i))
        );
    }
});

그리고 Hibernate는 JDBC 배치 삽입을 실행할 것이다 :

SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')

Query: [
    INSERT INTO post (title, version, id) 
    VALUES (?, ?, ?)
], 
Params:[
    (Post no. 1, 0, 1), 
    (Post no. 2, 0, 2), 
    (Post no. 3, 0, 3)
]

따라서 JDBC 일괄 처리가 제대로 작동한다는 것을 알고 있습니다.

이제 낙관적 잠금 문제를 재현 해 보겠습니다.

doInJPA(entityManager -> {
    List<Post> posts = entityManager.createQuery("""
        select p 
        from Post p
        """, Post.class)
    .getResultList();

    posts.forEach(
        post -> post.setTitle(
            post.getTitle() + " - 2nd edition"
        )
    );

    executeSync(
        () -> doInJPA(_entityManager -> {
            Post post = _entityManager.createQuery("""
                select p 
                from Post p
                order by p.id
                """, Post.class)
            .setMaxResults(1)
            .getSingleResult();

            post.setTitle(post.getTitle() + " - corrected");
        })
    );
});

첫 번째 트랜잭션은 모든 Post엔터티를 선택 하고 title속성을 수정합니다 .

그러나 첫 번째 EntityManager가 플러시 되기 전에 executeSync메소드를 사용하여 두 번째 전이를 실행하려고합니다 .

두 번째 거래는 첫 번째 거래를 수정합니다 Post 하므로 version증가합니다.

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - corrected', 1, 1, 0)
]

이제 첫 번째 트랜잭션이 EntityManager 과 같이됩니다 OptimisticLockException.

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - 2nd edition', 1, 1, 0), 
    ('Post no. 2 - 2nd edition', 1, 2, 0), 
    ('Post no. 3 - 2nd edition', 1, 3, 0)
]

o.h.e.j.b.i.AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements

o.h.e.j.b.i.BatchingBatch - HHH000315: Exception executing batch [
    org.hibernate.StaleStateException: 
    Batch update returned unexpected row count from update [0]; 
    actual row count: 0; 
    expected: 1; 
    statement executed: 
        PgPreparedStatement [
            update post set title='Post no. 3 - 2nd edition', version=1 where id=3 and version=0
        ]
], 
SQL: update post set title=?, version=? where id=? and version=?

따라서이 개선의 혜택을 받으려면 Hibernate 5.4.1 이상으로 업그레이드해야합니다.


0

네이티브 SQL 쿼리를 사용하여 데이터 세트에서 무언가를 변경했지만 동일한 데이터 세트에 대해 지속 된 오브젝트가 세션 캐시에있는 경우에 발생했습니다. session.evict (yourObject)를 사용하십시오.



0

사례 중 하나

SessionFactory sf=new Configuration().configure().buildSessionFactory();
Session session=sf.openSession();

UserDetails user=new UserDetails();

session.beginTransaction();
user.setUserName("update user agian");
user.setUserId(12);
session.saveOrUpdate(user);
session.getTransaction().commit();
System.out.println("user::"+user.getUserName());

sf.close();

0

나는이 예외에 직면하고 있었고 최대 절전 모드가 잘 작동했습니다. pgAdmin을 사용하여 하나의 레코드를 수동으로 삽입하려고 시도했지만 문제가 명확 해졌습니다. SQL 삽입 쿼리는 0 삽입을 반환합니다. 그리고 null을 반환하기 때문에이 문제를 일으키는 트리거 함수가 있습니다. 그래서 새로 반환하도록 설정해야합니다. 마침내 나는 문제를 해결했다.

그것이 어떤 신체에도 도움이되기를 바랍니다.


0

잘못 ID사용 하여 열을 매핑했기 때문에이 오류가 발생했습니다.Id(x => x.Id, "id").GeneratedBy.**Assigned**();

를 사용하여 해결 된 문제 Id(x => x.Id, "id").GeneratedBy.**Identity**();



0

필자의 경우 Stored Procs 중 하나가 모든 CPU를 소비하여 DB 응답 시간이 길어 데이터베이스에 문제가있었습니다. 이 문제가 해결되면 문제가 해결되었습니다.

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