Criteria and DetachedCriteria weirdness

J’ai déjà eu l’occasion de parler des Criteria et DetachedCriterias d’Hibernate, ainsi que de la difficulté de rechercher des objets à partir de critères mappés dans une Map.J’ai finalement réussi, mais j’en ai rudement bavé !Je vais vous expliquer un peu le bazar.Pour reprendre mon exemple, j’ai des objets Indicateur, qui contiennent dans une Map appelée listeValeurindicateur des associations vers des objets ValeurListe avec en plus un codeListe.Pour donner une idée de la table, ça ressemble à quelque chose comme

IND_IDINDICATEUR IND_CODELISTE VAL_IDVALEUR

Et c’est mappé dans une Map définie dans mon objet indicateur :

public class Indicateur {private Map listeValeurindicateur;}

Et donc, je dois rechercher des Indicateur pour lesquels j’ai les critères suivants :listeValeurindicateur[« DOMAINE »]=ValeurListe@12 (valeur flore) ET listeValeurindicateur[« PERIODE »]=ValeurListe@17 (valeur hebdomadaire)Il y a lé deux difficultés : rechercher des objets en se basant sur la clé de la Map (qui n’est décrite dans aucun fichier de mapping Hibernate, et donc non addressable via un Criterion classique), et rechercher en se basant sur plusieurs entrées dans cette table d’association.

Rechercher une clé dans la Map

Considérons qu’on a un critère associé à la classe Indicateur.Il suffit alors de se créer un sous-critère pour la Map :

DetachedMemorizerCriteria mapCriteria = indicateurCriteria.createCriteria("listeValeurIndicateur");

Ca, c’est facile, puis de lui ajouter les critéres de recherche :

// J'ai oublié comment j'avais fait pour l'objet ValeurListe associé, mais c'est assez simple, puisque ça marche comme dans une collection.
// Ajout du critére de recherche de la clé
mapCriteria.add(Restrictions.sqlRestriction("IND_CODELISTE = 'monCodeListe'"));

Il y a quand même un inconvénient à cette méthode : si on veut la rendre un peu générique, il faut aller farfouiller dans les ClassMetadat d’Hibernate pour savoir, dans cette table d’association, quelles sont les colonnes utilisées par l’objet conteneur et quelles sont celles utilisées par l’objet contenu pour déduire que les colonnes inutilisées sont celles correspondant à la clé. Je l’ai codé, c’est pas fameusement drôle, mais ça marche.

Recherche de plusieurs éléments dans une Map

Bon, là, c’est l’enfer.Je reprend ma recherche :listeValeurindicateur[« DOMAINE »]=ValeurListe@12 (valeur flore) ET listeValeurindicateur[« PERIODE »]=ValeurListe@17 (valeur hebdomadaire). Je recherche donc un indicateur qui corresponde à deux lignes dans ma table d’association, c’est-à-dire que le contenu de la table d’association doit être l’un ou l’autre. C’est-à-dire que, dans mon Criteria décrivant cette table, je devrais avoir un « OU » ou une double jointure (je n’imagine même pas comment faire ça en Hibernate … et le forum Hibernate me dit que ça n’est pas de la rigolade). J’ai donc implémenté une solution barbare, en partie liée à mes propres limitations. En effet, dans mon code, les différentes restrictions sont insérées par différentes méthodes, via des createCriteria qui fonctionnent tous, grâce à la classe que j’avais précédemment créée. Au passage, manipuler les critères créés, au moins dans un DetachedCriteria, a un sens certain. En effet, ce DetachedCriteria peut être vu, d’une certaine manière, comme un document qu’on manipule avant de le compiler pour générer des données, exactement comme on manipule un document XML avant de l’enregistrer grâce au DOM. Or, à l’heure actuelle, la classe (Detached)Criteria ne permet que d’ajouter des éléments dedans, comme une espèce de SAX inversé. L’effort à produire n’est pourtant pas bien grand … Puisque je l’ai fait (sans doute salement) dans mon DetachedMemorizerCriteria. Par ailleurs, je trouve l’objet Criteria invraissemblablement difficile à étendre. Oh, bien sûr, on me répondra que ça n’est pas le but. Et pourtant, j’aurais perdu moins de temps pour faire ce que j’ai fait si, par exemple, la méthode getCriteria avait eu une autre visibilité ou, autrement dit, si DetachedCriteria n’était pas une espèce de vache sacrée. Bref …J’ai donc modifié mon DetachedMemorizerCriteria pour pouvoir gérer deux types de critères : ceux avec un ou et ceux avec un et. La grande différence par rapport à une conjonction, c’est que je fais ça au moment où le critère est exécuté, en l’ayant spécifiée à la création. Il est ainsi possible de créer un critère de recherche OU, sans savoir à priori quels criterion seront placés dedans.Et pour faire ça, la grosse feinte, c’est de tracer les sous-critères créés (ce que fait déjà le DetachedMemorizerCriteria), mais aussi les criterions ajoutés, en surchargeant la méthode add comme ça :

public class DetachedmemorizerCriteria extends DetachedCriteria {
private List criterions;
// [...]
public void add(Criterion _criterion) {criterions.add(_criterion);}
// [...]
}

Les Criterions ne sont donc pas ajoutés dans le add mais … directement dans le getExecutableStatement, et ce en tenant compte du type de critère.

Conclusion

Grosse feinte, mais avantage mineur : Avant de me livrer à ces magouilles, une recherche multicritère ne retournait aucun résultat. Maintenant, elle me retourne tous ceux correspondant à au moins un critére. C’est mieux, mais c’est pas encore parfait.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s