Hibernate - La mise à jour par lots a renvoyé un nombre de lignes inattendu à partir de la mise à jour: 0 nombre de lignes réel: 0 attendu: 1

141

J'obtiens une erreur de mise en veille prolongée. Je suis en mesure d'identifier la fonction à l'origine du problème. Malheureusement, il existe plusieurs appels de base de données dans la fonction. Je ne parviens pas à trouver la ligne qui cause le problème car la mise en veille prolongée vide la session à la fin de la transaction. L'erreur de mise en veille prolongée mentionnée ci-dessous ressemble à une erreur générale. Il ne mentionne même pas quel Bean est à l'origine du problème. Quelqu'un familier avec cette erreur de mise en veille prolongée?

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)
Sujee
la source
Merci @Peter Mortensen. J'ai mis à jour mon email.
Sujee
J'ai le même problème. Ce n'est pas un gros problème car cela arrive très rarement. L'utilisation de show_sql n'est pas pratique car la reproduction de ce comportement nécessite des millions de transactions. Cependant, comme cela se produit à plusieurs reprises lors d'un test système que je lance (qui a des milliards de transactions), je soupçonne qu'il y a une raison spécifique.
daniel_or_else
J'ai rencontré ce problème en essayant de mettre à jour les lignes qui n'ont AUCUN changement. Ne mettez pas à jour quelque chose s'il n'y a pas de différence.
fifman

Réponses:

62

Sans code et mappages pour vos transactions, il sera pratiquement impossible d'étudier le problème.

Cependant, pour mieux comprendre ce qui cause le problème, essayez ce qui suit:

  • Dans votre configuration de mise en veille prolongée, définissez hibernate.show_sql sur true. Cela devrait vous montrer le SQL qui est exécuté et cause le problème.
  • Définissez les niveaux de journalisation pour Spring et Hibernate sur DEBUG, encore une fois, cela vous donnera une meilleure idée de la ligne à l'origine du problème.
  • Créez un test unitaire qui réplique le problème sans configurer de gestionnaire de transactions dans Spring. Cela devrait vous donner une meilleure idée de la ligne de code incriminée.

J'espère que cela pourra aider.

beny23
la source
21
hibernate.show_sql> Je conseillerais plutôt de définir le niveau de catégorie de journal org.hibernate.SQL sur DEBUG. De cette façon, vous n'avez pas besoin de modifier la configuration de mise en veille prolongée uniquement pour la journalisation.
Thierry
L'utilisation de "show_sql" peut être très verbeuse et irréalisable en production. Pour cela, j'ai modifié la clause catch à la ligne 74 de la classe BatchingBatcher pour imprimer l'instruction avec un ps.toString () pour n'avoir que l'instruction qui pose le problème.
Orden
71

J'ai eu la même exception lors de la suppression d'un enregistrement par ID qui n'existe pas du tout. Vérifiez donc que l'enregistrement que vous mettez à jour / supprimez existe réellement dans la base de données

Shreyas
la source
12
J'ai eu ce problème lorsque j'ai supprimé un enfant d'une relation parent-enfant, enregistré le parent (qui supprime l'enfant) et ensuite essayé de supprimer également l'enfant manuellement.
dave thieben
Cela a résolu mon problème. L'enregistrement n'existait pas et mon service appelait la méthode updateAll () alors qu'il avait en fait besoin d'appeler la méthode createOrUpdateAll (). Merci.
Mital Pritmani
3
alors comment résoudre le problème? Si j'obtiens un enregistrement, puis je le supprime; mais si le système le supprime déjà avant de le supprimer, un autre, mon application lèvera l'exception.
Stony
@davethieben, c'était exactement les informations dont j'avais besoin. Je ne savais pas que la relation parent-enfant persistait lors des suppressions.
snowe
La solution consiste à utiliser select for update lors de la récupération de l'enregistrement pour verrouiller la ligne, de sorte que rien d'autre ne puisse la supprimer avant vous.
Matthew Read
55

Solution: dans le fichier de mappage Hibernate pour la propriété id, si vous utilisez une classe de générateur, pour cette propriété, vous ne devez pas définir la valeur explicitement à l'aide d'une méthode setter.

Si vous définissez la valeur de la propriété Id explicitement, cela entraînera l'erreur ci-dessus. Vérifiez ceci pour éviter cette erreur. ou C'est une erreur lorsque vous mentionnez dans le fichier de mappage le générateur de champs = "native" ou "incremental" et dans votre BASE DE DONNÉES la table mappée n'est pas auto_incremented Solution: Accédez à votre DATABASE et mettez à jour votre table pour définir auto_increment

Rēda Biramanē
la source
2
Absolument raison. Cela doit être la bonne réponse. Merci @ Rēda Biramanē
Kuldeep Verma
1
Cependant , vous POUVEZ définir la valeur de l'ID sur `` 0 '', puis Hibernate se sentira libre de l'écraser
forresthopkinsa
1
Merci! Je ne sais pas pourquoi ce n'est pas la réponse la mieux classée.
Stuart McIntyre
18

Cela m'est arrivé une fois par accident lorsque j'attribuais des identifiants spécifiques à certains objets (tests) et que j'essayais de les enregistrer dans la base de données. Le problème était que dans la base de données il y avait une politique spécifique pour la configuration des ID des objets. N'attribuez simplement pas d'identifiant si vous avez une politique au niveau d'Hibernate.

cSn
la source
15

Dans mon cas, je suis arrivé à cette exception dans deux cas similaires:

  • Dans une méthode annotée avec @Transactionalj'ai eu un appel à un autre service (avec de longs délais de réponse). La méthode met à jour certaines propriétés de l'entité (après la méthode, l'entité existe toujours dans la base de données). Si l'utilisateur demande deux fois la méthode (car il pense que cela ne fonctionne pas la première fois) en quittant la méthode transactionnelle la deuxième fois, Hibernate tente de mettre à jour une entité qui a déjà changé son état depuis le début de la transaction. Lorsque Hibernate recherche une entité dans un état et trouve la même entité mais déjà modifiée par la première requête, il lève une exception car il ne peut pas mettre à jour l'entité. C'est comme un conflit dans GIT.
  • J'ai eu des requêtes automatiques (pour surveiller la plateforme) qui mettent à jour une entité (et le rollback manuel quelques secondes plus tard). Mais cette plateforme est déjà utilisée par une équipe de test. Lorsqu'un testeur effectue un test dans la même entité que les requêtes automatiques (dans le même centième de milliseconde), j'obtiens l'exception. Comme dans le cas précédent, lors de la sortie de la deuxième transaction, l'entité précédemment récupérée a déjà changé.

Conclusion: dans mon cas, ce n'était pas un problème que l'on retrouve dans le code. Cette exception est levée lorsque Hibernate constate que l'entité extraite pour la première fois de la base de données a changé pendant la transaction en cours , il ne peut donc pas la vider dans la base de données car Hibernate ne sait pas quelle est la version correcte de l'entité: celle qui est en cours extraction de transaction au début; ou celui déjà stocké dans la base de données.

Solution: pour résoudre le problème, vous devrez jouer avec le LockMode Hibernate pour trouver celui qui correspond le mieux à vos besoins.

Sergio Lema
la source
Salut, merci pour votre réponse utile. Pourriez-vous s'il vous plaît indiquer, quel LockMode vous avez opté finalement, pour vous débarrasser de l'erreur. Je suis confronté à un problème similaire, lorsqu'une API est touchée successivement en quelques millisecondes, en raison du clic de l'utilisateur deux fois par inadvertance. Le verrouillage pessimiste nuit aux performances; vous serez donc intéressé de savoir ce que vous avez finalement fait?
Madhur Bhaiya le
1
Cela faisait longtemps que j'avais le problème et je ne me souviens pas très bien de la solution exacte que j'ai mise en place, mais c'était LockMode.READou LockMode.OPTIMISTIC.
Sergio Lema
13

Je viens de rencontrer ce problème et j'ai découvert que je supprimais un enregistrement et que j'essayais de le mettre à jour par la suite dans une transaction Hibernate.

Julius
la source
9

Cela peut se produire lorsque des déclencheurs exécutent des requêtes DML (modification de données) supplémentaires qui affectent le nombre de lignes. Ma solution était d'ajouter ce qui suit en haut de mon déclencheur:

SET NOCOUNT ON;
M. TA
la source
1
Cette réponse m'a envoyé dans la bonne direction. Je travaillais avec une base de données héritée qui avait un déclencheur dessus - heureusement, je remplaçais ce que le déclencheur faisait par du code, donc je pouvais simplement le supprimer.
S.Baggy
2
+1 C'était aussi mon problème. J'utilisais postgresql, donc j'avais besoin d'utiliser l'annotation @SQLInsert pour désactiver la vérification du nombre de lignes: technology-ebay.de/the-teams/mobile-de/blog/…
s1mm0t
SET NOCOUNT OFF *
G. Ciardini
7

J'étais confronté au même problème. Le code fonctionnait dans l'environnement de test. Mais cela ne fonctionnait pas dans l'environnement de mise en scène.

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

Le problème était que la table avait une seule entrée pour chaque clé primaire lors du test de la table DB. Mais dans la base de données intermédiaire, il y avait plusieurs entrées pour la même clé primaire. (Le problème est dans la base de données intermédiaire, la table n'avait aucune contrainte de clé primaire et il y avait plusieurs entrées.)

Ainsi, chaque fois que l'opération de mise à jour échoue. Il essaie de mettre à jour un enregistrement unique et s'attend à obtenir un nombre de mises à jour égal à 1. Mais comme il y avait 3 enregistrements dans la table pour la même clé primaire, le nombre de mises à jour des résultats trouve 3. Puisque le nombre de mises à jour prévu et le nombre de mises à jour des résultats réels ne correspondent pas , Il lève une exception et revient en arrière.

Après le, j'ai supprimé tous les enregistrements qui ont une clé primaire en double et ajouté des contraintes de clé primaire. Cela fonctionne bien.

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

nombre de lignes réel: 0 // signifie qu'aucun enregistrement n'a été trouvé pour mettre à jour
update: 0 // signifie qu'aucun enregistrement n'a été trouvé, donc aucune mise à jour n'est
attendue: 1 // signifie au moins 1 enregistrement attendu avec la clé dans la table de base de données.

Ici, le problème est que la requête essaie de mettre à jour un enregistrement pour une clé, mais hibernate n'a trouvé aucun enregistrement avec la clé.

ParagFlume
la source
Salut @ParagFlume, j'ai eu la même erreur "La mise à jour par lots a renvoyé un nombre de lignes inattendu de la mise à jour: 0 nombre de lignes réel: 0 attendu: 1" lorsque j'essaie de sélectionner un objet de ma base de données, le problème n'existe que dans toute production, en avez-vous idée sur la façon de résoudre ce problème, je suis dans une situation critique. Cordialement !
James
Je pense que la requête n'a trouvé aucun enregistrement dans db, elle s'attend à trouver un enregistrement dans db, qu'elle essaie de mettre à jour. vous pouvez vérifier manuellement / query-browser si l'enregistrement existe réellement sur db.
ParagFlume
oui, l'enregistrement existe, mais mon problème est de savoir pourquoi hibernate essaie de mettre à jour, j'utilise juste un service de sélection pour obtenir l'objet de la base de données!
James
peut-être que vous essayez de changer l'état des objets. et la session est toujours ouverte.
ParagFlume
comment je peux vérifier ce comportement, parce que je ne suis pas la personne qui développe le service ...
James
5

Comme Julius le dit, cela se produit lorsqu'une mise à jour se produit sur un objet dont les enfants sont supprimés. (Probablement parce qu'il y avait un besoin d'une mise à jour pour tout l'objet Père et parfois nous préférons supprimer les enfants et les réinsérer sur le Père (nouveau, ancien n'a pas d'importance) avec toutes les autres mises à jour que le père pourrait avoir sur l'un des ses autres champs simples) Donc ... pour que cela fonctionne, supprimez les enfants (dans une transaction) en appelant childrenList.clear()(Ne parcourez pas les enfants et supprimez chacun d'eux avec certains childDAO.delete(childrenList.get(i).delete()))et définissez @OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)du côté de l'objet père. Mettez ensuite à jour le père (FatherDAO.update (père)). (Répétez pour chaque objet père) Le résultat est que les enfants ont leur lien avec leur père enlevé et ensuite ils sont enlevés comme orphelins par le cadre.

George Papatheodorou
la source
5

J'ai rencontré ce problème où nous avions une relation un-plusieurs.

Dans le fichier de mappage hbm hibernate pour master, pour un objet avec un arrangement de type défini, ajouté cascade="save-update"et cela a bien fonctionné.

Sans cela, par défaut, Hibernate essaie de mettre à jour un enregistrement inexistant et, ce faisant, l'insère à la place.

Shiv
la source
4

Cela peut également arriver lorsque vous essayez UPDATEune CLÉ PRIMAIRE .

Marcel
la source
3

J'ai eu le même problème et j'ai vérifié que cela pouvait se produire en raison de la clé primaire d'incrémentation automatique. Pour résoudre ce problème, n'insérez pas de valeur d'incrémentation automatique avec l'ensemble de données. Insérez des données sans la clé primaire.

MacDaddy
la source
3

Une autre façon d'obtenir cette erreur est si vous avez un élément nul dans une collection.

Bryan Pugh
la source
2

cela se produit lorsque vous essayez de supprimer le même objet, puis de mettre à jour à nouveau le même objet, utilisez-le après la suppression

session.clear ();

Sumit Singh
la source
2

Cela m'est arrivé aussi, car j'avais mon identifiant Long, et je recevais de la vue la valeur 0, et lorsque j'ai essayé d'enregistrer dans la base de données, j'ai eu cette erreur, puis je l'ai corrigée en définissant l'identifiant sur null.

Roberto Rodriguez
la source
1

J'ai rencontré ce problème lorsque je commençais et validais manuellement des transactions dans la méthode annotée comme @Transactional. J'ai résolu le problème en détectant si une transaction active existait déjà.

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

Ensuite, j'ai autorisé Spring à gérer la validation de la transaction.

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;
            }
        }
    }
}
Gibado
la source
1

Cela se produit lorsque vous avez déclaré le Bean géré JSF comme

@RequestScoped;

quand vous devez déclarer comme

@SessionScoped;

Cordialement;

John Lopez
la source
1

J'ai eu cette erreur lorsque j'ai essayé de mettre à jour un objet avec un identifiant qui n'existait pas dans la base de données. La raison de mon erreur était que j'avais attribué manuellement une propriété avec le nom `` id '' à la représentation JSON côté client de l'objet, puis lors de la désérialisation de l'objet côté serveur, cette propriété `` id '' écraserait la variable d'instance ( également appelé 'id') qu'Hibernate était censé générer. Faites donc attention aux collisions de noms si vous utilisez Hibernate pour générer des identifiants.

Soggiorno
la source
1

J'ai également rencontré le même défi. Dans mon cas, je mettais à jour un objet qui n'existait même pas, en utilisant hibernateTemplate.

En fait, dans mon application, je recevais un objet DB à mettre à jour. Et tout en mettant à jour ses valeurs, j'ai également mis à jour son identifiant par erreur, je suis allé le mettre à jour et suis tombé sur ladite erreur.

J'utilise hibernateTemplatepour les opérations CRUD.

Kuldeep Verma
la source
1

Après avoir lu toutes les réponses, je n'ai trouvé personne pour parler de l'attribut inverse de l'hibernation.

À mon avis, vous devriez également vérifier dans votre cartographie des relations si le mot clé inverse est convenablement réglé. Le mot clé inversé est créé pour définir de quel côté est le propriétaire pour maintenir la relation. La procédure de mise à jour et d'insertion varie en fonction de cet attribut.

Supposons que nous ayons deux tables:

principal_table , middle_table

avec une relation de un à plusieurs . Les classes de mappage hiberntate sont respectivement Principal et Middle .

Ainsi, la classe Principal a un SET d' objets Middle . Le fichier de mappage xml doit être comme suit:

<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>
    ...

Comme l'inverse est défini sur "true", cela signifie que la classe "Middle" est le propriétaire de la relation, donc la classe Principal NE METTE PAS À JOUR la relation.

Ainsi, la procédure de mise à jour pourrait être implémentée comme ceci:

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

Cela a fonctionné pour moi, mais vous pouvez suggérer quelques éditions afin d'améliorer la solution. De cette façon, nous apprendrons tous.

Luis Teijon
la source
1

Dans notre cas, nous avons finalement découvert la cause première de StaleStateException.

En fait, nous supprimions la ligne deux fois en une seule session de mise en veille prolongée. Auparavant, nous utilisions ojdbc6 lib, et c'était correct dans cette version.

Mais lorsque nous avons mis à niveau vers odjc7 ou ojdbc8, la suppression des enregistrements deux fois soulevait une exception. Il y avait un bogue dans notre code où nous supprimions deux fois, mais ce n'était pas évident dans ojdbc6.

Nous avons pu reproduire avec ce morceau de code:

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

Au premier vidage, la mise en veille prolongée va et fait des changements dans la base de données. Pendant le 2e vidage, la mise en veille prolongée compare l'objet de la session avec l'enregistrement de la table réelle, mais n'a pas pu en trouver un, d'où l'exception.

Sandeep Khantwal
la source
1

Ce problème se produit principalement lorsque nous essayons de sauvegarder ou de mettre à jour l'objet qui est déjà récupéré en mémoire par une session en cours. Si vous avez récupéré un objet de la session et que vous essayez de mettre à jour dans la base de données, cette exception peut être levée.

J'ai utilisé session.evict (); pour supprimer d'abord le cache stocké en veille prolongée ou si vous ne voulez pas prendre le risque de perdre des données, mieux vaut créer un autre objet pour stocker la température des données.

     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();
    }
Amit Mishra
la source
1

Problème Hibernate 5.4.1 et HHH-12878

Avant Hibernate 5.4.1, les exceptions d'échec de verrouillage optimiste (par exemple, StaleStateExceptionouOptimisticLockException ) n'incluaient pas l'instruction failing.

Le problème HHH-12878 a été créé pour améliorer Hibernate afin que lors du lancement d'une exception de verrouillage optimiste, l' PreparedStatementimplémentation JDBC soit également enregistrée:

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

Temps de test

J'ai créé le BatchingOptimisticLockingTestdans mon référentiel GitHub de persistance Java haute performance pour montrer comment le nouveau comportement fonctionne.

Tout d'abord, nous allons définir une Postentité qui définit une @Versionpropriété, permettant ainsi le mécanisme de verrouillage optimiste implicite :

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

Nous activerons le traitement par lots JDBC en utilisant les 3 propriétés de configuration suivantes:

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

Nous allons créer 3 Postentités:

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

Et Hibernate exécutera une insertion par lots 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)
]

Nous savons donc que le traitement par lots JDBC fonctionne très bien.

Maintenant, répliquons le problème de verrouillage optimiste:

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

La première transaction sélectionne toutes les Postentités et modifie letitle propriétés.

Cependant, avant que le premier ne EntityManagersoit vidé, nous allons exécuter une deuxième transition en utilisant la executeSyncméthode.

La deuxième transaction modifie la première Post, elle versionva donc être incrémentée:

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

Maintenant, lorsque la première transaction essaie de vider le EntityManager, nous obtiendrons le 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=?

Vous devez donc mettre à niveau vers Hibernate 5.4.1 ou une version plus récente pour bénéficier de cette amélioration.

Vlad Mihalcea
la source
0

Cela se produit si vous modifiez quelque chose dans l'ensemble de données à l'aide d'une requête SQL native mais que l'objet persistant pour le même ensemble de données est présent dans le cache de session. Utilisez session.evict (yourObject);

abhi
la source
0

Un des cas

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();
vootla561
la source
0

Je faisais face à cette exception et la mise en veille prolongée fonctionnait bien. J'ai essayé d'insérer manuellement un enregistrement à l'aide de pgAdmin, ici le problème est devenu clair. La requête d'insertion SQL renvoie 0 insertion. et il existe une fonction de déclenchement qui cause ce problème car elle renvoie null. donc je n'ai qu'à le configurer pour qu'il revienne nouveau. et finalement j'ai résolu le problème.

espérons que cela aide n'importe quel corps.

kamel2005
la source
0

J'ai eu cette erreur car j'ai mappé par erreur la IDcolonne en utilisantId(x => x.Id, "id").GeneratedBy.**Assigned**();

Problème résolu en utilisant Id(x => x.Id, "id").GeneratedBy.**Identity**();

Roylac
la source
0

Cela m'arrive parce que je n'ai pas de déclaration d'identification dans la classe de bean.

Ganesh Giri
la source
0

Dans mon cas, il y avait un problème avec la base de données car l'un des processus stockés consommait tout le processeur, ce qui entraînait des temps de réponse de base de données élevés. Une fois que cela a été tué, le problème a été résolu.

Amit
la source