Les ORM (Object-Relational Mapping) sont aujourd’hui des technologies matures ; l’essentiel d’Hibernate a été versé au standard Java EE au travers de la spécification JPA qui en est maintenant à sa deuxième version.
Les réfractaires à la technologie, qui préfèrent encore utiliser directement l’API JDBC ou (moindre mal) les templates JDBC de Spring, sont aujourd’hui de moins en moins nombreux. Malgré tout, s’il est donc vrai qu’un ORM apporte une solution élégante à la problématique de persistance de nos applications, il faut aussi admettre qu’en déléguant une partie du travail d’interaction avec la base de données à ces outils nous perdons un peu en contrôle ce qui introduit de nouvelles difficultés. Parmi celles-ci il y a la bonne prise en compte du « dirty checking ».
Le « dirty checking » est un mécanisme d’Hibernate qui consiste à lister parmi les objets attachés ceux qui ont été modifiés pour ensuite propager ces modifications en base. Ce comportement du framework doit être bien compris car il peut être source d’effets secondaires indésirables.
Hibernate peut lancer une opération de « dirty checking » à plusieurs occasions :
- Lors d’un flush, qui intervient au moment du commit de la transaction ou d’un appel explicite par
EntityManager.flush(),
- Juste avant l’exécution d’une requête de type « select ». Ce cas de figure peut sembler moins évident que le premier mais la raison est relativement simple : la requête sera exécutée au niveau de la base de données, il est par conséquent capital que les données modifiées dans la transaction en cours qui peuvent influencer le résultat de la requête soient « flushées ».
Si la plupart du temps, le « dirty checking » se fait dans la plus grande transparence sans que le développeur n’ait à s’en soucier, il arrive aussi que son déclenchement soit gênant. Par exemple dans le cas de traitement de masse où le nombre d’objets attachés est très important cette opération peut pénaliser fortement les performances. Par ailleurs, si l’on souhaite avoir la main sur l’ordonnancement des requêtes SQL, il est embêtant qu’une simple recherche JPQL déclenche une série de requêtes de type « update » à la suite d’un « dirty checking ».
Comment faire alors pour maîtriser la propagation des modifications dans la base et éviter les traitements de « dirty checking » intempestifs ?
Hibernate étant un framework souple et paramétrable, il offre pas mal de possibilités au développeur pour gérer cela : on peut travailler dans un mode « read only », on peut aussi utiliser la classe StatelessSession qui est spécialement pensée pour dérouler des opérations en bloc (sans « dirty checking » automatique). Si l’on préfère, probablement à raison, rester sur le standard JPA, il suffit de positionner le « flush mode » comme ceci :
EntityManager.setFlushMode(FlushModeType.COMMIT);
Ainsi les objets ne seront synchronisés avec la base de données qu’à la fin de la transaction, au moment du commit. Évidemment, il faut là aussi être sûr de son coup, il convient de bien vérifier qu’aucune requête de sélection ne sera perturbée par ce flush tardif.