Java-JPA - @ version annotation

Comment fonctionne l'annotation @Version 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 apportées au même enregistrement de banque de données. Lorsque le runtime 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.


Également à partir de ce qui suit lignes:

Vous devriez considérer les champs de version immuables. La modification de la valeur du champ a des résultats indéfinis.

Signifie que nous devons déclarer notre champ de version comme final?

92
demandé sur ROMANIA_engineer 2010-04-04 00:28:03

5 réponses

Mais je ne sais toujours pas comment cela fonctionne?

Disons qu'une entité MyEntity a une propriété annotée version:

@Entity
public class MyEntity implements Serializable {    

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Version
    private Long version;

    //...
}

Lors de la mise à jour, le champ annoté avec {[4] } sera incrémenté et ajouté à la clause WHERE, quelque chose comme ceci:

UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))

Si la clause WHERE ne correspond pas à un enregistrement (car la même entité a déjà été mise à jour par un autre thread), le fournisseur de persistance lancera un OptimisticLockException.

Signifie que nous devrions déclarez notre champ de version comme final

Non, mais vous pourriez envisager de protéger le setter car vous n'êtes pas censé l'appeler.

159
répondu Pascal Thivent 2010-04-03 20:50:45

Bien que la réponse @ Pascal soit parfaitement valide, d'après mon expérience, je trouve le code ci-dessous utile pour accomplir un verrouillage optimiste:

@Entity
public class MyEntity implements Serializable {    
    // ...

    @Version
    @Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
    private long version = 0L;

    // ...
}

Pourquoi? Parce que:

  1. verrouillage Optimiste ne fonctionne pas si le champ annoté avec @Version est accidentellement mis à null.
  2. Comme ce champ spécial n'est pas nécessairement une version métier de l'objet, pour éviter une erreur, je préfère nommer ce champ à sth comme optlock plutôt que version.

Le premier point ne fonctionne pas importe si l'application utilise uniquement JPA pour insérer des données dans la base de données, car le fournisseur JPA appliquera 0 pour le champ @version au moment de la création. Mais presque toujours des instructions SQL simples sont également utilisées (au moins pendant les tests d'unité/ intégration).

20
répondu G. Demecki 2014-11-04 09:39:49

Chaque fois qu'une entité est mise à jour dans la base de données, le champ version est augmenté d'un. Chaque opération qui met à jour l'entité dans la base de données aura ajouté 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 load et persist.

8
répondu stefanglase 2010-04-03 20:50:09

Version utilisée pour s'assurer qu'une seule mise à jour à la fois. Le fournisseur JPA vérifiera la version, si la version attendue augmente déjà, alors quelqu'un d'autre met déjà à jour l'entité afin qu'une exception soit levée.

La mise à jour de la valeur de l'entité serait donc plus sécurisée, plus optimiste.

Si la valeur change fréquemment, vous pouvez envisager de ne pas utiliser le champ version. Pour un exemple "une entité qui a un champ de compteur, qui augmente chaque fois qu'une page web est consultée"

1
répondu uudashr 2011-08-22 06:07:48

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 ce cas, vous devez ajouter manuellement l'incrément de version à la requête.

Pedro

1
répondu Pedro Borges 2017-04-27 17:47:49