Add doc for the resulet mapping definition

git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@7530 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Emmanuel Bernard 2005-07-18 04:16:02 +00:00
parent 0bf63e950b
commit 497d4dff4d
1 changed files with 459 additions and 452 deletions

View File

@ -1,124 +1,112 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<chapter id="querysql" revision="2"> <chapter id="querysql" revision="2">
<title>Native SQL</title> <title>Native SQL</title>
<para> <para>You may also express queries in the native SQL dialect of your
You may also express queries in the native SQL dialect of your database. This is useful if you database. This is useful if you want to utilize database specific features
want to utilize database specific features such as query hints or the <literal>CONNECT</literal> such as query hints or the <literal>CONNECT</literal> keyword in Oracle. It
keyword in Oracle. It also provides a clean migration path from a direct SQL/JDBC based also provides a clean migration path from a direct SQL/JDBC based
application to Hibernate. application to Hibernate.</para>
</para>
<para> <para>Hibernate3 allows you to specify handwritten SQL (including stored
Hibernate3 allows you to specify handwritten SQL (including stored procedures) for procedures) for all create, update, delete, and load operations.</para>
all create, update, delete, and load operations.
</para>
<sect1 id="querysql-creating" revision="2"> <sect1 id="querysql-creating" revision="3">
<title>Using a <literal>SQLQuery</literal></title> <title>Using a <literal>SQLQuery</literal></title>
<para> <para>Execution of native SQL queries is controlled via the
Execution of native SQL queries is controlled via the <literal>SQLQuery</literal> interface, <literal>SQLQuery</literal> interface, which is obtained by calling
which is obtained by calling <literal>Session.createSQLQuery()</literal>. In extremely <literal>Session.createSQLQuery()</literal>. In extremely simple cases, we
simple cases, we can use the following form: can use the following form:</para>
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select * from cats") <programlisting>List cats = sess.createSQLQuery("select * from cats")
.addEntity(Cat.class) .addEntity(Cat.class)
.list();]]></programlisting> .list();</programlisting>
<para> <para>This query specified:</para>
This query specified:
</para>
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>the SQL query string</para>
the SQL query string
</para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>the entity returned by the query</para>
the entity returned by the query
</para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
<para> <para>Here, the result set column names are assumed to be the same as the
Here, the result set column names are assumed to be the same as the column names column names specified in the mapping document. This can be problematic
specified in the mapping document. This can be problematic for SQL queries which for SQL queries which join multiple tables, since the same column names
join multiple tables, since the same column names may appear in more than one may appear in more than one table. The following form is not vulnerable to
table. The following form is not vulnerable to column name duplication: column name duplication:</para>
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat") <programlisting>List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class) .addEntity("cat", Cat.class)
.list();]]></programlisting> .list();</programlisting>
<para> <para>This query specified:</para>
This query specified:
</para>
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>the SQL query string, with a placeholder for Hibernate to inject
the SQL query string, with a placeholder for Hibernate to inject the column aliases the column aliases</para>
</para>
</listitem> </listitem>
<listitem>
<para>
the entity returned by the query, and its SQL table alias
</para> <listitem>
<para>the entity returned by the query, and its SQL table alias</para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
<para> <para>The <literal>addEntity()</literal> method associates the SQL table
The <literal>addEntity()</literal> method associates the SQL table alias with the returned alias with the returned entity class, and determines the shape of the
entity class, and determines the shape of the query result set. query result set.</para>
</para>
<para> <para>The <literal>addJoin()</literal> method may be used to load
The <literal>addJoin()</literal> method may be used to load associations to other entities associations to other entities and collections.</para>
and collections.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery( <programlisting>List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
) )
.addEntity("cat", Cat.class) .addEntity("cat", Cat.class)
.addJoin("kitten", "cat.kittens") .addJoin("kitten", "cat.kittens")
.list();]]></programlisting> .list();</programlisting>
<para> <para>A native SQL query might return a simple scalar value or a
A native SQL query might return a simple scalar value or a combination of scalars and combination of scalars and entities.</para>
entities.
</para>
<programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat") <programlisting>Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE); .addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();]]></programlisting> .uniqueResult();</programlisting>
<para>You can alternatively describe the resultset mapping informations in
your hbm files and use them for your queries</para>
<programlisting>List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();</programlisting>
</sect1> </sect1>
<sect1 id="querysql-aliasreferences"> <sect1 id="querysql-aliasreferences">
<title>Alias and property references</title> <title>Alias and property references</title>
<para> <para>The <literal>{cat.*}</literal> notation used above is a shorthand
The <literal>{cat.*}</literal> notation used above is a shorthand for "all properties". for "all properties". Alternatively, you may list the columns explicity,
Alternatively, you may list the columns explicity, but even this case we let Hibernate but even this case we let Hibernate inject the SQL column aliases for each
inject the SQL column aliases for each property. The placeholder for a column alias is property. The placeholder for a column alias is just the property name
just the property name qualified by the table alias. In the following example, we retrieve qualified by the table alias. In the following example, we retrieve
<literal>Cat</literal>s from a different table (<literal>cat_log</literal>) to the one <literal>Cat</literal>s from a different table
declared in the mapping metadata. Notice that we may even use the property aliases in the (<literal>cat_log</literal>) to the one declared in the mapping metadata.
where clause if we like. Notice that we may even use the property aliases in the where clause if we
</para> like.</para>
<para>
The <literal>{}</literal>-syntax is <emphasis>not</emphasis> required for named queries.
See <xref linkend="querysql-namedqueries"/>
</para>
<programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " + <para>The <literal>{}</literal>-syntax is <emphasis>not</emphasis>
required for named queries. See <xref
linkend="querysql-namedqueries" /></para>
<programlisting>String sql = "select cat.originalId as {cat.id}, " +
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " + "cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " + "cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
"from cat_log cat where {cat.mate} = :catId" "from cat_log cat where {cat.mate} = :catId"
@ -126,123 +114,154 @@
List loggedCats = sess.createSQLQuery(sql) List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class) .addEntity("cat", Cat.class)
.setLong("catId", catId) .setLong("catId", catId)
.list();]]></programlisting> .list();</programlisting>
<para> <para><emphasis>Note:</emphasis> if you list each property explicitly, you
<emphasis>Note:</emphasis> if you list each property explicitly, you must include all must include all properties of the class <emphasis>and its
properties of the class <emphasis>and its subclasses</emphasis>! subclasses</emphasis>!</para>
</para>
<para> <para>The following table shows the different possibilities of using the
The following table shows the different possibilities of using the alias injection. Note: the alias names in the result alias injection. Note: the alias names in the result are examples, each
are examples, each alias will have a unique and probably different name when used. alias will have a unique and probably different name when used.</para>
</para>
<table frame="topbot" id="aliasinjection-summary"> <table frame="topbot" id="aliasinjection-summary">
<title>Alias injection names</title> <title>Alias injection names</title>
<tgroup cols="3">
<tgroup cols="4">
<colspec colwidth="1*" /> <colspec colwidth="1*" />
<colspec colwidth="1*" /> <colspec colwidth="1*" />
<colspec colwidth="2.5*" /> <colspec colwidth="2.5*" />
<thead> <thead>
<row> <row>
<entry>Description</entry> <entry>Description</entry>
<entry>Syntax</entry> <entry>Syntax</entry>
<entry>Example</entry> <entry>Example</entry>
</row> </row>
</thead> </thead>
<tbody> <tbody>
<row> <row>
<entry>A simple property</entry> <entry>A simple property</entry>
<entry><literal>{[aliasname].[propertyname]</literal></entry> <entry><literal>{[aliasname].[propertyname]</literal></entry>
<entry><literal>A_NAME as {item.name}</literal></entry> <entry><literal>A_NAME as {item.name}</literal></entry>
</row> </row>
<row> <row>
<entry>A composite property</entry> <entry>A composite property</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry> <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
<entry><literal>CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}</literal></entry>
<entry><literal>CURRENCY as {item.amount.currency}, VALUE as
{item.amount.value}</literal></entry>
</row> </row>
<row> <row>
<entry>Discriminator of an entity</entry> <entry>Discriminator of an entity</entry>
<entry><literal>{[aliasname].class}</literal></entry> <entry><literal>{[aliasname].class}</literal></entry>
<entry><literal>DISC as {item.class}</literal></entry> <entry><literal>DISC as {item.class}</literal></entry>
</row> </row>
<row> <row>
<entry>All properties of an entity</entry> <entry>All properties of an entity</entry>
<entry><literal>{[aliasname].*}</literal></entry> <entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{item.*}</literal></entry> <entry><literal>{item.*}</literal></entry>
</row> </row>
<row> <row>
<entry>A collection key</entry> <entry>A collection key</entry>
<entry><literal>{[aliasname].key}</literal></entry> <entry><literal>{[aliasname].key}</literal></entry>
<entry><literal>ORGID as {coll.key}</literal></entry> <entry><literal>ORGID as {coll.key}</literal></entry>
</row> </row>
<row> <row>
<entry>The id of an collection</entry> <entry>The id of an collection</entry>
<entry><literal>{[aliasname].id}</literal></entry> <entry><literal>{[aliasname].id}</literal></entry>
<entry><literal>EMPID as {coll.id}</literal></entry> <entry><literal>EMPID as {coll.id}</literal></entry>
</row> </row>
<row> <row>
<entry>The element of an collection</entry> <entry>The element of an collection</entry>
<entry><literal>{[aliasname].element}</literal></entry> <entry><literal>{[aliasname].element}</literal></entry>
<entry><literal>XID as {coll.element}</literal></entry> <entry><literal>XID as {coll.element}</literal></entry>
<entry></entry> <entry></entry>
</row> </row>
<row> <row>
<entry>Property of the element in the collection</entry> <entry>Property of the element in the collection</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry> <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
<entry><literal>NAME as {coll.element.name}</literal></entry> <entry><literal>NAME as {coll.element.name}</literal></entry>
</row> </row>
<row> <row>
<entry>All properties of the element in the collection</entry> <entry>All properties of the element in the collection</entry>
<entry><literal>{[aliasname].element.*}</literal></entry> <entry><literal>{[aliasname].element.*}</literal></entry>
<entry><literal>{coll.element.*}</literal></entry> <entry><literal>{coll.element.*}</literal></entry>
</row> </row>
<row> <row>
<entry>All properties of the the collection</entry> <entry>All properties of the the collection</entry>
<entry><literal>{[aliasname].*}</literal></entry> <entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{coll.*}</literal></entry> <entry><literal>{coll.*}</literal></entry>
</row> </row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
</sect1> </sect1>
<sect1 id="querysql-namedqueries" revision="3">
<sect1 id="querysql-namedqueries" revision="2">
<title>Named SQL queries</title> <title>Named SQL queries</title>
<para> <para>Named SQL queries may be defined in the mapping document and called
Named SQL queries may be defined in the mapping document and called in exactly the in exactly the same way as a named HQL query. In this case, we do
same way as a named HQL query. In this case, we do <emphasis>not</emphasis> need <emphasis>not</emphasis> need to call
to call <literal>addEntity()</literal>. <literal>addEntity()</literal>.</para>
</para>
<programlisting><![CDATA[<sql-query name="persons"> <programlisting>&lt;sql-query name="persons"&gt;
<return alias="person" class="eg.Person"/> &lt;return alias="person" class="eg.Person"/&gt;
SELECT person.NAME AS {person.name}, SELECT person.NAME AS {person.name},
person.AGE AS {person.age}, person.AGE AS {person.age},
person.SEX AS {person.sex} person.SEX AS {person.sex}
FROM PERSON person FROM PERSON person
WHERE person.NAME LIKE :namePattern WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<programlisting><![CDATA[List people = sess.getNamedQuery("persons") <programlisting>List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern) .setString("namePattern", namePattern)
.setMaxResults(50) .setMaxResults(50)
.list();]]></programlisting> .list();</programlisting>
<para> <para>The <literal>&lt;return-join&gt;</literal> and
The <literal>&lt;return-join&gt;</literal> and <literal>&lt;load-collection&gt;</literal> <literal>&lt;load-collection&gt;</literal> elements are used to join
elements are used to join associations and define queries which initialize collections, associations and define queries which initialize collections,
respectively. respectively.</para>
</para>
<programlisting><![CDATA[<sql-query name="personsWith"> <programlisting>&lt;sql-query name="personsWith"&gt;
<return alias="person" class="eg.Person"/> &lt;return alias="person" class="eg.Person"/&gt;
<return-join alias="address" property="person.mailingAddress"/> &lt;return-join alias="address" property="person.mailingAddress"/&gt;
SELECT person.NAME AS {person.name}, SELECT person.NAME AS {person.name},
person.AGE AS {person.age}, person.AGE AS {person.age},
person.SEX AS {person.sex}, person.SEX AS {person.sex},
@ -254,87 +273,106 @@ List loggedCats = sess.createSQLQuery(sql)
JOIN ADDRESS adddress JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<para> <para>A named SQL query may return a scalar value. You must specfy the
A named SQL query may return a scalar value. You must specfy the column alias column alias and Hibernate type using the
and Hibernate type using the <literal>&lt;return-scalar&gt;</literal> element: <literal>&lt;return-scalar&gt;</literal> element:</para>
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery"> <programlisting>&lt;sql-query name="mySqlQuery"&gt;
<return-scalar column="name" type="string"/> &lt;return-scalar column="name" type="string"/&gt;
<return-scalar column="age" type="long"/> &lt;return-scalar column="age" type="long"/&gt;
SELECT p.NAME AS name, SELECT p.NAME AS name,
p.AGE AS age, p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%' FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<para>You can externalize the resultset mapping informations in a
<literal>&lt;resultset&gt;</literal> element to either reuse them accross
several named queries or through the
<literal>setResultSetMapping()</literal> API.</para>
<programlisting>&lt;resultset name="personAddress"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
&lt;return-join alias="address" property="person.mailingAddress"/&gt;
&lt;/resultset&gt;
&lt;sql-query name="personsWith" resultset-ref="personAddress"&gt;
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
<sect2 id="propertyresults"> <sect2 id="propertyresults">
<title>Using return-property to explicitly specify column/alias names</title> <title>Using return-property to explicitly specify column/alias
names</title>
<para> <para>With <literal>&lt;return-property&gt;</literal> you can explicitly
With <literal>&lt;return-property&gt;</literal> you can explicitly tell Hibernate what column tell Hibernate what column aliases to use, instead of using the
aliases to use, instead of using the <literal>{}</literal>-syntax to let Hibernate inject its <literal>{}</literal>-syntax to let Hibernate inject its own
own aliases. aliases.</para>
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery"> <programlisting>&lt;sql-query name="mySqlQuery"&gt;
<return alias="person" class="eg.Person"> &lt;return alias="person" class="eg.Person"&gt;
<return-property name="name" column="myName"/> &lt;return-property name="name" column="myName"/&gt;
<return-property name="age" column="myAge"/> &lt;return-property name="age" column="myAge"/&gt;
<return-property name="sex" column="mySex"/> &lt;return-property name="sex" column="mySex"/&gt;
</return> &lt;/return&gt;
SELECT person.NAME AS myName, SELECT person.NAME AS myName,
person.AGE AS myAge, person.AGE AS myAge,
person.SEX AS mySex, person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name FROM PERSON person WHERE person.NAME LIKE :name
</sql-query> &lt;/sql-query&gt;
]]></programlisting> </programlisting>
<para> <para><literal>&lt;return-property&gt;</literal> also works with
<literal>&lt;return-property&gt;</literal> also works with multiple columns. This solves a multiple columns. This solves a limitation with the
limitation with the <literal>{}</literal>-syntax which can not allow fine grained control <literal>{}</literal>-syntax which can not allow fine grained control of
of multi-column properties. multi-column properties.</para>
</para>
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments"> <programlisting>&lt;sql-query name="organizationCurrentEmployments"&gt;
<return alias="emp" class="Employment"> &lt;return alias="emp" class="Employment"&gt;
<return-property name="salary"> &lt;return-property name="salary"&gt;
<return-column name="VALUE"/> &lt;return-column name="VALUE"/&gt;
<return-column name="CURRENCY"/> &lt;return-column name="CURRENCY"/&gt;
</return-property> &lt;/return-property&gt;
<return-property name="endDate" column="myEndDate"/> &lt;return-property name="endDate" column="myEndDate"/&gt;
</return> &lt;/return&gt;
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}, SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate}, STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC ORDER BY STARTDATE ASC
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<para> <para>Notice that in this example we used
Notice that in this example we used <literal>&lt;return-property&gt;</literal> in combination <literal>&lt;return-property&gt;</literal> in combination with the
with the <literal>{}</literal>-syntax for injection. Allowing users to choose <literal>{}</literal>-syntax for injection. Allowing users to choose how
how they want to refer column and properties. they want to refer column and properties.</para>
</para>
<para> <para>If your mapping has a discriminator you must use
If your mapping has a discriminator you must use <literal>&lt;return-discriminator&gt;</literal> <literal>&lt;return-discriminator&gt;</literal> to specify the
to specify the discriminator column. discriminator column.</para>
</para>
</sect2> </sect2>
<sect2 id="sp_query"> <sect2 id="sp_query">
<title>Using stored procedures for querying</title> <title>Using stored procedures for querying</title>
<para> <para>Hibernate 3 introduces support for queries via stored procedures.
Hibernate 3 introduces support for queries via stored procedures. The stored procedures must The stored procedures must return a resultset as the first out-parameter
return a resultset as the first out-parameter to be able to work with Hibernate. An example to be able to work with Hibernate. An example of such a stored procedure
of such a stored procedure in Oracle 9 and higher is as follows: in Oracle 9 and higher is as follows:</para>
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments <programlisting>CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR RETURN SYS_REFCURSOR
AS AS
st_cursor SYS_REFCURSOR; st_cursor SYS_REFCURSOR;
@ -345,156 +383,138 @@ BEGIN
REGIONCODE, EID, VALUE, CURRENCY REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT; FROM EMPLOYMENT;
RETURN st_cursor; RETURN st_cursor;
END;]]></programlisting> END;</programlisting>
<para> <para>To use this query in Hibernate you need to map it via a named
To use this query in Hibernate you need to map it via a named query. query.</para>
</para>
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true"> <programlisting>&lt;sql-query name="selectAllEmployees_SP" callable="true"&gt;
<return alias="emp" class="Employment"> &lt;return alias="emp" class="Employment"&gt;
<return-property name="employee" column="EMPLOYEE"/> &lt;return-property name="employee" column="EMPLOYEE"/&gt;
<return-property name="employer" column="EMPLOYER"/> &lt;return-property name="employer" column="EMPLOYER"/&gt;
<return-property name="startDate" column="STARTDATE"/> &lt;return-property name="startDate" column="STARTDATE"/&gt;
<return-property name="endDate" column="ENDDATE"/> &lt;return-property name="endDate" column="ENDDATE"/&gt;
<return-property name="regionCode" column="REGIONCODE"/> &lt;return-property name="regionCode" column="REGIONCODE"/&gt;
<return-property name="id" column="EID"/> &lt;return-property name="id" column="EID"/&gt;
<return-property name="salary"> &lt;return-property name="salary"&gt;
<return-column name="VALUE"/> &lt;return-column name="VALUE"/&gt;
<return-column name="CURRENCY"/> &lt;return-column name="CURRENCY"/&gt;
</return-property> &lt;/return-property&gt;
</return> &lt;/return&gt;
{ ? = call selectAllEmployments() } { ? = call selectAllEmployments() }
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<para> <para>Notice stored procedures currently only return scalars and
Notice stored procedures currently only return scalars and entities. entities. <literal>&lt;return-join&gt;</literal> and
<literal>&lt;return-join&gt;</literal> and <literal>&lt;load-collection&gt;</literal> <literal>&lt;load-collection&gt;</literal> are not supported.</para>
are not supported.
</para>
<sect3 id="querysql-limits-storedprocedures"> <sect3 id="querysql-limits-storedprocedures">
<title>Rules/limitations for using stored procedures</title> <title>Rules/limitations for using stored procedures</title>
<para> <para>To use stored procedures with Hibernate the procedures have to
To use stored procedures with Hibernate the procedures have to follow some rules. follow some rules. If they do not follow those rules they are not
If they do not follow those rules they are not usable with Hibernate. If you still usable with Hibernate. If you still want to use these procedures you
want to use these procedures you have to execute them via <literal>session.connection()</literal>. have to execute them via <literal>session.connection()</literal>. The
The rules are different for each database, since database vendors have different stored rules are different for each database, since database vendors have
procedure semantics/syntax. different stored procedure semantics/syntax.</para>
</para>
<para> <para>Stored procedure queries can't be paged with
Stored procedure queries can't be paged with <literal>setFirstResult()/setMaxResults()</literal>. <literal>setFirstResult()/setMaxResults()</literal>.</para>
</para>
<para> <para>For Oracle the following rules apply:</para>
For Oracle the following rules apply:
</para>
<itemizedlist spacing="compact"> <itemizedlist spacing="compact">
<listitem> <listitem>
<para> <para>The procedure must return a result set. This is done by
The procedure must return a result set. This is done by returning a returning a <literal>SYS_REFCURSOR</literal> in Oracle 9 or 10. In
<literal>SYS_REFCURSOR</literal> in Oracle 9 or 10. In Oracle you Oracle you need to define a <literal>REF CURSOR</literal>
need to define a <literal>REF CURSOR</literal> type. type.</para>
</para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>Recommended form is <literal>{ ? = call
Recommended form is <literal>{ ? = call procName(&lt;parameters&gt;) }</literal> procName(&lt;parameters&gt;) }</literal> or <literal>{ ? = call
or <literal>{ ? = call procName }</literal> (this is more an Oracle rule than a procName }</literal> (this is more an Oracle rule than a Hibernate
Hibernate rule). rule).</para>
</para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
<para> <para>For Sybase or MS SQL server the following rules apply:</para>
For Sybase or MS SQL server the following rules apply:
</para>
<itemizedlist spacing="compact"> <itemizedlist spacing="compact">
<listitem> <listitem>
<para> <para>The procedure must return a result set. Note that since
The procedure must return a result set. Note that since these servers can/will return multiple these servers can/will return multiple result sets and update
result sets and update counts, Hibernate will iterate the results and take the first result that counts, Hibernate will iterate the results and take the first
is a result set as its return value. Everything else will be discarded. result that is a result set as its return value. Everything else
</para> will be discarded.</para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>If you can enable <literal>SET NOCOUNT ON</literal> in your
If you can enable <literal>SET NOCOUNT ON</literal> in your procedure it will probably be procedure it will probably be more efficient, but this is not a
more efficient, but this is not a requirement. requirement.</para>
</para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
</sect3> </sect3>
</sect2> </sect2>
</sect1> </sect1>
<sect1 id="querysql-cud"> <sect1 id="querysql-cud">
<title>Custom SQL for create, update and delete</title> <title>Custom SQL for create, update and delete</title>
<para> <para>Hibernate3 can use custom SQL statements for create, update, and
Hibernate3 can use custom SQL statements for create, update, and delete operations. delete operations. The class and collection persisters in Hibernate
The class and collection persisters in Hibernate already contain a set of configuration already contain a set of configuration time generated strings (insertsql,
time generated strings (insertsql, deletesql, updatesql etc.). The mapping tags deletesql, updatesql etc.). The mapping tags
<literal>&lt;sql-insert&gt;</literal>, <literal>&lt;sql-delete&gt;</literal>, and <literal>&lt;sql-insert&gt;</literal>,
<literal>&lt;sql-update&gt;</literal> override these strings: <literal>&lt;sql-delete&gt;</literal>, and
</para> <literal>&lt;sql-update&gt;</literal> override these strings:</para>
<programlisting><![CDATA[<class name="Person"> <programlisting>&lt;class name="Person"&gt;
<id name="id"> &lt;id name="id"&gt;
<generator class="increment"/> &lt;generator class="increment"/&gt;
</id> &lt;/id&gt;
<property name="name" not-null="true"/> &lt;property name="name" not-null="true"/&gt;
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert> &lt;sql-insert&gt;INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )&lt;/sql-insert&gt;
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update> &lt;sql-update&gt;UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?&lt;/sql-update&gt;
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete> &lt;sql-delete&gt;DELETE FROM PERSON WHERE ID=?&lt;/sql-delete&gt;
</class>]]></programlisting> &lt;/class&gt;</programlisting>
<para> <para>The SQL is directly executed in your database, so you are free to
The SQL is directly executed in your database, so you are free to use any dialect use any dialect you like. This will of course reduce the portability of
you like. This will of course reduce the portability of your mapping if you use database your mapping if you use database specific SQL.</para>
specific SQL.
</para>
<para> <para>Stored procedures are supported if the <literal>callable</literal>
Stored procedures are supported if the <literal>callable</literal> attribute is set: attribute is set:</para>
</para>
<programlisting><![CDATA[<class name="Person"> <programlisting>&lt;class name="Person"&gt;
<id name="id"> &lt;id name="id"&gt;
<generator class="increment"/> &lt;generator class="increment"/&gt;
</id> &lt;/id&gt;
<property name="name" not-null="true"/> &lt;property name="name" not-null="true"/&gt;
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert> &lt;sql-insert callable="true"&gt;{call createPerson (?, ?)}&lt;/sql-insert&gt;
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete> &lt;sql-delete callable="true"&gt;{? = call deletePerson (?)}&lt;/sql-delete&gt;
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update> &lt;sql-update callable="true"&gt;{? = call updatePerson (?, ?)}&lt;/sql-update&gt;
</class>]]></programlisting> &lt;/class&gt;</programlisting>
<para> <para>The order of the positional parameters are currently vital, as they
The order of the positional parameters are currently vital, as they must be in must be in the same sequence as Hibernate expects them.</para>
the same sequence as Hibernate expects them.
</para>
<para> <para>You can see the expected order by enabling debug logging for the
You can see the expected order by enabling debug logging for the <literal>org.hibernate.persister.entity</literal> level. With this level
<literal>org.hibernate.persister.entity</literal> level. With this level enabled enabled Hibernate will print out the static SQL that is used to create,
Hibernate will print out the static SQL that is used to create, update, delete etc. update, delete etc. entities. (To see the expected sequence, remember to
entities. (To see the expected sequence, remember to not include your custom SQL in not include your custom SQL in the mapping files as that will override the
the mapping files as that will override the Hibernate generated static sql.) Hibernate generated static sql.)</para>
</para>
<para> <para>The stored procedures are in most cases (read: better do it than
The stored procedures are in most cases (read: better do it than not) required to not) required to return the number of rows inserted/updated/deleted, as
return the number of rows inserted/updated/deleted, as Hibernate has some runtime Hibernate has some runtime checks for the success of the statement.
checks for the success of the statement. Hibernate always registers the first statement Hibernate always registers the first statement parameter as a numeric
parameter as a numeric output parameter for the CUD operations: output parameter for the CUD operations:</para>
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2) <programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS RETURN NUMBER IS
BEGIN BEGIN
@ -506,76 +526,63 @@ BEGIN
return SQL%ROWCOUNT; return SQL%ROWCOUNT;
END updatePerson;]]></programlisting> END updatePerson;</programlisting>
</sect1> </sect1>
<sect1 id="querysql-load"> <sect1 id="querysql-load">
<title>Custom SQL for loading</title> <title>Custom SQL for loading</title>
<para> <para>You may also declare your own SQL (or HQL) queries for entity
You may also declare your own SQL (or HQL) queries for entity loading: loading:</para>
</para>
<programlisting><![CDATA[<sql-query name="person"> <programlisting>&lt;sql-query name="person"&gt;
<return alias="pers" class="Person" lock-mode="upgrade"/> &lt;return alias="pers" class="Person" lock-mode="upgrade"/&gt;
SELECT NAME AS {pers.name}, ID AS {pers.id} SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON FROM PERSON
WHERE ID=? WHERE ID=?
FOR UPDATE FOR UPDATE
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<para> <para>This is just a named query declaration, as discussed earlier. You
This is just a named query declaration, as discussed earlier. You may may reference this named query in a class mapping:</para>
reference this named query in a class mapping:
</para>
<programlisting><![CDATA[<class name="Person"> <programlisting>&lt;class name="Person"&gt;
<id name="id"> &lt;id name="id"&gt;
<generator class="increment"/> &lt;generator class="increment"/&gt;
</id> &lt;/id&gt;
<property name="name" not-null="true"/> &lt;property name="name" not-null="true"/&gt;
<loader query-ref="person"/> &lt;loader query-ref="person"/&gt;
</class>]]></programlisting> &lt;/class&gt;</programlisting>
<para> <para>This even works with stored procedures.</para>
This even works with stored procedures.
</para>
<para> <para>You may even define a query for collection loading:</para>
You may even define a query for collection loading:
</para>
<programlisting><![CDATA[<set name="employments" inverse="true"> <programlisting>&lt;set name="employments" inverse="true"&gt;
<key/> &lt;key/&gt;
<one-to-many class="Employment"/> &lt;one-to-many class="Employment"/&gt;
<loader query-ref="employments"/> &lt;loader query-ref="employments"/&gt;
</set>]]></programlisting> &lt;/set&gt;</programlisting>
<programlisting><![CDATA[<sql-query name="employments"> <programlisting>&lt;sql-query name="employments"&gt;
<load-collection alias="emp" role="Person.employments"/> &lt;load-collection alias="emp" role="Person.employments"/&gt;
SELECT {emp.*} SELECT {emp.*}
FROM EMPLOYMENT emp FROM EMPLOYMENT emp
WHERE EMPLOYER = :id WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
<para> <para>You could even define an entity loader that loads a collection by
You could even define an entity loader that loads a collection by join fetching:</para>
join fetching:
</para>
<programlisting><![CDATA[<sql-query name="person"> <programlisting>&lt;sql-query name="person"&gt;
<return alias="pers" class="Person"/> &lt;return alias="pers" class="Person"/&gt;
<return-join alias="emp" property="pers.employments"/> &lt;return-join alias="emp" property="pers.employments"/&gt;
SELECT NAME AS {pers.*}, {emp.*} SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID ON pers.ID = emp.PERSON_ID
WHERE ID=? WHERE ID=?
</sql-query>]]></programlisting> &lt;/sql-query&gt;</programlisting>
</sect1> </sect1>
</chapter> </chapter>