Si vous mélangez Hibernate avec du SQL…

De plus en plus d’applications utilisent Hibernate. Mais de temps en temps, pour des raisons de performance, on peut être tenté de remplacer les requêtes Hibernate par du SQL. Et là, les choses se gâtent.

En effet, le cycle de vie d’un objet chargé avec Hibernate est comme suit :

  1. Chargement de l’objet avec une requête en HQL ou par critères
  2. L’application met à jour l’objet, celui-ci reste en mémoire
  3. L’application appelle session.save(). Cette méthode ne fait que synchroniser l’objet avec le cache hibernate dans le cas où celui-ci est absent. Autrement dit, dans notre cas, elle ne fait rien.
  4. L’objet est vraiment écrit en base soit lors de l’appel à session.flush(), soit lors de la fermeture de la session si celle-ci est en flush automatique, c’est le cas par défaut.

Maintenant où est le problème ? Eh bien, supposons qu’entre l’étape 2 et l’étape 3 l’application fasse une écriture en base sur notre objet, sans passer par Hibernate. Ce dernier ne saura pas qu’il y a eu une mise à jour dans son dos. Dès lors, après que la mise à jour de données a été faite par le SQL, Hibernate va l’écraser au moment du flush() et donc, il n’y a aucun moyen de prédire ce qui va se passer et les données en base seront probablement incohérentes.

Il existe plusieurs moyens de contourner le problème :

  • Soit avant de faire un appel au DAO en SQL, on flush la session. Maintenant cette solution est « sale » et ne fonctionne pas dans un contexte transactionnel.
  • Soit quand on rencontre le cas ci-dessus on fait une requête hibernate moins sélective en base, quitte à refaire du filtrage dans le DAO. C’est certes plus lent en terme de transfert réseau, mais d’un autre côté on peut ainsi bénéficier du cache de session Hibernate et surtout nos données sont en sécurité.
  • Soit l’application requiert un grand nombre de requêtes spécifiques à un SGBD comme par exemple du Oracle Text, et dans ce cas, le mieux est encore de ne pas utiliser Hibernate mais plutôt Spring JDBC ou du MyBatis.

Cet article vous a plu ? Vous aimerez sûrement aussi :

Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter Son LinkedIn

gojul

View Comments

  • Hum l'utilisation d'un champ version devrait normalement régler ce genre de problème non?

    Par ailleurs pour moi les problèmes de performance se rencontrent plus généralement à la lecture qu'à l'écriture et on peut demander à la session hibernate (donc passant par le cache) d'executer une SQL query si je me souvient bien.

    Cordialement.

  • Bonjour,

    En fait non, quand on demande à la session Hibernate d'exécuter une query SQL, celle-ci l'envoie directement au driver JDBC sans mettre à jour son état interne. La doc ici n'est pas très claire, je vous invite à essayer un exemple simple pour vous en convaincre du type une table avec deux champs, on charge une entité avec Hibernate qu'on met à jour avec une valeur, on met à jour ensuite la table avec du SQL natif en passant par la session et ensuite on flush la session. Contrôlez le résultat, vous risquez d'être surpris.
    Il y a qq trucs sur stackoverflow sur le sujet, notamment ceci :
    http://stackoverflow.com/questions/15595627/hibernate-query-cache-applicable-for-native-queries

    Pour l'utilisation du champ version, l'approche peut fonctionner mais pas dans tous les cas. En effet dans le cas où le premier update en SQL met à jour la version, les données ne seront pas remplacées par du Hibernate. Maintenant cela pose deux problèmes :
    1/ La gestion côté Hibernate doit être totalement transactionnelle, ce qui n'est hélas pas toujours le cas dans la vraie vie.
    2/ Dans le cas où il y a un mix d'update en SQL et d'update en Hibernate au sein du même thread, on risque tout de même se retrouver avec un truc assez horrible à déboguer, et il faut aussi que le SQL mette à jour la version de manière atomique.

    Néanmoins c'est tout de même plus safe que ne rien faire du tout.

Recent Posts

On accueille le nouveau CTO 🎉

Un nouveau capitaine technique débarque à la barre de WeLoveDevs ! Après le rachat par…

3 jours ago

AI Act for Developers : comprendre les 5 niveaux de risques

L’AI Act pour les développeurs, c’est la première loi vraiment impactante depuis le RGPD. Et…

1 semaine ago

Angular, mais en mode « easy » : interview avec Gaetan Redin.

"Venez, faites le module 1 et on en reparle." C’est le défi lancé par Gaetan…

2 semaines ago

OWASP Top 10 : 10 erreurs que les développeurs web font tous les jours (et comment les éviter)

L’OWASP Top 10, c’est un outil pour les développeurs web. Et pourtant, il est largement…

3 semaines ago

RGPD pour les développeurs : coder la confiance avant tout.

Dans cet article, on va parler du RGPD pour les développeurs. C’est un sujet que…

1 mois ago

Monolithe vs Microservices : comment choisir la bonne architecture pour votre application ?

En 2025, le débat monolithe vs microservices n’est toujours pas tranché. Faut-il garder une architecture…

1 mois ago