mercredi 12 avril 2023

L'impact de l'annotation @EqualsAndHashCode de Lombok sur le dirty checking de JPA \ Hibernate.

 Lombok est une bibliothèque Java qui permet de simplifier l'écriture de code en fournissant des annotations pour générer automatiquement du code répétitif. L'une de ces annotations est @EqualsAndHashCode, qui génère automatiquement les méthodes equals() et hashCode() pour une classe Java.


Cependant, lorsqu'on utilise l'annotation @EqualsAndHashCode avec JPA Hibernate, cela peut avoir un impact sur le dirty checking de JPA Hibernate, qui est un mécanisme utilisé pour détecter les changements apportés à une entité et pour les enregistrer dans la base de données.


Dirty checking avec JPA Hibernate

Avant de comprendre l'impact de l'annotation @EqualsAndHashCode sur le dirty checking de JPA Hibernate, il est important de comprendre comment fonctionne le dirty checking dans JPA Hibernate.


Lorsqu'on utilise JPA Hibernate, les entités sont suivies en permanence par le framework. Cela signifie que lorsque l'on charge une entité depuis la base de données, Hibernate garde une copie de cette entité en mémoire. Lorsque l'on apporte des modifications à cette entité, Hibernate compare la copie en mémoire avec l'entité modifiée pour détecter les changements.


Cette comparaison se fait via la méthode equals() de l'entité. Si les deux instances de l'entité sont égales, cela signifie que l'entité n'a pas été modifiée et qu'il n'est pas nécessaire de la sauvegarder dans la base de données. En revanche, si les deux instances de l'entité sont différentes, cela signifie qu'il y a eu des changements et que l'entité doit être sauvegardée dans la base de données.


L'impact de @EqualsAndHashCode sur le dirty checking

Lorsqu'on utilise l'annotation @EqualsAndHashCode avec JPA Hibernate, cela peut avoir un impact sur le dirty checking de JPA Hibernate. En effet, l'annotation @EqualsAndHashCode génère automatiquement les méthodes equals() et hashCode() pour une classe Java en se basant sur les champs de la classe. Si un champ de la classe est modifié, la méthode equals() générée par l'annotation considérera que l'instance de l'entité a changé, même si le champ modifié n'a pas d'impact sur l'état de l'entité dans la base de données.


Par exemple, si une entité "Client" a un champ "nom" et que l'on utilise l'annotation @EqualsAndHashCode pour générer la méthode equals(), si l'on modifie le champ "nom", la méthode equals() considérera que l'instance de l'entité a changé, même si ce changement n'a aucun impact sur l'état de l'entité dans la base de données.


Dans ce cas, le dirty checking de JPA Hibernate va détecter ce changement et va enregistrer l'entité modifiée dans la base de données, même si cela n'est pas nécessaire.


Les attributs à exclure de @EqualsAndHashCode

Pour éviter cet impact sur le dirty checking de JPA Hibernate, il est recommandé d'exclure certains attributs de la méthode equals() générée par l'annotation @EqualsAndHashCode.


Les attributs à exclure de la méthode equals() sont ceux qui n'ont pas d'impact sur l'état de l'entité dans la base de données. Ces attributs peuvent être des valeurs dérivées d'autres attributs, des attributs utilisés uniquement pour l'affichage, ou des attributs de configuration qui ne sont pas stockés dans la base de données.


Par exemple, pour une entité "Client" qui a un champ "nom" et un champ "adresse", si l'on veut exclure l'adresse de la méthode equals(), on peut utiliser l'annotation @EqualsAndHashCode.Exclude pour exclure le champ "adresse" de la méthode equals() :

@Data

@Entity

@EqualsAndHashCode(exclude={"adresse"})

public class Client {

    @Id

    @GeneratedValue(strategy = GenerationType.AUTO)

    private Long id;

    private String nom;

    private String adresse;

    // ...

}


Ainsi, si l'on modifie le champ "adresse", la méthode equals() ne considérera pas que l'instance de l'entité a changé, et le dirty checking de JPA Hibernate ne détectera pas de changement.

Le champ "id" d'une entité est une clé de base de données, et ne devrait pas être une préoccupation de la logique métier. Pour que le contrat equals et hashCode de l'entité soit valide quel que soit l'état de l'entité, il est recommandé d'utiliser l'annotation @EqualsAndHashCode.Exclude sur le champ "id" et sur les champs bidirectionnels relatifs aux adresses.

Enfin, lors de l'utilisation de la méthode toString, il est important de séparer les préoccupations, et d'exclure les champs bidirectionnels pour éviter un débordement de pile potentiel si on essaye d'imprimer l'objet. Pour cela, on peut utiliser l'annotation @ToString.Exclude sur chaque champ bidirectionnel.

Conclusion

L'utilisation de l'annotation @EqualsAndHashCode de Lombok peut simplifier l'écriture de code en générant automatiquement les méthodes equals() et hashCode() pour une classe Java. Cependant, lorsqu'on utilise cette annotation avec JPA Hibernate, il est important de faire attention à l'impact sur le dirty checking de JPA Hibernate.


Pour éviter cet impact, il est recommandé d'exclure certains attributs de la méthode equals() générée par l'annotation @EqualsAndHashCode. Ces attributs sont ceux qui n'ont pas d'impact sur l'état de l'entité dans la base de données. En excluant ces attributs, on peut éviter que le dirty checking de JPA Hibernate détecte des changements inutiles et améliorer les performances de notre application.