Si vous ne connaissez par les identifiants des objets que vous recherchez, vous avez besoin d'une requête. Hibernate supporte un langage de requêtes orientées objet facile à utiliser mais puissant. Pour la création de requêtes par programmation, Hibernate supporte une fonction de requêtage sophistiqué Criteria et Example (QBC et QBE). Vous pouvez aussi exprimez votre requête dans le SQL natif de votre base de données, avec un support optionnel d'Hibernate pour la conversion des ensembles de résultats en objets.
Les requêtes HQL et SQL natives sont représentées avec une instance de org.hibernate.Query
. L'interface offre des méthodes pour la liaison des paramètres, la gestion des ensembles de resultats, et pour l'exécution
de la requête réelle. Vous obtenez toujours une Query
en utilisant la Session
courante :
List cats = session.createQuery( "from Cat as cat where cat.birthdate < ?") .setDate(0, date) .list(); List mothers = session.createQuery( "select mother from Cat as cat join cat.mother as mother where cat.name = ?") .setString(0, name) .list(); List kittens = session.createQuery( "from Cat as cat where cat.mother = ?") .setEntity(0, pk) .list(); Cat mother = (Cat) session.createQuery( "select cat.mother from Cat as cat where cat = ?") .setEntity(0, izi) .uniqueResult();]] Query mothersWithKittens = (Cat) session.createQuery( "select mother from Cat as mother left join fetch mother.kittens"); Set uniqueMothers = new HashSet(mothersWithKittens.list());
Une requête est généralement exécutée en invoquant list()
, le résultat de la requête sera chargée complètement dans une collection en mémoire. Les intances d'entités recupérées par
une requête sont dans un état persistant. La méthode uniqueResult()
offre un raccourci si vous savez que votre requête retournera seulement un seul objet.
Occasionnellement, vous pourriez être capable d'obtenir de meilleures performances en exécutant la requête avec la méthode
iterate()
. Ce sera généralement seulement le cas si vous espérez que les intances réelles d'entité retournées par la requête soient
déjà chargées dans la session ou le cache de second niveau. Si elles ne sont pas cachées, iterate()
sera plus lent que list()
et pourrait nécessiter plusieurs accès à la base de données pour une simple requête, généralement 1 pour le select initial qui retourne seulement les identifiants, et n selects supplémentaires pour initialiser les instances réelles.
// fetch ids Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate(); while ( iter.hasNext() ) { Qux qux = (Qux) iter.next(); // fetch the object // something we couldnt express in the query if ( qux.calculateComplicatedAlgorithm() ) { // delete the current instance iter.remove(); // dont need to process the rest break; } }
Les requêtes d'Hibernate retournent parfois des tuples d'objets, auquel cas chaque tuple est retourné comme un tableau :
Iterator kittensAndMothers = sess.createQuery( "select kitten, mother from Cat kitten join kitten.mother mother") .list() .iterator(); while ( kittensAndMothers.hasNext() ) { Object[] tuple = (Object[]) kittensAndMothers.next(); Cat kitten = (Cat) tuple[0]; Cat mother = (Cat) tuple[1]; .... }
Des requêtes peuvent spécifier une propriété d'une classe dans la clause select
. Elles peuvent même appeler des fonctions d'aggrégat SQL. Les propriétés ou les aggrégats sont considérés comme des résultats
"scalaires" (et pas des entités dans un état persistant).
Iterator results = sess.createQuery( "select cat.color, min(cat.birthdate), count(cat) from Cat cat " + "group by cat.color") .list() .iterator(); while ( results.hasNext() ) { Object[] row = (Object[]) results.next(); Color type = (Color) row[0]; Date oldest = (Date) row[1]; Integer count = (Integer) row[2]; ..... }
Des méthodes de Query
sont fournies pour lier des valeurs à des paramètres nommés ou à des paramètres de style JDBC ?
. Contrairement à JDBC, les numéros des paramètres d'Hibernate commencent à zéro. Les paramètres nommés sont des identifiants de la forme :nom
dans la chaîne de caractères de la requête. Les avantages des paramètres nommés sont :
les paramètres nommés sont insensibles à l'ordre de leur place dans la chaîne de la requête
ils peuvent apparaître plusieurs fois dans la même requête
ils sont auto-documentés
//named parameter (preferred) Query q = sess.createQuery("from DomesticCat cat where cat.name = :name"); q.setString("name", "Fritz"); Iterator cats = q.iterate();
//positional parameter Query q = sess.createQuery("from DomesticCat cat where cat.name = ?"); q.setString(0, "Izi"); Iterator cats = q.iterate();
//named parameter list List names = new ArrayList(); names.add("Izi"); names.add("Fritz"); Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)"); q.setParameterList("namesList", names); List cats = q.list();
Si vous avez besoin de spécifier des liens sur votre ensemble de résultats (le nombre maximum de lignes que vous voulez récupérez
et/ou la première ligne que vous voulez récupérer) vous devriez utiliser des méthodes de l'interface Query
:
Query q = sess.createQuery("from DomesticCat cat"); q.setFirstResult(20); q.setMaxResults(10); List cats = q.list();
Hibernate sait comment traduite cette requête de limite en SQL natif pour votre SGBD.
Si votre connecteur JDBC supporte les ResultSet
s "scrollables", l'interface Query
peut être utilisée pour obtenir un objet ScrollableResults
, lequel permet une navigation flexible dans les résultats de la requête.
Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " + "order by cat.name"); ScrollableResults cats = q.scroll(); if ( cats.first() ) { // find the first name on each page of an alphabetical list of cats by name firstNamesOfPages = new ArrayList(); do { String name = cats.getString(0); firstNamesOfPages.add(name); } while ( cats.scroll(PAGE_SIZE) ); // Now get the first page of cats pageOfCats = new ArrayList(); cats.beforeFirst(); int i=0; while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) ); } cats.close()
Notez qu'une connexion ouverte (et un curseur) est requise pour cette fonctionnalité, utilisez setMaxResult()
/setFirstResult()
si vous avez besoin d'une fonctionnalité de pagination hors ligne.
Vous pouvez aussi définir des requêtes nommées dans le document de mapping. (Souvenez-vous d'utiliser une section CDATA
si votre requête contient des caractères qui pourraient être interprétés comme des éléments XML.)
<query name="ByNameAndMaximumWeight"><![CDATA[ from eg.DomesticCat as cat where cat.name = ? and cat.weight > ? ] ]></query>
La liaison de paramètres et l'exécution sont fait par programmation :
Query q = sess.getNamedQuery("ByNameAndMaximumWeight"); q.setString(0, name); q.setInt(1, minWeight); List cats = q.list();
Notez que le code réel du programme est indépendant du langage de requête qui est utilisé, vous pouvez aussi définir des requêtes SQL nativez dans les méta-données, ou migrer des requêtes existantes vers Hibernate en les plaçant dans les fichiers de mapping.
UNTRANSLATED! Also note that a query declaration inside a <hibernate-mapping>
element requires a global unique name for the query, while a query declaration inside a <class>
element is made unique automatically by prepending the fully qualified name of the class, for example eg.Cat.ByNameAndMaximumWeight
.
Un filtre de collection est un type spécial de requête qui peut être appliqué à une collection persistante ou à un tableau. La chaîne
de requête peut se référer à this
, correspondant à l'élément de la collection courant.
Collection blackKittens = session.createFilter( pk.getKittens(), "where this.color = ?") .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) ) .list() );
La collection retournée est considérée comme un bag, et c'est une copie de la collection donnée. La collection originale n'est pas modifiée (c'est contraire à l'implication du nom "filtre"; mais cohérent avec le comportement attendu).
Observez que les filtres ne nécessitent pas une clause from
(bien qu'ils puissent en avoir une si besoin est). Les filtres ne sont pas limités à retourner des éléments de la collection
eux-mêmes.
Collection blackKittenMates = session.createFilter( pk.getKittens(), "select this.mate where this.color = eg.Color.BLACK.intValue") .list();
Même une requête de filtre vide est utile, par exemple pour charger un sous-ensemble d'éléments dans une énorme collection :
Collection tenKittens = session.createFilter( mother.getKittens(), "") .setFirstResult(0).setMaxResults(10) .list();
HQL est extrêmement puissant mais certains développeurs préfèrent construire des requêtes dynamiquement, en utilisant l'API
orientée objet, plutôt que construire des chaînes de requêtes. Hibernate fournit une API intuitive de requête Criteria
pour ces cas :
Criteria crit = session.createCriteria(Cat.class); crit.add( Restrictions.eq( "color", eg.Color.BLACK ) ); crit.setMaxResults(10); List cats = crit.list();
Les APIs Criteria
et Example
associé sont traitées plus en détail dans Chapitre 15, Requêtes par critères.
Vous pouvez exprimer une requête en SQL, en utilisant createSQLQuery()
et laisser Hibernate s'occuper du mapping des résultats vers des objets. Notez que vous pouvez n'importe quand appeler session.connection()
et utiliser directement la Connection
JDBC. Si vous choisissez d'utiliser l'API Hibernate, vous devez mettre les alias SQL entre accolades :
List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10") .addEntity("cat", Cat.class) .list();
List cats = session.createSQLQuery( "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " + "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " + "FROM CAT {cat} WHERE ROWNUM<10") .addEntity("cat", Cat.class) .list()
Les requêtes SQL peuvent contenir des paramètres nommés et positionnels, comme des requêtes Hibernate. Plus d'informations à propos des requêtes SQL natives dans Hibernate peuvent être trouvées dans Chapitre 16, SQL natif.