31. Legacy Hibernate Native Queries

31.1. Legacy named SQL queries

Named SQL queries can also be defined during mapping and called in exactly the same way as a named HQL query. In this case, you do not need to call addEntity() anymore.

Example 700. Named sql query using the <sql-query> mapping element

  1. <sql-query name = "persons">
  2. <return alias="person" class="eg.Person"/>
  3. SELECT person.NAME AS {person.name},
  4. person.AGE AS {person.age},
  5. person.SEX AS {person.sex}
  6. FROM PERSON person
  7. WHERE person.NAME LIKE :namePattern
  8. </sql-query>

Example 701. Execution of a named query

  1. List people = session
  2. .getNamedQuery( "persons" )
  3. .setParameter( "namePattern", namePattern )
  4. .setMaxResults( 50 )
  5. .list();

The <return-join> element is use to join associations and the <load-collection> element is used to define queries which initialize collections.

Example 702. Named sql query with association

  1. <sql-query name = "personsWith">
  2. <return alias="person" class="eg.Person"/>
  3. <return-join alias="address" property="person.mailingAddress"/>
  4. SELECT person.NAME AS {person.name},
  5. person.AGE AS {person.age},
  6. person.SEX AS {person.sex},
  7. address.STREET AS {address.street},
  8. address.CITY AS {address.city},
  9. address.STATE AS {address.state},
  10. address.ZIP AS {address.zip}
  11. FROM PERSON person
  12. JOIN ADDRESS address
  13. ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
  14. WHERE person.NAME LIKE :namePattern
  15. </sql-query>

A named SQL query may return a scalar value. You must declare the column alias and Hibernate type using the <return-scalar> element:

Example 703. Named query returning a scalar

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

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

Example 704. mapping used to externalize mapping information

  1. <resultset name = "personAddress">
  2. <return alias="person" class="eg.Person"/>
  3. <return-join alias="address" property="person.mailingAddress"/>
  4. </resultset>
  5. <sql-query name = "personsWith" resultset-ref="personAddress">
  6. SELECT person.NAME AS {person.name},
  7. person.AGE AS {person.age},
  8. person.SEX AS {person.sex},
  9. address.STREET AS {address.street},
  10. address.CITY AS {address.city},
  11. address.STATE AS {address.state},
  12. address.ZIP AS {address.zip}
  13. FROM PERSON person
  14. JOIN ADDRESS address
  15. ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
  16. WHERE person.NAME LIKE :namePattern
  17. </sql-query>

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

Example 705. Programmatically specifying the result mapping information

  1. List cats = session
  2. .createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" )
  3. .setResultSetMapping( "catAndKitten" )
  4. .list();

31.2. Legacy return-property to explicitly specify column/alias names

You can explicitly tell Hibernate what column aliases to use with <return-property>, instead of using the {} syntax to let Hibernate inject its own aliases. For example:

  1. <sql-query name = "mySqlQuery">
  2. <return alias = "person" class = "eg.Person">
  3. <return-property name = "name" column = "myName"/>
  4. <return-property name = "age" column = "myAge"/>
  5. <return-property name = "sex" column = "mySex"/>
  6. </return>
  7. SELECT person.NAME AS myName,
  8. person.AGE AS myAge,
  9. person.SEX AS mySex,
  10. FROM PERSON person WHERE person.NAME LIKE :name
  11. </sql-query>

<return-property> also works with multiple columns. This solves a limitation with the {} syntax which cannot allow fine grained control of multi-column properties.

  1. <sql-query name = "organizationCurrentEmployments">
  2. <return alias = "emp" class = "Employment">
  3. <return-property name = "salary">
  4. <return-column name = "VALUE"/>
  5. <return-column name = "CURRENCY"/>
  6. </return-property>
  7. <return-property name = "endDate" column = "myEndDate"/>
  8. </return>
  9. SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
  10. STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
  11. REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
  12. FROM EMPLOYMENT
  13. WHERE EMPLOYER = :id AND ENDDATE IS NULL
  14. ORDER BY STARTDATE ASC
  15. </sql-query>

In this example <return-property> was used in combination with the {} syntax for injection. This allows users to choose how they want to refer column and properties.

If your mapping has a discriminator you must use <return-discriminator> to specify the discriminator column.

31.3. Legacy stored procedures for querying

Hibernate provides support for queries via stored procedures and functions. Most of the following documentation is equivalent for both. The stored procedure/function must return a resultset as the first out-parameter to be able to work with Hibernate. An example of such a stored function in Oracle 9 and higher is as follows:

  1. CREATE OR REPLACE FUNCTION selectAllEmployments
  2. RETURN SYS_REFCURSOR
  3. AS
  4. st_cursor SYS_REFCURSOR;
  5. BEGIN
  6. OPEN st_cursor FOR
  7. SELECT EMPLOYEE, EMPLOYER,
  8. STARTDATE, ENDDATE,
  9. REGIONCODE, EID, VALUE, CURRENCY
  10. FROM EMPLOYMENT;
  11. RETURN st_cursor;
  12. END;

To use this query in Hibernate you need to map it via a named query.

  1. <sql-query name = "selectAllEmployees_SP" callable = "true">
  2. <return alias="emp" class="Employment">
  3. <return-property name = "employee" column = "EMPLOYEE"/>
  4. <return-property name = "employer" column = "EMPLOYER"/>
  5. <return-property name = "startDate" column = "STARTDATE"/>
  6. <return-property name = "endDate" column = "ENDDATE"/>
  7. <return-property name = "regionCode" column = "REGIONCODE"/>
  8. <return-property name = "id" column = "EID"/>
  9. <return-property name = "salary">
  10. <return-column name = "VALUE"/>
  11. <return-column name = "CURRENCY"/>
  12. </return-property>
  13. </return>
  14. { ? = call selectAllEmployments() }
  15. </sql-query>

Stored procedures currently only return scalars and entities. <return-join> and <load-collection> are not supported.

31.4. Legacy rules/limitations for using stored procedures

You cannot use stored procedures with Hibernate unless you follow some procedure/function rules. If they do not follow those rules they are not usable with Hibernate. If you still want to use these procedures you have to execute them via session.doWork().

The rules are different for each database since database vendors have different stored procedure semantics/syntax.

Stored procedure queries cannot be paged with setFirstResult()/setMaxResults().

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

For Oracle the following rules apply:

  • A function must return a result set.

  • The first parameter of a procedure must be an OUT that returns a result set. This is done by using a SYS_REFCURSOR type in Oracle 9 or 10. In Oracle you need to define a REF CURSOR type. See Oracle literature for further information.

For Sybase or MS SQL server the following rules apply:

  • The procedure must return a result set. Note that since these servers can return multiple result sets and update counts, Hibernate will iterate the results and take the first result that is a result set as its return value. Everything else will be discarded.

  • If you can enable SET NOCOUNT ON in your procedure it will probably be more efficient, but this is not a requirement.

31.5. Legacy custom SQL for create, update and delete

Hibernate can use custom SQL for create, update, and delete operations. The SQL can be overridden at the statement level or individual column level. This section describes statement overrides. For columns, see Column transformers: read and write expressions. The following example shows how to define custom SQL operations using annotations.

Example 706. Custom CRUD XML

  1. <class name = "Person">
  2. <id name = "id">
  3. <generator class = "increment"/>
  4. </id>
  5. <property name = "name" not-null = "true"/>
  6. <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
  7. <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
  8. <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
  9. </class>

If you expect to call a stored procedure, be sure to set the callable attribute to true in both annotation and XML-based mappings.

To check that the execution happens correctly, Hibernate allows you to define one of those three strategies:

  • none: no check is performed; the store procedure is expected to fail upon issues

  • count: use of rowcount to check that the update is successful

  • param: like COUNT but using an output parameter rather that the standard mechanism

To define the result check style, use the check parameter which is again available in annotations as well as in xml.

Last but not least, stored procedures are in most cases required to return the number of rows inserted, updated and deleted. Hibernate always registers the first statement parameter as a numeric output parameter for the CUD operations:

Example 707. Stored procedures and their return value

  1. CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
  2. RETURN NUMBER IS
  3. BEGIN
  4. update PERSON
  5. set
  6. NAME = uname,
  7. where
  8. ID = uid;
  9. return SQL%ROWCOUNT;
  10. END updatePerson;

31.6. Legacy custom SQL for loading

You can also declare your own SQL (or HQL) queries for entity loading. As with inserts, updates, and deletes, this can be done at the individual column level as described in For columns, see Column transformers: read and write expressions or at the statement level. Here is an example of a statement level override:

  1. <sql-query name = "person">
  2. <return alias = "pers" class = "Person" lock-mode= "upgrade"/>
  3. SELECT NAME AS {pers.name}, ID AS {pers.id}
  4. FROM PERSON
  5. WHERE ID=?
  6. FOR UPDATE
  7. </sql-query>

This is just a named query declaration, as discussed earlier. You can reference this named query in a class mapping:

  1. <class name = "Person">
  2. <id name = "id">
  3. <generator class = "increment"/>
  4. </id>
  5. <property name = "name" not-null = "true"/>
  6. <loader query-ref = "person"/>
  7. </class>

This even works with stored procedures.

You can even define a query for collection loading:

  1. <set name = "employments" inverse = "true">
  2. <key/>
  3. <one-to-many class = "Employment"/>
  4. <loader query-ref = "employments"/>
  5. </set>
  1. <sql-query name = "employments">
  2. <load-collection alias = "emp" role = "Person.employments"/>
  3. SELECT {emp.*}
  4. FROM EMPLOYMENT emp
  5. WHERE EMPLOYER = :id
  6. ORDER BY STARTDATE ASC, EMPLOYEE ASC
  7. </sql-query>

You can also define an entity loader that loads a collection by join fetching:

  1. <sql-query name = "person">
  2. <return alias = "pers" class = "Person"/>
  3. <return-join alias = "emp" property = "pers.employments"/>
  4. SELECT NAME AS {pers.*}, {emp.*}
  5. FROM PERSON pers
  6. LEFT OUTER JOIN EMPLOYMENT emp
  7. ON pers.ID = emp.PERSON_ID
  8. WHERE ID=?
  9. </sql-query>