documented use of unqualified property names in HQL

fixed some of the problems in the SQL chapter


git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@6215 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Gavin King 2005-03-25 05:18:45 +00:00
parent e1e8adccd4
commit abbf47ed61
2 changed files with 224 additions and 182 deletions

View File

@ -38,16 +38,20 @@
<programlisting><![CDATA[from eg.Cat]]></programlisting>
<para>
which simply returns all instances of the class <literal>eg.Cat</literal>.
which simply returns all instances of the class <literal>eg.Cat</literal>.
We don't usually need to qualify the class name, since <literal>auto-import</literal>
is the default. So we almost always just write:
</para>
<programlisting><![CDATA[from Cat]]></programlisting>
<para>
Most of the time, you will need to assign an <emphasis>alias</emphasis>, since
you will want to refer to the <literal>Cat</literal> in other parts of the
query.
</para>
<programlisting><![CDATA[from eg.Cat as cat]]></programlisting>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
This query assigns the alias <literal>cat</literal> to <literal>Cat</literal>
@ -55,7 +59,7 @@
keyword is optional; we could also write:
</para>
<programlisting><![CDATA[from eg.Cat cat]]></programlisting>
<programlisting><![CDATA[from Cat cat]]></programlisting>
<para>
Multiple classes may appear, resulting in a cartesian product or "cross" join.
@ -80,13 +84,13 @@
collection of values, using a <literal>join</literal>.
</para>
<programlisting><![CDATA[from eg.Cat as cat
<programlisting><![CDATA[from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten
left outer join cat.kittens as kitten]]></programlisting>
from eg.Cat as cat left join cat.mate.kittens as kittens
<programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
from Formula form full join form.parameter param]]></programlisting>
<programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
<para>
The supported join types are borrowed from ANSI SQL
@ -120,7 +124,7 @@ from Formula form full join form.parameter param]]></programlisting>
<literal>right outer join</literal> constructs may be abbreviated.
</para>
<programlisting><![CDATA[from eg.Cat as cat
<programlisting><![CDATA[from Cat as cat
join cat.mate as mate
left join cat.kittens as kitten]]></programlisting>
@ -132,7 +136,7 @@ from Formula form full join form.parameter param]]></programlisting>
<xref linkend="performance-fetching"/> for more information.
</para>
<programlisting><![CDATA[from eg.Cat as cat
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens]]></programlisting>
@ -162,7 +166,7 @@ from Formula form full join form.parameter param]]></programlisting>
</para>
<programlisting><![CDATA[select mate
from eg.Cat as cat
from Cat as cat
inner join cat.mate as mate]]></programlisting>
<para>
@ -170,7 +174,7 @@ from eg.Cat as cat
Actually, you may express this query more compactly as:
</para>
<programlisting><![CDATA[select cat.mate from eg.Cat cat]]></programlisting>
<programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
<!-- NO LONGER SUPPORTED!
@ -179,13 +183,13 @@ from eg.Cat as cat
function. The following query returns all kittens of any cat.
</para>
<programlisting><![CDATA[select elements(cat.kittens) from eg.Cat cat]]></programlisting>
<programlisting><![CDATA[select elements(cat.kittens) from Cat cat]]></programlisting>
-->
<para>
Queries may return properties of any value type including properties of component type:
</para>
<programlisting><![CDATA[select cat.name from eg.DomesticCat cat
<programlisting><![CDATA[select cat.name from DomesticCat cat
where cat.name like 'fri%'
select cust.name.firstName from Customer as cust]]></programlisting>
@ -196,7 +200,7 @@ select cust.name.firstName from Customer as cust]]></programlisting>
</para>
<programlisting><![CDATA[select mother, offspr, mate.name
from eg.DomesticCat as mother
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
@ -205,7 +209,7 @@ from eg.DomesticCat as mother
</para>
<programlisting><![CDATA[select new Family(mother, mate, offspr)
from eg.DomesticCat as mother
from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr]]></programlisting>
@ -223,7 +227,7 @@ from eg.DomesticCat as mother
</para>
<programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
from eg.Cat cat]]></programlisting>
from Cat cat]]></programlisting>
<!-- NO LONGER SUPPORTED
<para>
@ -232,7 +236,7 @@ from eg.Cat cat]]></programlisting>
</para>
<programlisting><![CDATA[select cat, count( elements(cat.kittens) )
from eg.Cat cat group by cat]]></programlisting>
from Cat cat group by cat]]></programlisting>
-->
<para>
@ -262,9 +266,9 @@ from eg.Cat cat group by cat]]></programlisting>
the same semantics as in SQL.
</para>
<programlisting><![CDATA[select distinct cat.name from eg.Cat cat
<programlisting><![CDATA[select distinct cat.name from Cat cat
select count(distinct cat.name), count(cat) from eg.Cat cat]]></programlisting>
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
</sect1>
@ -275,7 +279,7 @@ select count(distinct cat.name), count(cat) from eg.Cat cat]]></programlisting>
A query like:
</para>
<programlisting><![CDATA[from eg.Cat as cat]]></programlisting>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
returns instances not only of <literal>Cat</literal>, but also of subclasses like
@ -292,7 +296,7 @@ select count(distinct cat.name), count(cat) from eg.Cat cat]]></programlisting>
classes:
</para>
<programlisting><![CDATA[from eg.Named n, eg.Named m where n.name = m.name]]></programlisting>
<programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
<para>
Note that these last two queries will require more than one SQL <literal>SELECT</literal>. This
@ -307,16 +311,23 @@ select count(distinct cat.name), count(cat) from eg.Cat cat]]></programlisting>
<para>
The <literal>where</literal> clause allows you to narrow the list of instances returned.
If no alias exists, you may refer to properties by name:
</para>
<programlisting><![CDATA[from eg.Cat as cat where cat.name='Fritz']]></programlisting>
<programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
<para>
If there is an alias, use a qualified property name:
</para>
<programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
<para>
returns instances of <literal>Cat</literal> named 'Fritz'.
</para>
<programlisting><![CDATA[select foo
from eg.Foo foo, eg.Bar bar
from Foo foo, Bar bar
where foo.startDate = bar.date]]></programlisting>
<para>
@ -328,14 +339,14 @@ where foo.startDate = bar.date]]></programlisting>
<literal>where</literal> clause extremely powerful. Consider:
</para>
<programlisting><![CDATA[from eg.Cat cat where cat.mate.name is not null]]></programlisting>
<programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
<para>
This query translates to an SQL query with a table (inner) join. If you were to write
something like
</para>
<programlisting><![CDATA[from eg.Foo foo
<programlisting><![CDATA[from Foo foo
where foo.bar.baz.customer.address.city is not null]]></programlisting>
<para>
@ -347,10 +358,10 @@ where foo.bar.baz.customer.address.city is not null]]></programlisting>
instances:
</para>
<programlisting><![CDATA[from eg.Cat cat, eg.Cat rival where cat.mate = rival.mate
<programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
select cat, mate
from eg.Cat cat, eg.Cat mate
<programlisting><![CDATA[select cat, mate
from Cat cat, Cat mate
where cat.mate = mate]]></programlisting>
<para>
@ -358,9 +369,9 @@ where cat.mate = mate]]></programlisting>
unique identifier of an object. (You may also use its property name.)
</para>
<programlisting><![CDATA[from eg.Cat as cat where cat.id = 123
<programlisting><![CDATA[from Cat as cat where cat.id = 123
from eg.Cat as cat where cat.mate.id = 69]]></programlisting>
from Cat as cat where cat.mate.id = 69]]></programlisting>
<para>
The second query is efficient. No table join is required!
@ -374,9 +385,9 @@ from eg.Cat as cat where cat.mate.id = 69]]></programlisting>
<programlisting><![CDATA[from bank.Person person
where person.id.country = 'AU'
and person.id.medicareNumber = 123456
and person.id.medicareNumber = 123456]]></programlisting>
from bank.Account account
<programlisting><![CDATA[from bank.Account account
where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456]]></programlisting>
@ -390,7 +401,7 @@ where account.owner.id.country = 'AU'
where clause will be translated to its discriminator value.
</para>
<programlisting><![CDATA[from eg.Cat cat where cat.class = eg.DomesticCat]]></programlisting>
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
<para>
You may also specify properties of components or composite user types (and of components
@ -408,8 +419,8 @@ store.owner.address // error!]]></programlisting>
is a property mapped with <literal>&lt;any&gt;</literal>).
</para>
<programlisting><![CDATA[from eg.AuditLog log, eg.Payment payment
where log.item.class = 'eg.Payment' and log.item.id = payment.id]]></programlisting>
<programlisting><![CDATA[from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
<para>
Notice that <literal>log.item.class</literal> and <literal>payment.class</literal>
@ -513,17 +524,17 @@ where log.item.class = 'eg.Payment' and log.item.id = payment.id]]></programlist
<literal>in</literal> and <literal>between</literal> may be used as follows:
</para>
<programlisting><![CDATA[from eg.DomesticCat cat where cat.name between 'A' and 'B'
<programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
from eg.DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
and the negated forms may be written
</para>
<programlisting><![CDATA[from eg.DomesticCat cat where cat.name not between 'A' and 'B'
<programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
from eg.DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
Likewise, <literal>is null</literal> and <literal>is not null</literal> may be used to test
@ -542,16 +553,16 @@ from eg.DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></progra
literals <literal>1</literal> and <literal>0</literal> in the translated SQL from this HQL:
</para>
<programlisting><![CDATA[from eg.Cat cat where cat.alive = true]]></programlisting>
<programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
<para>
You may test the size of a collection with the special property <literal>size</literal>, or
the special <literal>size()</literal> function.
</para>
<programlisting><![CDATA[from eg.Cat cat where cat.kittens.size > 0
<programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
from eg.Cat cat where size(cat.kittens) > 0]]></programlisting>
<programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
<para>
For indexed collections, you may refer to the minimum and maximum indices using
@ -561,11 +572,11 @@ from eg.Cat cat where size(cat.kittens) > 0]]></programlisting>
functions.
</para>
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current date
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current date]]></programlisting>
from Order order where maxindex(order.items) > 100
<programlisting><![CDATA[from Order order where maxindex(order.items) > 100]]></programlisting>
from Order order where minelement(order.items) > 10000]]></programlisting>
<programlisting><![CDATA[from Order order where minelement(order.items) > 10000]]></programlisting>
<para>
The SQL functions <literal>any, some, all, exists, in</literal> are supported when passed the element
@ -573,17 +584,17 @@ from Order order where minelement(order.items) > 10000]]></programlisting>
or the result of a subquery (see below).
</para>
<programlisting><![CDATA[select mother from eg.Cat as mother, eg.Cat as kit
where kit in elements(foo.kittens)
<programlisting><![CDATA[select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)]]></programlisting>
select p from eg.NameList list, eg.Person p
where p.name = some elements(list.names)
<programlisting><![CDATA[select p from NameList list, Person p
where p.name = some elements(list.names)]]></programlisting>
from eg.Cat cat where exists elements(cat.kittens)
<programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
from eg.Player p where 3 > all elements(p.scores)
<programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
from eg.Show show where 'fizard' in indices(show.acts)]]></programlisting>
<programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
<para>
Note that these constructs - <literal>size</literal>, <literal>elements</literal>,
@ -597,16 +608,16 @@ from eg.Show show where 'fizard' in indices(show.acts)]]></programlisting>
index (in a where clause only):
</para>
<programlisting><![CDATA[from Order order where order.items[0].id = 1234
<programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
select person from Person person, Calendar calendar
<programlisting><![CDATA[select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar
and person.nationality.calendar = calendar]]></programlisting>
select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
select item from Item item, Order order
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
<para>
@ -629,7 +640,7 @@ where index(item) < 5]]></programlisting>
Scalar SQL functions supported by the underlying database may be used
</para>
<programlisting><![CDATA[from eg.DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
<para>
If you are not yet convinced by all this, think how much longer and less readable the
@ -675,7 +686,7 @@ WHERE prod.name = 'widget'
The list returned by a query may be ordered by any property of a returned class or components:
</para>
<programlisting><![CDATA[from eg.DomesticCat cat
<programlisting><![CDATA[from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
<para>
@ -692,11 +703,11 @@ order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
</para>
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from eg.Cat cat
group by cat.color
from Cat cat
group by cat.color]]></programlisting>
select foo.id, avg(name), max(name)
from eg.Foo foo join foo.names name
<programlisting><![CDATA[select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id]]></programlisting>
<para>
@ -704,18 +715,18 @@ group by foo.id]]></programlisting>
</para>
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from eg.Cat cat
from Cat cat
group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
<para>
SQL functions and aggregate functions are allowed in the <literal>having</literal>
and <literal>order by</literal> clauses, if supported by the underlying database (eg.
not in MySQL).
and <literal>order by</literal> clauses, if supported by the underlying database
(eg. not in MySQL).
</para>
<programlisting><![CDATA[select cat
from eg.Cat cat
from Cat cat
join cat.kittens kitten
group by cat
having avg(kitten.weight) > 100
@ -737,24 +748,24 @@ order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
(subqueries that refer to an alias in the outer query) are allowed.
</para>
<programlisting><![CDATA[from eg.Cat as fatcat
<programlisting><![CDATA[from Cat as fatcat
where fatcat.weight > (
select avg(cat.weight) from eg.DomesticCat cat
)
select avg(cat.weight) from DomesticCat cat
)]]></programlisting>
from eg.DomesticCat as cat
<programlisting><![CDATA[from DomesticCat as cat
where cat.name = some (
select name.nickName from eg.Name as name
)
select name.nickName from Name as name
)]]></programlisting>
from eg.Cat as cat
<programlisting><![CDATA[from Cat as cat
where not exists (
from eg.Cat as mate where mate.mate = cat
)
from Cat as mate where mate.mate = cat
)]]></programlisting>
from eg.DomesticCat as cat
<programlisting><![CDATA[from DomesticCat as cat
where cat.name not in (
select name.nickName from eg.Name as name
select name.nickName from Name as name
)]]></programlisting>
</sect1>

View File

@ -86,7 +86,7 @@
"from cat_log cat where {cat.mate} = :catId"
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("cat", Cat.class)
.setLong("catId", catId)
.list();]]></programlisting>
@ -138,14 +138,14 @@ List loggedCats = sess.createSQLQuery(sql)
</para>
<sect2 id="propertyresults">
<title>Using property-result to explicitly specify column/alias names</title>
<para>
With <literal>&lt;property-result&gt;</literal> you can explicitly tell Hibernate what columns
to use as opposed to use <literal>{}</literal>-syntax to let Hibernate inject its own aliases.
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<title>Using property-result to explicitly specify column/alias names</title>
<para>
With <literal>&lt;property-result&gt;</literal> you can explicitly tell Hibernate what columns
to use as opposed to use <literal>{}</literal>-syntax to let Hibernate inject its own aliases.
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<property-result name="name" column="myName"/>
<property-result name="age" column="myAge"/>
@ -157,18 +157,18 @@ List loggedCats = sess.createSQLQuery(sql)
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
]]></programlisting>
<literal>&lt;property-result&gt;</literal> also works with multiple columns. This solves a limitation with
the <literal>{}</literal>-syntax which can not allow fine grained control of multi-column properties.
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<literal>&lt;property-result&gt;</literal> also works with multiple columns. This solves a limitation with
the <literal>{}</literal>-syntax which can not allow fine grained control of multi-column properties.
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<property-result name="salary">
<result-column name="VALUE"/>
<result-column name="CURRENCY"/>
</property-result>
<property-result name="endDate" column="myEndDate"/>
</return>
<result-column name="CURRENCY"/>
</property-result>
<property-result 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
@ -176,29 +176,29 @@ List loggedCats = sess.createSQLQuery(sql)
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>]]></programlisting>
<para>
Notice that in this example we used <literal>&lt;property-result&gt;</literal> in combination
with the <literal>{}</literal>-syntax for injection. Allowing users to choose
how they want to refer column and properties.
</para>
<para>
Notice that in this example we used <literal>&lt;property-result&gt;</literal> in combination
with the <literal>{}</literal>-syntax for injection. Allowing users to choose
how they want to refer column and properties.
</para>
<para>
If your mapping has a discriminator you must use &lt;discriminator-result&gt; to specify the discriminators column.
</para>
<para>
If your mapping has a discriminator you must use &lt;discriminator-result&gt; to specify the discriminators column.
</para>
</sect2>
<sect2 id="sp_query">
<title>Using stored procedures for querying</title>
<para>
Hibernate 3 introduces support for queries via stored procedures.
The stored procedures must return a resultset as the first out-parameter to be able to work with Hibernate.
An example of such a stored procedure in Oracle 9 and higher is as follows:
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
<title>Using stored procedures for querying</title>
<para>
Hibernate 3 introduces support for queries via stored procedures.
The stored procedures must return a resultset as the first out-parameter to be able to work with Hibernate.
An example of such a stored procedure in Oracle 9 and higher is as follows:
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
@ -208,57 +208,88 @@ BEGIN
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
RETURN st_cursor;
END;]]></programlisting>
To use this query in Hibernate you need to map it via a named query.
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<property-result name="employee" column="EMPLOYEE"/>
<property-result name="employer" column="EMPLOYER"/>
<property-result name="startDate" column="STARTDATE"/>
<property-result name="endDate" column="ENDDATE"/>
<property-result name="regionCode" column="REGIONCODE"/>
<property-result name="id" column="EID"/>
<property-result name="salary">
<result-column name="VALUE"/>
<result-column name="CURRENCY"/>
</property-result>
</return>
{ ? = call selectAllEmployments() }
</sql-query>]]></programlisting>
</para>
<para>
Notice stored procedures currently only return scalars and entities. <literal>&gt;return-join&lt;</literal> and
<literal>&gt;load-collection&lt;</literal> is not supported.
</para>
<para>
TODO: make the "rules" visually nicer and understandable ,)
</para>
<sect3>
<title>Rules/Limitations for using stored procedures</title>
<para>
To use stored procedures with Hibernate the procedures have to follow some 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 <literal>session.connection()</literal>.
The rules are different per database since database vendors have different stored procedure semantics/syntax.
For Oracle the following rules applies:
1. It must return a result set. (This is done by returning a SYS_REFCURSOR in Oracle 9 & 10, in Oracle you need to define a REF CURSOR type)
2. It should be on the form <literal>{ ? = call procName(<parameters>) }</literal> or <literal>{ ? = call procName}</literal> (this is more a Oracle rule than a Hibernate rule)
For Sybase & MS SQL server the following rules applies:
It must return a result set. Note that since these servers can/will return multiple result sets and update count 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 the most efficient, but it is not an requirement.
</para>
</sect3>
To use this query in Hibernate you need to map it via a named query.
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<property-result name="employee" column="EMPLOYEE"/>
<property-result name="employer" column="EMPLOYER"/>
<property-result name="startDate" column="STARTDATE"/>
<property-result name="endDate" column="ENDDATE"/>
<property-result name="regionCode" column="REGIONCODE"/>
<property-result name="id" column="EID"/>
<property-result name="salary">
<result-column name="VALUE"/>
<result-column name="CURRENCY"/>
</property-result>
</return>
{ ? = call selectAllEmployments() }
</sql-query>]]></programlisting>
</para>
<para>
Notice stored procedures currently only return scalars and entities.
<literal>&lt;return-join&gt;</literal> and <literal>&lt;load-collection&gt;</literal>
are not supported.
</para>
<para>
TODO: make the "rules" visually nicer and understandable ,)
</para>
<sect3>
<title>Rules/Limitations for using stored procedures</title>
<para>
To use stored procedures with Hibernate the procedures have to follow some 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 <literal>session.connection()</literal>.
The rules are different per database since database vendors have different stored procedure
semantics/syntax.
</para>
<para>
For Oracle the following rules apply:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
It must return a result set. This is done by returning a SYS_REFCURSOR in Oracle 9 or 10.
In Oracle you need to define a REF CURSOR type.
</para>
</listitem>
<listitem>
<para>
It should be on the form <literal>{ ? = call procName(&lt;parameters&gt;) }</literal> or
<literal>{ ? = call procName }</literal> (This is more an Oracle rule than a Hibernate rule.)
</para>
</listitem>
</itemizedlist>
<para>
For Sybase or MS SQL server the following rules apply:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
It must return a result set. Note that since these servers can/will return multiple result
sets and update count Hibernate will iterate the results and take the first result that is
a result set as its return value. Everything else will be discarded.
</para>
</listitem>
<listitem>
<para>
If you can enable SET NOCOUNT ON in your procedure it will probably be the most efficient, but
it is not a requirement.
</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</sect1>
@ -304,16 +335,16 @@ BEGIN
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>
The order of the positional parameters are currently vital, as they must be in
the same sequence as Hibernate expects them.
</para>
<para>
You can see the expected order by enabling debug logging for the <literal>org.hiberante.persister.entity</literal>
level. With this level enabled Hibernate will print out the static SQL that is used to create, update, delete etc. entities.
To see the expected sequence, remember to not include your custom SQL in the mapping files as that will override the Hibernate generated static sql.
</para>
<para>
The order of the positional parameters are currently vital, as they must be in
the same sequence as Hibernate expects them.
</para>
<para>
You can see the expected order by enabling debug logging for the <literal>org.hiberante.persister.entity</literal>
level. With this level enabled Hibernate will print out the static SQL that is used to create, update, delete etc. entities.
To see the expected sequence, remember to not include your custom SQL in the mapping files as that will override the Hibernate generated static sql.
</para>
<para>
The stored procedures are in most cases (read: better do it than not) required to
@ -336,7 +367,7 @@ BEGIN
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
@ -363,10 +394,10 @@ END updatePerson;]]></programlisting>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>
And this also works with stored procedures.
</para>
<para>
And this also works with stored procedures.
</para>
<para>
TODO: Document the following example for collection loader.