Comment fonctionne l' @Version
annotation dans JPA?
J'ai trouvé diverses réponses dont l'extrait est le suivant:
JPA utilise un champ de version dans vos entités pour détecter les modifications simultanées du même enregistrement de banque de données. Lorsque l'environnement d'exécution JPA détecte une tentative de modification simultanée du même enregistrement, il lève une exception à la transaction qui tente de valider en dernier.
Mais je ne sais toujours pas comment cela fonctionne.
Aussi à partir des lignes suivantes:
Vous devez considérer les champs de version immuables. La modification de la valeur du champ a des résultats non définis.
Cela signifie-t-il que nous devrions déclarer notre champ de version comme final
?
java
jpa
jpa-annotations
Yatendra Goel
la source
la source
UPDATE myentity SET mycolumn = 'new value', version = version + 1 WHERE version = [old.version]
. Si quelqu'un a mis à jour l'enregistrement,old.version
ne correspondra plus à celui de la base de données et la clause where empêchera la mise à jour de se produire. Les «lignes mises à jour» seront0
, que JPA peut détecter pour conclure qu'une modification simultanée s'est produite.Réponses:
Disons qu'une entité
MyEntity
a uneversion
propriété annotée :Lors de la mise à jour, le champ annoté avec
@Version
sera incrémenté et ajouté à laWHERE
clause, quelque chose comme ceci:Si la
WHERE
clause ne correspond pas à un enregistrement (car la même entité a déjà été mise à jour par un autre thread), le fournisseur de persistance lèvera unOptimisticLockException
.Non, mais vous pourriez envisager de protéger le passeur car vous n'êtes pas censé l'appeler.
la source
long
ou simplementlongValue()
sur l' appel . Vous avez besoin denull
contrôles explicites . Je ne ferais pas de l'initialisation explicite vous-même, car ce champ est censé être géré par le fournisseur ORM. Je pense qu'il est réglé0L
lors de la première insertion dans la base de données, donc s'il est réglé surnull
cela, cela vous indique que cet enregistrement n'a pas encore été conservé.Long
plutôt qu'une primitivelong
pour le@Version
champ, carSimpleJpaRepository.save(entity)
il traitera probablement un champ de version nulle comme un indicateur que l'entité est nouvelle. Si l'entité est nouvelle (comme indiqué par la version étant nulle), Spring appelleraem.persist(entity)
. Mais si la version a une valeur, elle appelleraem.merge(entity)
. C'est également pourquoi un champ de version déclaré en tant que type de wrapper doit être laissé non initialisé.Bien que la réponse @Pascal soit parfaitement valide, d'après mon expérience, je trouve le code ci-dessous utile pour réaliser un verrouillage optimiste:
Pourquoi? Car:
@Version
est accidentellement défini surnull
.optlock
plutôt queversion
.Premier point n'a pas d' importance si application utilise uniquement JPA pour insérer des données dans la base de données, en tant que fournisseur JPA appliquera
0
pour le@version
terrain au moment de la création. Mais presque toujours des instructions SQL simples sont également utilisées (au moins pendant les tests unitaires et d'intégration).la source
u_lmod
(utilisateur modifié en dernier lieu) où l' utilisateur peut être un processus / entité / application humain ou défini (automatisé) (donc le stockage supplémentaireu_lmod_id
pourrait également avoir un sens). (C'est à mon avis un méta-attribut métier très basique). Il stocke généralement sa valeur sous la forme DATE (HEURE) (ce qui signifie des informations sur l'année ... millis) en UTC (si le fuseau horaire "emplacement" de l'éditeur est important à stocker, alors avec le fuseau horaire). en outre très utile pour les scénarios de synchronisation DWH.Chaque fois qu'une entité est mise à jour dans la base de données, le champ de version est augmenté de un. Chaque opération qui met à jour l'entité dans la base de données sera ajoutée
WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
à sa requête.En vérifiant les lignes affectées de votre opération, le framework jpa peut s'assurer qu'il n'y a pas eu de modification simultanée entre le chargement et la persistance de votre entité car la requête ne trouverait pas votre entité dans la base de données lorsque son numéro de version a été augmenté entre le chargement et la persistance.
la source
Version utilisée pour garantir qu'une seule mise à jour à la fois. Le fournisseur JPA vérifiera la version, si la version attendue augmente déjà, quelqu'un d'autre met déjà à jour l'entité, donc une exception sera levée.
La mise à jour de la valeur de l'entité serait donc plus sûre, plus optimiste.
Si la valeur change fréquemment, vous pouvez envisager de ne pas utiliser le champ de version. Pour un exemple "une entité qui a un champ de compteur, qui augmentera chaque fois qu'une page Web accédera"
la source
Juste en ajoutant un peu plus d'informations.
JPA gère la version sous le capot pour vous, mais il ne le fait pas lorsque vous mettez à jour votre enregistrement via
JPAUpdateClause
, dans de tels cas, vous devez ajouter manuellement l'incrément de version à la requête.Il en va de même pour la mise à jour via JPQL, c'est-à-dire pas une simple modification de l'entité, mais une commande de mise à jour de la base de données même si cela est fait par hibernate
Pedro
la source
JPAUpdateClause
?