hibernate-orm/reference/en/modules/query_sql.xml

253 lines
9.1 KiB
XML
Raw Normal View History

<chapter id="querysql" revision="1">
<title>Native SQL</title>
<para>
You may also express queries in the native SQL dialect of your database. This is useful if you
want to utilize database specific features such as query hints or the <literal>CONNECT</literal>
keyword in Oracle. It also provides a clean migration path from a direct SQL/JDBC based
application to Hibernate.
</para>
<para>
Hibernate3 allows you to specify handwritten SQL for all create, update, delete, and load
operations.
</para>
<sect1 id="querysql-creating">
<title>Creating a native SQL <literal>Query</literal></title>
<para>
SQL queries are controlled via the <literal>SQLQuery</literal> interface, which
is obtained by calling <literal>Session.createSQLQuery()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class);
.setMaxResults(50);
.list();]]></programlisting>
<para>
This query specified:
</para>
<itemizedlist>
<listitem>
<para>
the SQL query string, with a placeholder for Hibernate to inject the column aliases
</para>
</listitem>
<listitem>
<para>
the entity returned by the query, and its SQL table alias
</para>
</listitem>
</itemizedlist>
<para>
The <literal>addEntity()</literal> method associates SQL table aliases with entity classes,
and determines the shape of the query result set.
</para>
<para>
The <literal>addJoin()</literal> method may be used to load associations to other entities
and collections. TODO: examples!
</para>
<para>
A native SQL query might return a simple scalar value or a combination of scalars and
entities.
</para>
<programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();]]></programlisting>
</sect1>
<sect1 id="querysql-aliasreferences">
<title>Alias and property references</title>
<para>
The <literal>{cat.*}</literal> notation used above is a shorthand for "all properties".
Alternatively, you may list the columns explicity, but even then you must let Hibernate
inject the SQL column aliases for each property. The placeholder for a column alias is
just the property name 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
declared in the mapping metadata. Notice that we may even use the property aliases in the
where clause if we like.
</para>
<programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " +
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
"from cat_log cat where {cat.mate} = :catId"
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.setLong("catId", catId)
.list();]]></programlisting>
<para>
<emphasis>Note:</emphasis> if you list each property explicitly, you must include all
properties of the class <emphasis>and its subclasses</emphasis>!
</para>
</sect1>
<sect1 id="querysql-namedqueries" revision="1">
<title>Named SQL queries</title>
<para>
Named SQL queries may be defined in the mapping document and called in exactly the same way
as a named HQL query. In this case, we do <emphasis>not</emphasis> need to call
<literal>addEntity()</literal>.
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<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 'Hiber%'
</sql-query>]]></programlisting>
<programlisting><![CDATA[List people = sess.getNamedQuery("mySqlQuery")
.setMaxResults(50)
.list();]]></programlisting>
<para>
A named SQL query may return a scalar value. You must specfy the column alias
and Hibernate type using the <literal>&lt;return-scalar&gt;</literal> element:
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
The <literal>&lt;return-join&gt;</literal> and <literal>&lt;load-collection&gt;</literal>
elements are used to join associations and define queries which initialize collections,
respectively. TODO!
</para>
</sect1>
<sect1 id="querysql-cud">
<title>Custom SQL for create, update and delete</title>
<para>
Hibernate3 can use custom SQL statements for create, update, and delete operations.
The class and collection persisters in Hibernate already contain a set of configuration
time generated strings (insertsql, deletesql, updatesql etc.). The mapping tags
<literal>&lt;sql-insert&gt;</literal>, <literal>&lt;sql-delete&gt;</literal>, and
<literal>&lt;sql-update&gt;</literal> override these strings:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>]]></programlisting>
<para>
The SQL is directly executed in your database, so you are free to use any dialect
you like.
</para>
<para>
Stored procedures are supported if the <literal>callable</literal> attribute is set:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>
The stored procedures are in most cases (read: better do it than not) required to
return the number of rows inserted/updated/deleted, as Hibernate has some runtime
checks for the success of the statement. Hibernate always registers the first statement
parameter as a numeric output parameter for the CUD operations:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
<title>Custom SQL for loading</title>
<para>
You may also declare your own SQL (or HQL) queries for entity loading:
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="p" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {p.name}, ID AS {p.id} FROM PERSON WHERE ID=? FOR UPDATE
</sql-query>]]></programlisting>
<para>
This is just a named query declaration, as discussed earlier. You may
reference this named query in a class mapping:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>
TODO: Document the following example for collection loader.
</para>
<programlisting><![CDATA[<sql-query name="organizationEmployments">
<load-collection alias="empcol" role="Organization.employments"/>
SELECT {empcol.*}
FROM EMPLOYMENT empcol
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>
<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment"/>
<synchronize table="EMPLOYMENT"/>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, ID AS {emp.id}
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>]]></programlisting>
</sect1>
</chapter>