16.2. Requêtes SQL nommées

Les requêtes SQL nommées peuvent être définies dans le document de mapping et appelées exactement de la même manière qu'un requête HQL nommée. Dans ce cas, nous n'avons pas besoin d'appeler addEntity().

<sql-query name="persons">
    <return alias="person" class="eg.Person"/>
    SELECT person.NAME AS {person.name},
           person.AGE AS {person.age},
           person.SEX AS {person.sex}
    FROM PERSON person
    WHERE person.NAME LIKE :namePattern
</sql-query>
List people = sess.getNamedQuery("persons")
    .setString("namePattern", namePattern)
    .setMaxResults(50)
    .list();

Les éléments <return-join> et <load-collection> sont respectivement utilisés pour lier des associations et définir des requêtes qui initialisent des collections.

<sql-query name="personsWith">
    <return alias="person" class="eg.Person"/>
    <return-join alias="address" property="person.mailingAddress"/>
    SELECT person.NAME AS {person.name},
           person.AGE AS {person.age},
           person.SEX AS {person.sex},
           address.STREET AS {address.street},
           address.CITY AS {address.city},
           address.STATE AS {address.state},
           address.ZIP AS {address.zip}
    FROM PERSON person
    JOIN ADDRESS address
        ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
    WHERE person.NAME LIKE :namePattern
</sql-query>

Une requête SQL nommée peut retourner une valeur scalaire. Vous devez spécifier l'alias de colonne et le type Hibernate utilisant l'élément <return-scalar> :

<sql-query name="mySqlQuery">
    <return-scalar column="name" type="string"/>
    <return-scalar column="age" type="long"/>
    SELECT p.NAME AS name,
           p.AGE AS age,
    FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>

You can externalize the resultset mapping informations in a <resultset> element to either reuse them across several named queries or through the setResultSetMapping() API.

<resultset name="personAddress">
    <return alias="person" class="eg.Person"/>
    <return-join alias="address" property="person.mailingAddress"/>
</resultset>

<sql-query name="personsWith" resultset-ref="personAddress">
    SELECT person.NAME AS {person.name},
           person.AGE AS {person.age},
           person.SEX AS {person.sex},
           address.STREET AS {address.street},
           address.CITY AS {address.city},
           address.STATE AS {address.state},
           address.ZIP AS {address.zip}
    FROM PERSON person
    JOIN ADDRESS address
        ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
    WHERE person.NAME LIKE :namePattern
</sql-query>

You can alternatively use the resultset mapping information in your hbm files directly in java code.

List cats = sess.createSQLQuery(
        "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
    )
    .setResultSetMapping("catAndKitten")
    .list();

16.2.1. Utilisation de return-property pour spécifier explicitement les noms des colonnes/alias

Avec <return-property> vous pouvez explicitement dire à Hibernate quels alias de colonne utiliser, plutot que d'employer la syntaxe {} pour laisser Hibernate injecter ses propres alias.

<sql-query name="mySqlQuery">
    <return alias="person" class="eg.Person">
        <return-property name="name" column="myName"/>
        <return-property name="age" column="myAge"/>
        <return-property name="sex" column="mySex"/>
    </return>
    SELECT person.NAME AS myName,
           person.AGE AS myAge,
           person.SEX AS mySex,
    FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>

<return-property> fonctionne aussi avec de multiple colonnes. Cela résout une limitation de la syntaxe {} qui ne peut pas permettre une bonne granularité des propriétés multi-colonnes.

<sql-query name="organizationCurrentEmployments">
    <return alias="emp" class="Employment">
        <return-property name="salary">
            <return-column name="VALUE"/>
            <return-column name="CURRENCY"/>
        </return-property>
        <return-property name="endDate" column="myEndDate"/>
    </return>
        SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
        FROM EMPLOYMENT
        WHERE EMPLOYER = :id AND ENDDATE IS NULL
        ORDER BY STARTDATE ASC
</sql-query>

Notez que dans cet exemple nous avons utilisé <return-property> en combinaison avec la syntaxe {} pour l'injection. Cela autorise les utilisateurs à choisir comment ils veulent référencer les colonnes et les propriétés.

Si votre mapping a un discriminant vous devez utiliser <return-discriminator> pour spécifier la colonne discriminante.

16.2.2. Utilisation de procédures stockées pour les requêtes

Hibernate 3 introduit le support des requêtes via procédures stockées et les fonctions. La documentation suivante est valable pour les deux. Les procédures stockées/fonctions doivent retourner l'ensemble de résultats en tant que premier paramètre sortant (NdT: "out-parameter") pour être capable de fonctionner avec Hibernate. Un exemple d'une telle procédure stockée en Oracle 9 et version supérieure :

CREATE OR REPLACE FUNCTION selectAllEmployments
    RETURN SYS_REFCURSOR
AS
    st_cursor SYS_REFCURSOR;
BEGIN
    OPEN st_cursor FOR
 SELECT EMPLOYEE, EMPLOYER,
 STARTDATE, ENDDATE,
 REGIONCODE, EID, VALUE, CURRENCY
 FROM EMPLOYMENT;
      RETURN  st_cursor;
 END;

Pour utiliser cette requête dans Hibernate vous avez besoin de la mapper via une requête nommée.

<sql-query name="selectAllEmployees_SP" callable="true">
    <return alias="emp" class="Employment">
        <return-property name="employee" column="EMPLOYEE"/>
        <return-property name="employer" column="EMPLOYER"/>
        <return-property name="startDate" column="STARTDATE"/>
        <return-property name="endDate" column="ENDDATE"/>
        <return-property name="regionCode" column="REGIONCODE"/>
        <return-property name="id" column="EID"/>
        <return-property name="salary">
            <return-column name="VALUE"/>
            <return-column name="CURRENCY"/>
        </return-property>
    </return>
    { ? = call selectAllEmployments() }
</sql-query>

Notez que les procédures stockées retournent, pour le moment, seulement des scalaires et des entités. <return-join> et <load-collection> ne sont pas supportés.

16.2.2.1. Règles/limitations lors de l'utilisation des procédures stockées

Pur utiliser des procédures stockées avec Hibernate, les procédures doivent suivre certaines règles. Si elles ne suivent pas ces règles, elles ne sont pas utilisables avec Hibernate. Si vous voulez encore utiliser ces procédures vous devez les exécuter via session.connection(). Les règles sont différentes pour chaque base de données, puisque les vendeurs de base de données ont des sémantiques/syntaxes différentes pour les procédures stockées.

Les requêtes de procédures stockées ne peuvent pas être paginées avec setFirstResult()/setMaxResults().

Recommended call form is standard SQL92: { ? = call functionName(<parameters>) } or { ? = call procedureName(<parameters>}. Native call syntax is not supported.

Pour Oracle les règles suivantes s'appliquent :

  • La procédure doit retourner un ensemble de résultats. Le prmeier paramètre d'une procédure doit être un OUT qui retourne un ensemble de résultats. Ceci est fait en retournant un SYS_REFCURSOR dans Oracle 9 ou 10. Dans Oracle vous avez besoin de définir un type REF CURSOR.

Pour Sybase ou MS SQL server les règles suivantes s'appliquent :

  • La procédure doit retourner un ensemble de résultats. Notez que comme ces serveurs peuvent retourner de multiples ensembles de résultats et mettre à jour des compteurs, Hibernate itérera les résultats et prendra le premier résultat qui est un ensemble de résultat comme valeur de retour. Tout le reste sera ignoré.

  • Si vous pouvez activer SET NOCOUNT ON dans votre procédure, elle sera probablement plus efficace, mais ce n'est pas une obligation.