Verrouillage optimiste dans JPA

Verrouillage optimiste dans JPA

1. introduction

Lorsqu'il s'agit d'applications d'entreprise, il est essentiel de gérer correctement l'accès simultané à une base de données. Cela signifie que nous devrions être en mesure de gérer plusieurs transactions de manière efficace et, surtout, sans risque d'erreur.

De plus, nous devons nous assurer que les données restent cohérentes entre les lectures et les mises à jour simultanées.

Pour ce faire, nous pouvons utiliser le mécanisme de verrouillage optimiste fourni par l'API Java Persistence. Cela signifie que plusieurs mises à jour effectuées sur les mêmes données en même temps n'interfèrent pas les unes avec les autres.

2. Comprendre le verrouillage optimiste

Afin d'utiliser le verrouillage optimiste,we need to have an entity including a property with @Version annotation. Lors de son utilisation, chaque transaction qui lit des données contient la valeur de la propriété version.

Avant que la transaction ne souhaite effectuer une mise à jour, elle vérifie à nouveau la propriété de version.

Si la valeur a changé entre-temps, unOptimisticLockException est émis. Sinon, la transaction valide la mise à jour et incrémente une propriété de version de valeur.

3. Verrouillage pessimiste contre Verrouillage optimiste

Il est bon de savoir que, contrairement au verrouillage optimiste, JPA nous donne un verrouillage pessimiste. C’est un autre mécanisme de gestion de l’accès simultané aux données.

Nous abordons le verrouillage pessimiste dans l'un de nos articles précédents -Pessimistic Locking in JPA. Voyons quelle est la différence et comment nous pouvons bénéficier de chaque type de verrouillage.

Comme nous l’avons déjà dit,optimistic locking is based on detecting changes on entities by checking their version attribute. Si une mise à jour simultanée a lieu,OptmisticLockException se produit. Après cela, nous pouvons réessayer de mettre à jour les données.

On peut imaginer que ce mécanisme convient aux applications qui lisent beaucoup plus que les mises à jour ou les suppressions. De plus, il est utile dans les situations où les entités doivent être détachées pendant un certain temps et les verrous ne peuvent pas être maintenus.

Au contraire, le mécanisme de verrouillage pessimiste implique le verrouillage des entités au niveau de la base de données.

Chaque transaction peut acquérir un verrou sur les données. Tant qu'elle conserve le verrou, aucune transaction ne peut lire, supprimer ou mettre à jour les données verrouillées. Nous pouvons présumer que le verrouillage pessimiste peut entraîner des blocages. Cependant, cela garantit une plus grande intégrité des données que le verrouillage optimiste.

4. Attributs de version

Les attributs de version sont des propriétés avec l'annotation@Version. They are necessary for enabling optimistic locking. Voyons un exemple de classe d'entité:

@Entity
public class Student {

    @Id
    private Long id;

    private String name;

    private String lastName;

    @Version
    private Integer version;

    // getters and setters

}

Il y a plusieurs règles à suivre lors de la déclaration des attributs de version:

  • chaque classe d'entité ne doit avoir qu'un seul attribut de version

  • il doit être placé dans la table primaire pour une entité mappée à plusieurs tables

  • le type d'un attribut de version doit être l'un des suivants:int,Integer,long,Long,short,Short,java.sql.Timestamp

We should know that we can retrieve a value of the version attribute via entity, but we mustn’t update or increment it. Seul le fournisseur de persistance peut le faire, les données restent donc cohérentes.

Il est intéressant de noter que les fournisseurs de persistance peuvent prendre en charge le verrouillage optimiste pour les entités qui n'ont pas d'attributs de version. Pourtant, il est judicieux de toujours inclure les attributs de version lorsque vous travaillez avec un verrouillage optimiste.

Si nous essayons de verrouiller une entité qui ne contient pas un tel attribut et que le fournisseur de persistance ne le prend pas en charge, cela se traduira par unPersitenceException.

5. Modes de verrouillage

JPA nous fournit deux modes de verrouillage optimistes différents (et deux alias):

  • OPTIMISTIC - il obtient un verrou de lecture optimiste pour toutes les entités contenant un attribut de version

  • OPTIMISTIC_FORCE_INCREMENT - il obtient un verrou optimiste identique àOPTIMISTIC et incrémente en plus la valeur d'attribut de version

  • READ - c'est un synonyme deOPTIMISTIC

  • WRITE - c'est un synonyme deOPTIMISTIC_FORCE_INCREMENT

Nous pouvons trouver tous les types listés ci-dessus dans la classeLockModeType.

5.1. OPTIMISTIC (READ)

Comme nous le savons déjà, les modes de verrouillageOPTIMISTIC etREAD sont des synonymes. Cependant, la spécification JPA nous recommande d'utiliserOPTIMISTIC dans les nouvelles applications.

Whenever we request the OPTIMISTIC lock mode, a persistence provider will prevent our data from dirty reads as well as non-repeatable reads.

En termes simples, il convient de s’assurer que toute transaction ne commet aucune modification sur les données d’une autre transaction:

  • a mis à jour ou supprimé mais non engagé

  • a mis à jour ou supprimé avec succès entre-temps

5.2. OPTIMISTIC_INCREMENT (WRITE)

De la même façon que précédemment,OPTIMISTIC_INCREMENT etWRITE sont des synonymes, mais le premier est préférable.

OPTIMISTIC_INCREMENT doit remplir les mêmes conditions que le mode de verrouillageOPTIMISTIC. Additionally, it increments the value of a version attribute. Cependant, il n'est pas spécifié si cela doit être fait immédiatement ou peut être reporté jusqu'à la validation ou au vidage.

Il est intéressant de savoir qu’un fournisseur de persistance est autorisé à fournir la fonctionnalitéOPTIMISTIC_INCREMENT lorsque le mode de verrouillageOPTIMISTIC est demandé.

6. Utiliser le verrouillage optimiste

We should remember that for versioned entities optimistic locking is available by default. Pourtant, il existe plusieurs façons de le demander explicitement.

6.1. Find

Pour demander un verrouillage optimiste, nous pouvons passer lesLockModeType appropriés comme argument pour trouver la méthode deEntityManager:

entityManager.find(Student.class, studentId, LockModeType.OPTIMISTIC);

6.2. Requete

Une autre façon d'activer le verrouillage consiste à utiliser la méthodesetLockMode de l'objetQuery:

Query query = entityManager.createQuery("from Student where id = :id");
query.setParameter("id", studentId);
query.setLockMode(LockModeType.OPTIMISTIC_INCREMENT);
query.getResultList()

6.3. Verrouillage explicite

Nous pouvons définir un verrou en appelant la méthodelock d'EnitityManager:

Student student = entityManager.find(Student.class, id);
entityManager.lock(student, LockModeType.OPTIMISTIC);

6.4. Rafraîchir

On peut appeler la méthoderefresh de la même manière que la méthode précédente:

Student student = entityManager.find(Student.class, id);
entityManager.refresh(student, LockModeType.READ);

6.5. NamedQuery

La dernière option consiste à utiliser @NamedQuery avec la propriétélockMode:

@NamedQuery(name="optimisticLock",
  query="SELECT s FROM Student s WHERE s.id LIKE :id",
  lockMode = WRITE)

7. OptimisticLockException

When the persistence provider discovers optimistic locking conflicts on entities, it throws OptimisticLockExceptionNous devons être conscients qu'en raison de l'exception, la transaction active est toujours marquée pour une annulation.

Il est bon de savoir comment nous pouvons réagir auxOptimisticLockException. Cette exception contient commodément une référence à l'entité en conflit. However, it’s not mandatory for the persistence provider to supply it in every situation. Il n'y a aucune garantie que l'objet sera disponible.

Il existe cependant une méthode recommandée pour gérer l'exception décrite. Nous devrions récupérer à nouveau l'entité en rechargeant ou en rafraîchissant. De préférence dans une nouvelle transaction. Après cela, nous pouvons essayer de le mettre à jour une fois de plus.

8. Conclusion

Dans ce tutoriel, nous nous sommes familiarisés avec un outil qui peut nous aider à orchestrer des transactions simultanées. Optimistic locking uses version attributes included in entities to control concurrent modifications on them.

Par conséquent, il garantit que les mises à jour ou les suppressions ne seront pas écrasées ou perdues silencieusement. Contrairement au verrouillage pessimiste, il ne verrouille pas les entités au niveau de la base de données et par conséquent, il n’est pas vulnérable aux blocages de base de données.

Nous avons appris que le verrouillage optimiste est activé par défaut pour les entités versionnées. Cependant, il existe plusieurs façons de le demander explicitement en utilisant différents types de mode de verrouillage.

Un autre fait dont nous devons nous souvenir est que chaque fois qu'il y a des mises à jour conflictuelles sur des entités, nous devrions nous attendre à unOptimisticLockException.

Enfin, le code source de ce tutoriel est disponibleover on GitHub.