source

휴지 상태 - 배치 업데이트에서 업데이트에서 예기치 않은 행 수가 반환되었습니다: 0 실제 행 수: 0 예상: 1

factcode 2023. 1. 19. 20:58
반응형

휴지 상태 - 배치 업데이트에서 업데이트에서 예기치 않은 행 수가 반환되었습니다: 0 실제 행 수: 0 예상: 1

휴지 상태 에러가 표시된다.문제의 원인이 되는 기능을 특정할 수 있습니다.안타깝게도 함수에 몇 개의 DB 호출이 있습니다.트랜잭션 종료 시 휴지 상태가 되면 세션이 플러시되므로 문제의 원인이 되는 라인을 찾을 수 없습니다.다음에 나타내는 휴지 상태의 에러는, 일반적인 에러와 같습니다.어떤 빈이 문제의 원인인지조차 언급되지 않았다.이 휴지 상태 오류에 익숙한 사람?

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)

전혀 존재하지 않는 ID별 레코드를 삭제할 때도 같은 예외가 발생하였습니다.따라서 업데이트/삭제 중인 레코드가 실제로 DB에 있는지 확인하십시오.

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

다만, 문제의 원인을 보다 정확하게 파악하려면 , 다음의 조작을 실시해 주세요.

  • 휴지 상태 구성에서 휴지 상태를 설정합니다.show_sql을 true로 설정합니다.실행되어 문제의 원인이 되고 있는SQL이 표시됩니다.
  • [ Spring ](스프링)과 [Hibernate](휴면 상태)의 로그 레벨을 [DEBUG](디버깅)으로 설정합니다.이것으로, 문제의 원인이 되고 있는 행이 특정됩니다.
  • 봄에 트랜잭션 매니저를 설정하지 않고 문제를 재현하는 유닛테스트를 만듭니다.이렇게 하면 문제의 코드 행을 더 잘 알 수 있을 것입니다.

도움이 됐으면 좋겠다.

솔루션:id 속성의 휴지 상태 매핑 파일에서 제너레이터 클래스를 사용하는 경우 해당 속성의 값을 설정 방법을 사용하여 명시적으로 설정하지 마십시오.

ID 속성 값을 명시적으로 설정하면 위의 오류가 발생합니다.이 에러를 회피하려면 , 이것을 체크해 주세요.또는 매핑 파일에서 generator="field" 또는 "incremental" 필드를 언급하고 데이터베이스에서 매핑된 테이블이 auto_incremented 솔루션이 아닌 경우 오류가 표시됩니다.데이터베이스로 이동하여 테이블을 업데이트하여 auto_increment를 설정합니다.

제 경우, 이 예외는 다음과 같은 두 가지 경우입니다.

  • 「이러한 방법」으로 주석을 단 .@Transactional다른 서비스에 전화를 걸었습니다(응답 시간이 길었습니다).메서드는 엔티티의 일부 속성을 업데이트합니다(메서드 이후에도 엔티티는 데이터베이스에 계속 존재합니다).사용자가 트랜잭션 메서드를 두 번째로 종료할 때 메서드의 2배(처음에는 동작하지 않는다고 생각되는 것)를 요구하면 휴지 상태는 트랜잭션 시작부터 이미 변경된 엔티티의 갱신을 시도합니다.휴지 상태에서는 엔티티를 검색하여 동일한 엔티티를 찾았지만 첫 번째 요청에 의해 이미 변경되었기 때문에 엔티티를 갱신할 수 없기 때문에 예외를 발생시킵니다.의 GIT の 의같 같같 같같 같같 。
  • 엔티티를 갱신하는 자동 요구(플랫폼 감시용)가 있었습니다(수동 롤백은 몇 초 후).하지만 이 플랫폼은 이미 테스트 팀에 의해 사용되고 있습니다.테스터가 자동 요구와 동일한 엔티티에서 테스트를 수행할 때(밀리초의 100분의 1 이내) 예외가 발생합니다.앞의 경우와 마찬가지로 두 번째 트랜잭션을 종료할 때 이전에 가져온 엔티티가 이미 변경되었습니다.

결론: 저 같은 경우에는 코드에서 찾을 수 있는 문제가 아니었습니다.이 예외는 Hibernate가 현재 트랜잭션 중에 엔티티가 데이터베이스에서 처음 가져온 것을 발견했을 때 발생합니다. 따라서 Hibernate는 엔티티의 올바른 버전(현재 트랜잭션이 처음에 가져온 버전 또는 데이터베이스에 이미 저장된 버전)을 알 수 없기 때문에 데이터베이스에 해당 엔티티를 플러시할 수 없습니다.

해결책: 이 문제를 해결하려면 Hibernate Lock Mode를 사용하여 요건에 가장 적합한 것을 찾아야 합니다.

특정 ID를 일부 오브젝트에 할당(테스트 중)하여 데이터베이스에 저장하려고 할 때 우연히 이 문제가 발생하였습니다.문제는 데이터베이스에 객체의 ID를 설정하기 위한 특정 정책이 있다는 것입니다.휴지 상태 수준의 정책이 있는 경우 ID를 할당하지 마십시오.

방금 이 문제가 발생하여 휴지 상태 트랜잭션에서 레코드를 삭제하고 나중에 업데이트를 시도하고 있음을 알게 되었습니다.

휴지 상태 5.4.1 및 HH-12878 문제

1 장애 "5.4.1")가 있었습니다.StaleStateException ★★★★★★★★★★★★★★★★★」OptimisticLockException에는, 불합격의 스테이트먼트는 포함되지 않았습니다.

HH-12878의 문제는 Hibernate를 개선하기 위해 작성되었습니다.이것에 의해, 낙관적인 락의 예외가 발생했을 때에, 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
    );
}

테스트 시간

새로운 동작이 어떻게 동작하는지 보여주기 위해 고성능 Java 퍼시스텐스 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))
        );
    }
});

휴지 상태에서는 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 있습니다.두 됩니다.이 2 2 2, 번번번번번번번번번번번번번번.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 이상으로 업그레이드해야 이 개선의 혜택을 누릴 수 있습니다.

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

SET NOCOUNT ON;

나도 같은 문제에 직면해 있었다.코드는 테스트 환경에서 동작하고 있었습니다.하지만 그것은 스테이징 환경에서는 작동하지 않았다.

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 // 는 갱신할 수 없습니다.
update://는 수 : 0 //는 갱신되지 않습니다.
expected //는table에 되는 것을 합니다.expected: 1 //db db 1은 1은 1은 1은 1은 1의 레코드입니다.

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

하려고 할 도 있어요.UPDATE프라이머리

Julius가 말한 처럼, 이것은 아이가 삭제되고 있는 오브젝트에서 갱신이 발생했을 때 발생합니다.(아마도 Father 오브젝트 전체에 대한 갱신이 필요했고, 때때로 우리는 자녀들을 삭제하고 Father에 다시 삽입하는 것을 선호하기 때문일 것입니다.(새로운, 오래된 것은 중요하지 않습니다) 아버지가 가지고 있을 수 있는 다른 갱신과 함께 Father에 재삽입합니다.이 작업을 수행하려면 (트랜잭션 내의) 하위 필드를 호출하여 삭제합니다.childrenList.clear()을 실행한 후 일부(Dont loop)를 삭제합니다.childDAO.delete(childrenList.get(i).delete())) 설정@OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)아버지 물체의 측면에 있습니다.하게 .그 결과 아이들은 아버지와의 연결고리가 벗겨지고 그 틀에 의해 고아로 전락하게 된다.

나는 우리가 일대일로 관계를 맺었던 이 문제에 직면했다.

마스터용 오브젝트에 hbm set type 배열이 hbm으로 .cascade="save-update"잘 작동했어요

이렇게 하지 않으면 기본적으로 휴지 상태가 존재하지 않는 레코드의 갱신을 시도하고 대신 레코드를 삽입합니다.

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

같은 문제가 발생했는데 Auto increment primary key 때문에 발생할 수 있음을 확인했습니다.이 문제를 해결하려면 데이터 세트에 자동 증가 값을 삽입하지 마십시오.기본 키 없이 데이터를 삽입합니다.

하려고 할 때 현상입니다.delete 을 그 update같은 오브젝트다음에 사용delete:

session.clear();

저도 같은 일이 있었습니다.왜냐하면 제 ID가 Long이고 뷰에서 값 0을 받고 있는데 데이터베이스에 저장하려고 하면 이 오류가 발생하여 ID를 null로 설정하여 수정했습니다.

이 문제는 주로 실행 중인 세션에서 이미 메모리에 가져온 개체를 저장 또는 업데이트하려고 할 때 발생합니다.세션에서 개체를 가져와서 데이터베이스에서 업데이트하려는 경우 이 예외가 발생할 수 있습니다.

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();
    }

는 수동으로 하여 메서드 때 하였습니다.메서드 내에서 "이러한 트랜잭션을 커밋하다"라고 을 달았습니다.@Transactional활성 트랜잭션이 이미 존재하는지 감지하여 문제를 해결했습니다.

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

그리고 스프링에게 거래 커밋을 맡겼다.

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;
            }
        }
    }
}

이 문제는 JSF Managed Bean을 다음과 같이 선언했을 때 발생합니다.

@RequestScoped;

라고 선언해야 할 때

@SessionScoped;

경위

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

저도 같은 도전에 부딪혔어요..hibernateTemplate

실제로 어플리케이션에서 업데이트할 DB 오브젝트를 가져오고 있었습니다.그리고 값을 갱신하는 중에 실수로 아이디도 갱신하고, 업데이트를 진행하다가 오류가 발생하였습니다.

하고 .hibernateTemplateCRUD를 사용하다

모든 답을 읽은 후, 동면기의 역속성에 대해 이야기할 사람을 찾을 수 없었다.

내 생각에 당신은 또한 관계 매핑에서 역키워드가 적절하게 설정되어 있는지 검증해야 한다.Inverse 키워드가 작성되어 관계를 유지할 소유자가 어느 쪽인지 정의합니다.업데이트 및 삽입 절차는 이 속성에 따라 달라집니다.

두 개의 테이블이 있다고 가정합니다.

주요 테이블, 중간 테이블

1대 다의 관계를 가지고 있습니다.휴지 상태 매핑 클래스는 각각 Principle과 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>
    ...

반전이 "true"로 설정되어 있으므로 "Middle" 클래스가 관계 소유자임을 의미하므로 주요 클래스는 관계를 업데이트하지 않습니다.

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

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();

이 방법은 효과가 있었습니다만, 솔루션을 개선하기 위해 몇 가지 에디션을 제안해 주실 수 있습니다.그래야 우리 모두가 배울 수 있다.

이 예에서는 StaleStateException의 근본 원인을 최종적으로 밝혀냈습니다.

실제로 단일 휴지 세션에서 행을 두 번 삭제했습니다.아까 ojdbc6 lib를 사용했는데 이 버전에서는 문제가 없었습니다.

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

이 코드로 재현할 수 있었습니다.

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

첫 번째 플러시 시 휴지 상태가 시작되고 데이터베이스가 변경됩니다.두 번째 플러시 동안 휴지 상태에서는 세션의 개체를 실제 테이블의 레코드와 비교하지만 찾을 수 없으므로 예외가 발생합니다.

내가 풀었어.테이블에 내 ID 열의 기본 키가 없습니다.내가 그것을 만들어내면 나를 위해 해결된다.또한 테이블에서 중복된 ID가 발견되어 이전에 삭제하고 해결했습니다.

이 문제는 네이티브 SQL 쿼리를 사용하여 데이터 세트의 일부를 변경했지만 동일한 데이터 세트의 영구 개체가 세션 캐시에 있는 경우 발생합니다.session.evict(yourObject)를 사용합니다.

휴지 상태에서는 세션에서 개체를 캐시합니다.및, " "는 " " " 입니다.org.hibernate.StaleStateException던질 수 있습니다.저장하거나 잠금을 사용하기 전에 엔티티 병합/갱신 방식으로 해결할 수 있습니다.상세정보 : http://java-fp.blogspot.lt/2011/09/orghibernatestalestateexception-batch.html

케이스 중 하나

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();

저는 이 예외에 직면해 있었고, 휴면 상태가 양호했습니다.pgAdmin을 사용하여 수동으로 1개의 레코드를 삽입하려고 했습니다만, 여기서 문제가 해결되었습니다.SQL insert 쿼리는 0 insert를 반환하고 null을 반환하기 때문에 이 문제의 원인이 되는 트리거 함수가 있기 때문에 new를 반환하도록 설정하기만 하면 됩니다.마침내 문제를 해결했습니다.

어떤 사람에게도 도움이 되길 바라야죠

가 잘못 을 해서 것입니다.ID를 사용하여 컬럼을 만듭니다.Id(x => x.Id, "id").GeneratedBy.**Assigned**();

된 문제Id(x => x.Id, "id").GeneratedBy.**Identity**();

빈 수업 시간에 신분증 신고 누락으로 인해 이런 일이 벌어집니다.

언급URL : https://stackoverflow.com/questions/2743130/hibernate-batch-update-returned-unexpected-row-count-from-update-0-actual-row

반응형