HHH-3556: Envers documentation partially migrated

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15500 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Adam Warski 2008-11-04 17:31:43 +00:00
parent c2e7ba6e86
commit 7e04164b24
38 changed files with 0 additions and 21841 deletions

View File

@ -1,382 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="architecture">
<title>Architecture</title>
<sect1 id="architecture-overview" revision="1">
<title>Overview</title>
<para>
A (very) high-level view of the Hibernate architecture:
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="../images/overview.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../images/overview.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
<para>
This diagram shows Hibernate using the database and configuration data to
provide persistence services (and persistent objects) to the application.
</para>
<para>
We would like to show a more detailed view of the runtime architecture.
Unfortunately, Hibernate is flexible and supports several approaches. We will
show the two extremes. The "lite" architecture has the application
provide its own JDBC connections and manage its own transactions. This approach
uses a minimal subset of Hibernate's APIs:
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="../images/lite.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../images/lite.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
<para>
The "full cream" architecture abstracts the application away from the
underlying JDBC/JTA APIs and lets Hibernate take care of the details.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="../images/full_cream.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../images/full_cream.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
<para>
Heres some definitions of the objects in the diagrams:
<variablelist spacing="compact">
<varlistentry>
<term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
<listitem>
<para>
A threadsafe (immutable) cache of compiled mappings for a single database.
A factory for <literal>Session</literal> and a client of
<literal>ConnectionProvider</literal>. Might hold an optional (second-level)
cache of data that is reusable between transactions, at a
process- or cluster-level.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Session (<literal>org.hibernate.Session</literal>)</term>
<listitem>
<para>
A single-threaded, short-lived object representing a conversation between
the application and the persistent store. Wraps a JDBC connection. Factory
for <literal>Transaction</literal>. Holds a mandatory (first-level) cache
of persistent objects, used when navigating the object graph or looking up
objects by identifier.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Persistent objects and collections</term>
<listitem>
<para>
Short-lived, single threaded objects containing persistent state and business
function. These might be ordinary JavaBeans/POJOs, the only special thing about
them is that they are currently associated with (exactly one)
<literal>Session</literal>. As soon as the <literal>Session</literal> is closed,
they will be detached and free to use in any application layer (e.g. directly
as data transfer objects to and from presentation).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transient and detached objects and collections</term>
<listitem>
<para>
Instances of persistent classes that are not currently associated with a
<literal>Session</literal>. They may have been instantiated by
the application and not (yet) persisted or they may have been instantiated by a
closed <literal>Session</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
<listitem>
<para>
(Optional) A single-threaded, short-lived object used by the application to
specify atomic units of work. Abstracts application from underlying JDBC,
JTA or CORBA transaction. A <literal>Session</literal> might span several
<literal>Transaction</literal>s in some cases. However, transaction demarcation,
either using the underlying API or <literal>Transaction</literal>, is never
optional!
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
<listitem>
<para>
(Optional) A factory for (and pool of) JDBC connections. Abstracts application from
underlying <literal>Datasource</literal> or <literal>DriverManager</literal>.
Not exposed to application, but can be extended/implemented by the developer.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
<listitem>
<para>
(Optional) A factory for <literal>Transaction</literal> instances. Not exposed to the
application, but can be extended/implemented by the developer.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>Extension Interfaces</emphasis></term>
<listitem>
<para>
Hibernate offers many optional extension interfaces you can implement to customize
the behavior of your persistence layer. See the API documentation for details.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Given a "lite" architecture, the application bypasses the
<literal>Transaction</literal>/<literal>TransactionFactory</literal> and/or
<literal>ConnectionProvider</literal> APIs to talk to JTA or JDBC directly.
</para>
</sect1>
<sect1 id="architecture-states" revision="1">
<title>Instance states</title>
<para>
An instance of a persistent classes may be in one of three different states,
which are defined with respect to a <emphasis>persistence context</emphasis>.
The Hibernate <literal>Session</literal> object is the persistence context:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>transient</term>
<listitem>
<para>
The instance is not, and has never been associated with
any persistence context. It has no persistent identity
(primary key value).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>persistent</term>
<listitem>
<para>
The instance is currently associated with a persistence
context. It has a persistent identity (primary key value)
and, perhaps, a corresponding row in the database. For a
particular persistence context, Hibernate
<emphasis>guarantees</emphasis> that persistent identity
is equivalent to Java identity (in-memory location of the
object).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>detached</term>
<listitem>
<para>
The instance was once associated with a persistence
context, but that context was closed, or the instance
was serialized to another process. It has a persistent
identity and, perhaps, a corresponding row in the database.
For detached instances, Hibernate makes no guarantees
about the relationship between persistent identity and
Java identity.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 id="architecture-jmx" revision="1">
<title>JMX Integration</title>
<para>
JMX is the J2EE standard for management of Java components. Hibernate may be managed via
a JMX standard service. We provide an MBean implementation in the distribution,
<literal>org.hibernate.jmx.HibernateService</literal>.
</para>
<para>
For an example how to deploy Hibernate as a JMX service on the JBoss Application Server,
please see the JBoss User Guide. On JBoss AS, you also get these benefits if you deploy
using JMX:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Session Management:</emphasis> The Hibernate <literal>Session</literal>'s life cycle
can be automatically bound to the scope of a JTA transaction. This means you no
longer have to manually open and close the <literal>Session</literal>, this
becomes the job of a JBoss EJB interceptor. You also don't have to worry about
transaction demarcation in your code anymore (unless you'd like to write a portable
persistence layer of course, use the optional Hibernate <literal>Transaction</literal>
API for this). You call the <literal>HibernateContext</literal> to access a
<literal>Session</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>HAR deployment:</emphasis> Usually you deploy the Hibernate JMX service using a JBoss
service deployment descriptor (in an EAR and/or SAR file), it supports all the usual
configuration options of a Hibernate <literal>SessionFactory</literal>. However, you still
have to name all your mapping files in the deployment descriptor. If you decide to use
the optional HAR deployment, JBoss will automatically detect all mapping files in your
HAR file.
</para>
</listitem>
</itemizedlist>
<para>
Consult the JBoss AS user guide for more information about these options.
</para>
<para>
Another feature available as a JMX service are runtime Hibernate statistics. See
<xref linkend="configuration-optional-statistics"/>.
</para>
</sect1>
<sect1 id="architecture-jca" revision="1">
<title>JCA Support</title>
<para>
Hibernate may also be configured as a JCA connector. Please see the website for more
details. Please note that Hibernate JCA support is still considered experimental.
</para>
</sect1>
<sect1 id="architecture-current-session" revision="2">
<title>Contextual Sessions</title>
<para>
Most applications using Hibernate need some form of "contextual" sessions, where a given
session is in effect throughout the scope of a given context. However, across applications
the definition of what constitutes a context is typically different; and different contexts
define different scopes to the notion of current. Applications using Hibernate prior
to version 3.0 tended to utilize either home-grown <literal>ThreadLocal</literal>-based
contextual sessions, helper classes such as <literal>HibernateUtil</literal>, or utilized
third-party frameworks (such as Spring or Pico) which provided proxy/interception-based contextual sessions.
</para>
<para>
Starting with version 3.0.1, Hibernate added the <literal>SessionFactory.getCurrentSession()</literal>
method. Initially, this assumed usage of <literal>JTA</literal> transactions, where the
<literal>JTA</literal> transaction defined both the scope and context of a current session.
The Hibernate team maintains that, given the maturity of the numerous stand-alone
<literal>JTA TransactionManager</literal> implementations out there, most (if not all)
applications should be using <literal>JTA</literal> transaction management whether or not
they are deployed into a <literal>J2EE</literal> container. Based on that, the
<literal>JTA</literal>-based contextual sessions is all you should ever need to use.
</para>
<para>
However, as of version 3.1, the processing behind
<literal>SessionFactory.getCurrentSession()</literal> is now pluggable. To that
end, a new extension interface (<literal>org.hibernate.context.CurrentSessionContext</literal>)
and a new configuration parameter (<literal>hibernate.current_session_context_class</literal>)
have been added to allow pluggability of the scope and context of defining current sessions.
</para>
<para>
See the Javadocs for the <literal>org.hibernate.context.CurrentSessionContext</literal>
interface for a detailed discussion of its contract. It defines a single method,
<literal>currentSession()</literal>, by which the implementation is responsible for
tracking the current contextual session. Out-of-the-box, Hibernate comes with three
implementations of this interface.
</para>
<itemizedlist>
<listitem>
<para>
<literal>org.hibernate.context.JTASessionContext</literal> - current sessions
are tracked and scoped by a <literal>JTA</literal> transaction. The processing
here is exactly the same as in the older JTA-only approach. See the Javadocs
for details.
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.context.ThreadLocalSessionContext</literal> - current
sessions are tracked by thread of execution. Again, see the Javadocs for details.
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.context.ManagedSessionContext</literal> - current
sessions are tracked by thread of execution. However, you are responsible to
bind and unbind a <literal>Session</literal> instance with static methods
on this class, it does never open, flush, or close a <literal>Session</literal>.
</para>
</listitem>
</itemizedlist>
<para>
The first two implementations provide a "one session - one database transaction" programming
model, also known and used as <emphasis>session-per-request</emphasis>. The beginning
and end of a Hibernate session is defined by the duration of a database transaction.
If you use programmatic transaction demarcation in plain JSE without JTA, you are advised to
use the Hibernate <literal>Transaction</literal> API to hide the underlying transaction system
from your code. If you use JTA, use the JTA interfaces to demarcate transactions. If you
execute in an EJB container that supports CMT, transaction boundaries are defined declaratively
and you don't need any transaction or session demarcation operations in your code.
Refer to <xref linkend="transactions"/> for more information and code examples.
</para>
<para>
The <literal>hibernate.current_session_context_class</literal> configuration parameter
defines which <literal>org.hibernate.context.CurrentSessionContext</literal> implementation
should be used. Note that for backwards compatibility, if this config param is not set
but a <literal>org.hibernate.transaction.TransactionManagerLookup</literal> is configured,
Hibernate will use the <literal>org.hibernate.context.JTASessionContext</literal>.
Typically, the value of this parameter would just name the implementation class to
use; for the three out-of-the-box implementations, however, there are three corresponding
short names, "jta", "thread", and "managed".
</para>
</sect1>
</chapter>

View File

@ -1,650 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="associations">
<title>Association Mappings</title>
<sect1 id="assoc-intro" revision="1">
<title>Introduction</title>
<para>
Association mappings are the often most difficult thing to get right. In
this section we'll go through the canonical cases one by one, starting
with unidirectional mappings, and then considering the bidirectional cases.
We'll use <literal>Person</literal> and <literal>Address</literal> in all
the examples.
</para>
<para>
We'll classify associations by whether or not they map to an intervening
join table, and by multiplicity.
</para>
<para>
Nullable foreign keys are not considered good practice in traditional data
modelling, so all our examples use not null foreign keys. This is not a
requirement of Hibernate, and the mappings will all work if you drop the
nullability constraints.
</para>
</sect1>
<sect1 id="assoc-unidirectional" revision="1">
<title>Unidirectional associations</title>
<sect2 id="assoc-unidirectional-m21">
<title>many to one</title>
<para>
A <emphasis>unidirectional many-to-one association</emphasis> is the most
common kind of unidirectional association.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-121">
<title>one to one</title>
<para>
A <emphasis>unidirectional one-to-one association on a foreign key</emphasis>
is almost identical. The only difference is the column unique constraint.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
A <emphasis>unidirectional one-to-one association on a primary key</emphasis>
usually uses a special id generator. (Notice that we've reversed the direction
of the association in this example.)
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-12m">
<title>one to many</title>
<para>
A <emphasis>unidirectional one-to-many association on a foreign key</emphasis>
is a very unusual case, and is not really recommended.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId bigint not null )
]]></programlisting>
<para>
We think it's better to use a join table for this kind of association.
</para>
</sect2>
</sect1>
<sect1 id="assoc-unidirectional-join" revision="1">
<title>Unidirectional associations with join tables</title>
<sect2 id="assoc-unidirectional-join-12m">
<title>one to many</title>
<para>
A <emphasis>unidirectional one-to-many association on a join table</emphasis>
is much preferred. Notice that by specifying <literal>unique="true"</literal>,
we have changed the multiplicity from many-to-many to one-to-many.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-m21">
<title>many to one</title>
<para>
A <emphasis>unidirectional many-to-one association on a join table</emphasis>
is quite common when the association is optional.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-121">
<title>one to one</title>
<para>
A <emphasis>unidirectional one-to-one association on a join table</emphasis>
is extremely unusual, but possible.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-m2m">
<title>many to many</title>
<para>
Finally, we have a <emphasis>unidirectional many-to-many association</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-bidirectional" revision="1">
<title>Bidirectional associations</title>
<sect2 id="assoc-bidirectional-m21" revision="2">
<title>one to many / many to one</title>
<para>
A <emphasis>bidirectional many-to-one association</emphasis> is the
most common kind of association. (This is the standard parent/child
relationship.)
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
If you use a <literal>List</literal> (or other indexed collection) you need
to set the <literal>key</literal> column of the foreign key to <literal>not null</literal>,
and let Hibernate manage the association from the collections side to maintain the index
of each element (making the other side virtually inverse by setting
<literal>update="false"</literal> and <literal>insert="false"</literal>):
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id"/>
...
<many-to-one name="address"
column="addressId"
not-null="true"
insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class>]]></programlisting>
<para>
It is important that you define <literal>not-null="true"</literal> on the
<literal>&lt;key&gt;</literal> element of the collection mapping if the
underlying foreign key column is <literal>NOT NULL</literal>. Don't only
declare <literal>not-null="true"</literal> on a possible nested
<literal>&lt;column&gt;</literal> element, but on the <literal>&lt;key&gt;</literal>
element.
</para>
</sect2>
<sect2 id="assoc-bidirectional-121">
<title>one to one</title>
<para>
A <emphasis>bidirectional one-to-one association on a foreign key</emphasis>
is quite common.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
A <emphasis>bidirectional one-to-one association on a primary key</emphasis>
uses the special id generator.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-bidirectional-join" revision="1">
<title>Bidirectional associations with join tables</title>
<sect2 id="assoc-bidirectional-join-12m">
<title>one to many / many to one</title>
<para>
A <emphasis>bidirectional one-to-many association on a join table</emphasis>.
Note that the <literal>inverse="true"</literal> can go on either end of the
association, on the collection, or on the join.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-join-121">
<title>one to one</title>
<para>
A <emphasis>bidirectional one-to-one association on a join table</emphasis>
is extremely unusual, but possible.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="person"
column="personId"
not-null="true"
unique="true"/>
</join>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-join-m2m" revision="1">
<title>many to many</title>
<para>
Finally, we have a <emphasis>bidirectional many-to-many association</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-complex">
<title>More complex association mappings</title>
<para>
More complex association joins are <emphasis>extremely</emphasis> rare.
Hibernate makes it possible to handle more complex situations using
SQL fragments embedded in the mapping document. For example, if a table
with historical account information data defines
<literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
and <literal>effectiveStartDate</literal>columns, mapped as follows:
</para>
<programlisting><![CDATA[<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
<para>
Then we can map an association to the <emphasis>current</emphasis> instance
(the one with null <literal>effectiveEndDate</literal>) using:
</para>
<programlisting><![CDATA[<many-to-one name="currentAccountInfo"
property-ref="currentAccountKey"
class="AccountInfo">
<column name="accountNumber"/>
<formula>'1'</formula>
</many-to-one>]]></programlisting>
<para>
In a more complex example, imagine that the association between
<literal>Employee</literal> and <literal>Organization</literal> is maintained
in an <literal>Employment</literal> table full of historical employment data.
Then an association to the employee's <emphasis>most recent</emphasis> employer
(the one with the most recent <literal>startDate</literal>) might be mapped this way:
</para>
<programlisting><![CDATA[<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer"
class="Organization"
column="orgId"/>
</join>]]></programlisting>
<para>
You can get quite creative with this functionality, but it is usually more practical
to handle these kinds of cases using HQL or a criteria query.
</para>
</sect1>
</chapter>

View File

@ -1,374 +0,0 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="batch">
<title>Batch processing</title>
<para>
A naive approach to inserting 100 000 rows in the database using Hibernate might
look like this:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
}
tx.commit();
session.close();]]></programlisting>
<para>
This would fall over with an <literal>OutOfMemoryException</literal> somewhere
around the 50 000th row. That's because Hibernate caches all the newly inserted
<literal>Customer</literal> instances in the session-level cache.
</para>
<para>
In this chapter we'll show you how to avoid this problem. First, however, if you
are doing batch processing, it is absolutely critical that you enable the use of
JDBC batching, if you intend to achieve reasonable performance. Set the JDBC batch
size to a reasonable number (say, 10-50):
</para>
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
<para id="disablebatching" revision="1">
Note that Hibernate disables insert batching at the JDBC level transparently if you
use an <literal>identiy</literal> identifier generator.
</para>
<para>
You also might like to do this kind of work in a process where interaction with
the second-level cache is completely disabled:
</para>
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
<para>
However, this is not absolutely necessary, since we can explicitly set the
<literal>CacheMode</literal> to disable interaction with the second-level cache.
</para>
<sect1 id="batch-inserts">
<title>Batch inserts</title>
<para>
When making new objects persistent, you must <literal>flush()</literal> and
then <literal>clear()</literal> the session regularly, to control the size of
the first-level cache.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();]]></programlisting>
</sect1>
<sect1 id="batch-update" >
<title>Batch updates</title>
<para>
For retrieving and updating data the same ideas apply. In addition, you need to
use <literal>scroll()</literal> to take advantage of server-side cursors for
queries that return many rows of data.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//flush a batch of updates and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();]]></programlisting>
</sect1>
<sect1 id="batch-statelesssession">
<title>The StatelessSession interface</title>
<para>
Alternatively, Hibernate provides a command-oriented API that may be used for
streaming data to and from the database in the form of detached objects. A
<literal>StatelessSession</literal> has no persistence context associated
with it and does not provide many of the higher-level life cycle semantics.
In particular, a stateless session does not implement a first-level cache nor
interact with any second-level or query cache. It does not implement
transactional write-behind or automatic dirty checking. Operations performed
using a stateless session do not ever cascade to associated instances. Collections
are ignored by a stateless session. Operations performed via a stateless session
bypass Hibernate's event model and interceptors. Stateless sessions are vulnerable
to data aliasing effects, due to the lack of a first-level cache. A stateless
session is a lower-level abstraction, much closer to the underlying JDBC.
</para>
<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
session.update(customer);
}
tx.commit();
session.close();]]></programlisting>
<para>
Note that in this code example, the <literal>Customer</literal> instances returned
by the query are immediately detached. They are never associated with any persistence
context.
</para>
<para>
The <literal>insert(), update()</literal> and <literal>delete()</literal> operations
defined by the <literal>StatelessSession</literal> interface are considered to be
direct database row-level operations, which result in immediate execution of a SQL
<literal>INSERT, UPDATE</literal> or <literal>DELETE</literal> respectively. Thus,
they have very different semantics to the <literal>save(), saveOrUpdate()</literal>
and <literal>delete()</literal> operations defined by the <literal>Session</literal>
interface.
</para>
</sect1>
<sect1 id="batch-direct" revision="3">
<title>DML-style operations</title>
<para>
As already discussed, automatic and transparent object/relational mapping is concerned
with the management of object state. This implies that the object state is available
in memory, hence manipulating (using the SQL <literal>Data Manipulation Language</literal>
(DML) statements: <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
data directly in the database will not affect in-memory state. However, Hibernate provides methods
for bulk SQL-style DML statement execution which are performed through the
Hibernate Query Language (<link linkend="queryhql">HQL</link>).
</para>
<para>
The pseudo-syntax for <literal>UPDATE</literal> and <literal>DELETE</literal> statements
is: <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>. Some
points to note:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
In the from-clause, the FROM keyword is optional
</para>
</listitem>
<listitem>
<para>
There can only be a single entity named in the from-clause; it can optionally be
aliased. If the entity name is aliased, then any property references must
be qualified using that alias; if the entity name is not aliased, then it is
illegal for any property references to be qualified.
</para>
</listitem>
<listitem>
<para>
No <link linkend="queryhql-joins-forms">joins</link> (either implicit or explicit)
can be specified in a bulk HQL query. Sub-queries may be used in the where-clause;
the subqueries, themselves, may contain joins.
</para>
</listitem>
<listitem>
<para>
The where-clause is also optional.
</para>
</listitem>
</itemizedlist>
<para>
As an example, to execute an HQL <literal>UPDATE</literal>, use the
<literal>Query.executeUpdate()</literal> method (the method is named for
those familiar with JDBC's <literal>PreparedStatement.executeUpdate()</literal>):
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
HQL <literal>UPDATE</literal> statements, by default do not effect the
<link linkend="mapping-declaration-version">version</link>
or the <link linkend="mapping-declaration-timestamp">timestamp</link> property values
for the affected entities; this is in keeping with the EJB3 specification. However,
you can force Hibernate to properly reset the <literal>version</literal> or
<literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
keyword.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
are not allowed in conjunction with a <literal>update versioned</literal> statement.
</para>
<para>
To execute an HQL <literal>DELETE</literal>, use the same <literal>Query.executeUpdate()</literal>
method:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer c where c.name = :oldName";
// or String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
The <literal>int</literal> value returned by the <literal>Query.executeUpdate()</literal>
method indicate the number of entities effected by the operation. Consider this may or may not
correlate to the number of rows effected in the database. An HQL bulk operation might result in
multiple actual SQL statements being executed, for joined-subclass, for example. The returned
number indicates the number of actual entities affected by the statement. Going back to the
example of joined-subclass, a delete against one of the subclasses may actually result
in deletes against not just the table to which that subclass is mapped, but also the "root"
table and potentially joined-subclass tables further down the inheritence hierarchy.
</para>
<para>
The pseudo-syntax for <literal>INSERT</literal> statements is:
<literal>INSERT INTO EntityName properties_list select_statement</literal>. Some
points to note:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Only the INSERT INTO ... SELECT ... form is supported; not the INSERT INTO ... VALUES ... form.
</para>
<para>
The properties_list is analogous to the <literal>column speficiation</literal>
in the SQL <literal>INSERT</literal> statement. For entities involved in mapped
inheritence, only properties directly defined on that given class-level can be
used in the properties_list. Superclass properties are not allowed; and subclass
properties do not make sense. In other words, <literal>INSERT</literal>
statements are inherently non-polymorphic.
</para>
</listitem>
<listitem>
<para>
select_statement can be any valid HQL select query, with the caveat that the return types
must match the types expected by the insert. Currently, this is checked during query
compilation rather than allowing the check to relegate to the database. Note however
that this might cause problems between Hibernate <literal>Type</literal>s which are
<emphasis>equivalent</emphasis> as opposed to <emphasis>equal</emphasis>. This might cause
issues with mismatches between a property defined as a <literal>org.hibernate.type.DateType</literal>
and a property defined as a <literal>org.hibernate.type.TimestampType</literal>, even though the
database might not make a distinction or might be able to handle the conversion.
</para>
</listitem>
<listitem>
<para>
For the id property, the insert statement gives you two options. You can either
explicitly specify the id property in the properties_list (in which case its value
is taken from the corresponding select expression) or omit it from the properties_list
(in which case a generated value is used). This later option is only available when
using id generators that operate in the database; attempting to use this option with
any "in memory" type generators will cause an exception during parsing. Note that
for the purposes of this discussion, in-database generators are considered to be
<literal>org.hibernate.id.SequenceGenerator</literal> (and its subclasses) and
any implementors of <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
The most notable exception here is <literal>org.hibernate.id.TableHiLoGenerator</literal>,
which cannot be used because it does not expose a selectable way to get its values.
</para>
</listitem>
<listitem>
<para>
For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
the insert statement gives you two options. You can either specify the property in the
properties_list (in which case its value is taken from the corresponding select expressions)
or omit it from the properties_list (in which case the <literal>seed value</literal> defined
by the <literal>org.hibernate.type.VersionType</literal> is used).
</para>
</listitem>
</itemizedlist>
<para>
An example HQL <literal>INSERT</literal> statement execution:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
</sect1>
</chapter>

View File

@ -1,250 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="best-practices" revision="3">
<title>Best Practices</title>
<variablelist spacing="compact">
<varlistentry>
<term>Write fine-grained classes and map them using <literal>&lt;component&gt;</literal>.</term>
<listitem>
<para>
Use an <literal>Address</literal> class to encapsulate <literal>street</literal>,
<literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
This encourages code reuse and simplifies refactoring.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Declare identifier properties on persistent classes.</term>
<listitem>
<para>
Hibernate makes identifier properties optional. There are all sorts of reasons why
you should use them. We recommend that identifiers be 'synthetic' (generated, with
no business meaning).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Identify natural keys.</term>
<listitem>
<para>
Identify natural keys for all entities, and map them using
<literal>&lt;natural-id&gt;</literal>. Implement <literal>equals()</literal> and
<literal>hashCode()</literal> to compare the properties that make up the natural key.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Place each class mapping in its own file.</term>
<listitem>
<para>
Don't use a single monolithic mapping document. Map <literal>com.eg.Foo</literal> in
the file <literal>com/eg/Foo.hbm.xml</literal>. This makes particularly good sense in
a team environment.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Load mappings as resources.</term>
<listitem>
<para>
Deploy the mappings along with the classes they map.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Consider externalising query strings.</term>
<listitem>
<para>
This is a good practice if your queries call non-ANSI-standard SQL functions.
Externalising the query strings to mapping files will make the application more
portable.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Use bind variables.</term>
<listitem>
<para>
As in JDBC, always replace non-constant values by "?". Never use string manipulation to
bind a non-constant value in a query! Even better, consider using named parameters in
queries.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Don't manage your own JDBC connections.</term>
<listitem>
<para>
Hibernate lets the application manage JDBC connections. This approach should be considered
a last-resort. If you can't use the built-in connections providers, consider providing your
own implementation of <literal>org.hibernate.connection.ConnectionProvider</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Consider using a custom type.</term>
<listitem>
<para>
Suppose you have a Java type, say from some library, that needs to be persisted but doesn't
provide the accessors needed to map it as a component. You should consider implementing
<literal>org.hibernate.UserType</literal>. This approach frees the application
code from implementing transformations to / from a Hibernate type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Use hand-coded JDBC in bottlenecks.</term>
<listitem>
<para>
In performance-critical areas of the system, some kinds of operations might benefit from
direct JDBC. But please, wait until you <emphasis>know</emphasis> something is a bottleneck.
And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might
be worth opening a Hibernate <literal>Session</literal> and using that JDBC connection. That
way you can still use the same transaction strategy and underlying connection provider.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Understand <literal>Session</literal> flushing.</term>
<listitem>
<para>
From time to time the Session synchronizes its persistent state with the database. Performance will
be affected if this process occurs too often. You may sometimes minimize unnecessary flushing by
disabling automatic flushing or even by changing the order of queries and other operations within a
particular transaction.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>In a three tiered architecture, consider using detached objects.</term>
<listitem>
<para>
When using a servlet / session bean architecture, you could pass persistent objects loaded in
the session bean to and from the servlet / JSP layer. Use a new session to service each request.
Use <literal>Session.merge()</literal> or <literal>Session.saveOrUpdate()</literal> to
synchronize objects with the database.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>In a two tiered architecture, consider using long persistence contexts.</term>
<listitem>
<para>
Database Transactions have to be as short as possible for best scalability. However, it is often
neccessary to implement long running <emphasis>application transactions</emphasis>, a single
unit-of-work from the point of view of a user. An application transaction might span several
client request/response cycles. It is common to use detached objects to implement application
transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain
a single open persistence contact (session) for the whole life cycle of the application transaction
and simply disconnect from the JDBC connection at the end of each request and reconnect at the
beginning of the subsequent request. Never share a single session across more than one application
transaction, or you will be working with stale data.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Don't treat exceptions as recoverable.</term>
<listitem>
<para>
This is more of a necessary practice than a "best" practice. When an exception occurs, roll back
the <literal>Transaction</literal> and close the <literal>Session</literal>. If you don't, Hibernate
can't guarantee that in-memory state accurately represents persistent state. As a special case of this,
do not use <literal>Session.load()</literal> to determine if an instance with the given identifier
exists on the database; use <literal>Session.get()</literal> or a query instead.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Prefer lazy fetching for associations.</term>
<listitem>
<para>
Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that
are not likely to be completely held in the second-level cache. For associations to cached classes,
where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using
<literal>lazy="false"</literal>. When an join fetching is appropriate to a particular use
case, use a query with a <literal>left join fetch</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Use the <emphasis>open session in view</emphasis> pattern, or a disciplined
<emphasis>assembly phase</emphasis> to avoid problems with unfetched data.
</term>
<listitem>
<para>
Hibernate frees the developer from writing tedious <emphasis>Data Transfer Objects</emphasis> (DTO).
In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem
that entity beans are not serializable; second, they implicitly define an assembly phase where
all data to be used by the view is fetched and marshalled into the DTOs before returning control
to the presentation tier. Hibernate eliminates the first purpose. However, you will still need
an assembly phase (think of your business methods as having a strict contract with the presentation
tier about what data is available in the detached objects) unless you are prepared to hold the
persistence context (the session) open across the view rendering process. This is not a limitation
of Hibernate! It is a fundamental requirement of safe transactional data access.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Consider abstracting your business logic from Hibernate.</term>
<listitem>
<para>
Hide (Hibernate) data-access code behind an interface. Combine the <emphasis>DAO</emphasis> and
<emphasis>Thread Local Session</emphasis> patterns. You can even have some classes persisted by
handcoded JDBC, associated to Hibernate via a <literal>UserType</literal>. (This advice is
intended for "sufficiently large" applications; it is not appropriate for an application with
five tables!)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Don't use exotic association mappings.</term>
<listitem>
<para>
Good usecases for a real many-to-many associations are rare. Most of the time you need
additional information stored in the "link table". In this case, it is much better to
use two one-to-many associations to an intermediate link class. In fact, we think that
most associations are one-to-many and many-to-one, you should be careful when using any
other association style and ask yourself if it is really neccessary.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Prefer bidirectional associations.</term>
<listitem>
<para>
Unidirectional associations are more difficult to query. In a large application, almost
all associations must be navigable in both directions in queries.
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>

View File

@ -1,429 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="components">
<title>Component Mapping</title>
<para>
The notion of a <emphasis>component</emphasis> is re-used in several different contexts,
for different purposes, throughout Hibernate.
</para>
<sect1 id="components-dependentobjects" revision="2" >
<title>Dependent objects</title>
<para>
A component is a contained object that is persisted as a value type, not an entity
reference. The term "component" refers to the object-oriented notion of composition
(not to architecture-level components). For example, you might model a person like this:
</para>
<programlisting><![CDATA[public class Person {
private java.util.Date birthday;
private Name name;
private String key;
public String getKey() {
return key;
}
private void setKey(String key) {
this.key=key;
}
public java.util.Date getBirthday() {
return birthday;
}
public void setBirthday(java.util.Date birthday) {
this.birthday = birthday;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}]]></programlisting>
<programlisting><![CDATA[public class Name {
char initial;
String first;
String last;
public String getFirst() {
return first;
}
void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
void setLast(String last) {
this.last = last;
}
public char getInitial() {
return initial;
}
void setInitial(char initial) {
this.initial = initial;
}
}]]></programlisting>
<para>
Now <literal>Name</literal> may be persisted as a component of
<literal>Person</literal>. Notice that <literal>Name</literal> defines getter
and setter methods for its persistent properties, but doesn't need to declare
any interfaces or identifier properties.
</para>
<para>
Our Hibernate mapping would look like:
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
<para>
The person table would have the columns <literal>pid</literal>,
<literal>birthday</literal>,
<literal>initial</literal>,
<literal>first</literal> and
<literal>last</literal>.
</para>
<para>
Like all value types, components do not support shared references. In other words, two
persons could have the same name, but the two person objects would contain two independent
name ojects, only "the same" by value. The null value semantics of a component are
<emphasis>ad hoc</emphasis>. When reloading the containing object, Hibernate will assume
that if all component columns are null, then the entire component is null. This should
be okay for most purposes.
</para>
<para>
The properties of a component may be of any Hibernate type (collections, many-to-one
associations, other components, etc). Nested components should <emphasis>not</emphasis>
be considered an exotic usage. Hibernate is intended to support a very fine-grained
object model.
</para>
<para>
The <literal>&lt;component&gt;</literal> element allows a <literal>&lt;parent&gt;</literal>
subelement that maps a property of the component class as a reference back to the
containing entity.
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- reference back to the Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
</sect1>
<sect1 id="components-incollections" revision="1">
<title>Collections of dependent objects</title>
<para>
Collections of components are supported (eg. an array of type
<literal>Name</literal>). Declare your component collection by
replacing the <literal>&lt;element&gt;</literal> tag with a
<literal>&lt;composite-element&gt;</literal> tag.
</para>
<programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
<key column="id"/>
<composite-element class="eg.Name"> <!-- class attribute required -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set>]]></programlisting>
<para>
Note: if you define a <literal>Set</literal> of composite elements, it is
very important to implement <literal>equals()</literal> and
<literal>hashCode()</literal> correctly.
</para>
<para>
Composite elements may contain components but not collections. If your
composite element itself contains
components, use the <literal>&lt;nested-composite-element&gt;</literal>
tag. This is a pretty exotic case - a collection of components which
themselves have components. By this stage you should be asking yourself
if a one-to-many association is more appropriate. Try remodelling the
composite element as an entity - but note that even though the Java model
is the same, the relational model and persistence semantics are still
slightly different.
</para>
<para>
Please note that a composite element mapping doesn't support null-able properties
if you're using a <literal>&lt;set&gt;</literal>. Hibernate
has to use each columns value to identify a record when deleting objects
(there is no separate primary key column in the composite element table),
which is not possible with null values. You have to either use only
not-null properties in a composite-element or choose a
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal> or <literal>&lt;idbag&gt;</literal>.
</para>
<para>
A special case of a composite element is a composite element with a nested
<literal>&lt;many-to-one&gt;</literal> element. A mapping like this allows
you to map extra columns of a many-to-many association table to the
composite element class. The following is a many-to-many association
from <literal>Order</literal> to <literal>Item</literal> where
<literal>purchaseDate</literal>, <literal>price</literal> and
<literal>quantity</literal> are properties of the association:
</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.Purchase">
<property name="purchaseDate"/>
<property name="price"/>
<property name="quantity"/>
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
</composite-element>
</set>
</class>]]></programlisting>
<para>
Of course, there can't be a reference to the purchae on the other side, for
bidirectional association navigation. Remember that components are value types and
don't allow shared references. A single <literal>Purchase</literal> can be in the
set of an <literal>Order</literal>, but it can't be referenced by the <literal>Item</literal>
at the same time.
</para>
<para>Even ternary (or quaternary, etc) associations are possible:</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.OrderLine">
<many-to-one name="purchaseDetails class="eg.Purchase"/>
<many-to-one name="item" class="eg.Item"/>
</composite-element>
</set>
</class>]]></programlisting>
<para>
Composite elements may appear in queries using the same syntax as
associations to other entities.
</para>
</sect1>
<sect1 id="components-asmapindex">
<title>Components as Map indices</title>
<para>
The <literal>&lt;composite-map-key&gt;</literal> element lets you map a
component class as the key of a <literal>Map</literal>. Make sure you override
<literal>hashCode()</literal> and <literal>equals()</literal> correctly on
the component class.
</para>
</sect1>
<sect1 id="components-compositeid" revision="1">
<title>Components as composite identifiers</title>
<para>
You may use a component as an identifier of an entity class. Your component
class must satisfy certain requirements:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
It must implement <literal>java.io.Serializable</literal>.
</para>
</listitem>
<listitem>
<para>
It must re-implement <literal>equals()</literal> and
<literal>hashCode()</literal>, consistently with the database's
notion of composite key equality.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>Note: in Hibernate3, the second requirement is not an absolutely hard
requirement of Hibernate. But do it anyway.</emphasis>
</para>
<para>
You can't use an <literal>IdentifierGenerator</literal> to generate composite keys.
Instead the application must assign its own identifiers.
</para>
<para>
Use the <literal>&lt;composite-id&gt;</literal> tag (with nested
<literal>&lt;key-property&gt;</literal> elements) in place of the usual
<literal>&lt;id&gt;</literal> declaration. For example, the
<literal>OrderLine</literal> class has a primary key that depends upon
the (composite) primary key of <literal>Order</literal>.
</para>
<programlisting><![CDATA[<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class>]]></programlisting>
<para>
Now, any foreign keys referencing the <literal>OrderLine</literal> table are also
composite. You must declare this in your mappings for other classes. An association
to <literal>OrderLine</literal> would be mapped like this:
</para>
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>]]></programlisting>
<para>
(Note that the <literal>&lt;column&gt;</literal> tag is an alternative to the
<literal>column</literal> attribute everywhere.)
</para>
<para>
A <literal>many-to-many</literal> association to <literal>OrderLine</literal> also
uses the composite foreign key:
</para>
<programlisting><![CDATA[<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set>]]></programlisting>
<para>
The collection of <literal>OrderLine</literal>s in <literal>Order</literal> would
use:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(The <literal>&lt;one-to-many&gt;</literal> element, as usual, declares no columns.)
</para>
<para>
If <literal>OrderLine</literal> itself owns a collection, it also has a composite
foreign key.
</para>
<programlisting><![CDATA[<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key> <!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>]]></programlisting>
</sect1>
<sect1 id="components-dynamic" revision="1">
<title>Dynamic components</title>
<para>
You may even map a property of type <literal>Map</literal>:
</para>
<programlisting><![CDATA[<dynamic-component name="userAttributes">
<property name="foo" column="FOO" type="string"/>
<property name="bar" column="BAR" type="integer"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>]]></programlisting>
<para>
The semantics of a <literal>&lt;dynamic-component&gt;</literal> mapping are identical
to <literal>&lt;component&gt;</literal>. The advantage of this kind of mapping is
the ability to determine the actual properties of the bean at deployment time, just
by editing the mapping document. Runtime manipulation of the mapping document is
also possible, using a DOM parser. Even better, you can access (and change) Hibernate's
configuration-time metamodel via the <literal>Configuration</literal> object.
</para>
</sect1>
</chapter>

View File

@ -1,292 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="events">
<title>Interceptors and events</title>
<para>
It is often useful for the application to react to certain events that occur
inside Hibernate. This allows implementation of certain kinds of generic
functionality, and extension of Hibernate functionality.
</para>
<sect1 id="objectstate-interceptors" revision="3">
<title>Interceptors</title>
<para>
The <literal>Interceptor</literal> interface provides callbacks from the session to the
application allowing the application to inspect and/or manipulate properties of a
persistent object before it is saved, updated, deleted or loaded. One
possible use for this is to track auditing information. For example, the following
<literal>Interceptor</literal> automatically sets the <literal>createTimestamp</literal>
when an <literal>Auditable</literal> is created and updates the
<literal>lastUpdateTimestamp</literal> property when an <literal>Auditable</literal> is
updated.
</para>
<para>
You may either implement <literal>Interceptor</literal> directly or (better) extend
<literal>EmptyInterceptor</literal>.
</para>
<programlisting><![CDATA[package org.hibernate.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class AuditInterceptor extends EmptyInterceptor {
private int updates;
private int creates;
private int loads;
public void onDelete(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// do nothing
}
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
updates++;
for ( int i=0; i < propertyNames.length; i++ ) {
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
currentState[i] = new Date();
return true;
}
}
}
return false;
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
loads++;
}
return false;
}
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
creates++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "createTimestamp".equals( propertyNames[i] ) ) {
state[i] = new Date();
return true;
}
}
}
return false;
}
public void afterTransactionCompletion(Transaction tx) {
if ( tx.wasCommitted() ) {
System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
}
updates=0;
creates=0;
loads=0;
}
}]]></programlisting>
<para>
Interceptors come in two flavors: <literal>Session</literal>-scoped and
<literal>SessionFactory</literal>-scoped.
</para>
<para>
A <literal>Session</literal>-scoped interceptor is specified
when a session is opened using one of the overloaded SessionFactory.openSession()
methods accepting an <literal>Interceptor</literal>.
</para>
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
<para>
A <literal>SessionFactory</literal>-scoped interceptor is registered with the <literal>Configuration</literal>
object prior to building the <literal>SessionFactory</literal>. In this case, the supplied interceptor
will be applied to all sessions opened from that <literal>SessionFactory</literal>; this is true unless
a session is opened explicitly specifying the interceptor to use. <literal>SessionFactory</literal>-scoped
interceptors must be thread safe, taking care to not store session-specific state since multiple
sessions will use this interceptor (potentially) concurrently.
</para>
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
</sect1>
<sect1 id="objectstate-events" revision="4">
<title>Event system</title>
<para>
If you have to react to particular events in your persistence layer, you may
also use the Hibernate3 <emphasis>event</emphasis> architecture. The event
system can be used in addition or as a replacement for interceptors.
</para>
<para>
Essentially all of the methods of the <literal>Session</literal> interface correlate
to an event. You have a <literal>LoadEvent</literal>, a <literal>FlushEvent</literal>, etc
(consult the XML configuration-file DTD or the <literal>org.hibernate.event</literal>
package for the full list of defined event types). When a request is made of one of
these methods, the Hibernate <literal>Session</literal> generates an appropriate
event and passes it to the configured event listeners for that type. Out-of-the-box,
these listeners implement the same processing in which those methods always resulted.
However, you are free to implement a customization of one of the listener interfaces
(i.e., the <literal>LoadEvent</literal> is processed by the registered implemenation
of the <literal>LoadEventListener</literal> interface), in which case their
implementation would be responsible for processing any <literal>load()</literal> requests
made of the <literal>Session</literal>.
</para>
<para>
The listeners should be considered effectively singletons; meaning, they are shared between
requests, and thus should not save any state as instance variables.
</para>
<para>
A custom listener should implement the appropriate interface for the event it wants to
process and/or extend one of the convenience base classes (or even the default event
listeners used by Hibernate out-of-the-box as these are declared non-final for this
purpose). Custom listeners can either be registered programmatically through the
<literal>Configuration</literal> object, or specified in the Hibernate configuration
XML (declarative configuration through the properties file is not supported). Here's an
example of a custom load event listener:
</para>
<programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
// this is the single method defined by the LoadEventListener interface
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
}
}]]></programlisting>
<para>
You also need a configuration entry telling Hibernate to use the listener in addition
to the default listener:
</para>
<programlisting><![CDATA[<hibernate-configuration>
<session-factory>
...
<event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
Instead, you may register it programmatically:
</para>
<programlisting><![CDATA[Configuration cfg = new Configuration();
LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
<para>
Listeners registered declaratively cannot share instances. If the same class name is
used in multiple <literal>&lt;listener/&gt;</literal> elements, each reference will
result in a separate instance of that class. If you need the capability to share
listener instances between listener types you must use the programmatic registration
approach.
</para>
<para>
Why implement an interface and define the specific type during configuration? Well, a
listener implementation could implement multiple event listener interfaces. Having the
type additionally defined during registration makes it easier to turn custom listeners on
or off during configuration.
</para>
</sect1>
<sect1 id="objectstate-decl-security" revision="2">
<title>Hibernate declarative security</title>
<para>
Usually, declarative security in Hibernate applications is managed in a session facade
layer. Now, Hibernate3 allows certain actions to be permissioned via JACC, and authorized
via JAAS. This is optional functionality built on top of the event architecture.
</para>
<para>
First, you must configure the appropriate event listeners, to enable the use of JAAS
authorization.
</para>
<programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
<para>
Note that <literal>&lt;listener type="..." class="..."/&gt;</literal> is just a shorthand
for <literal>&lt;event type="..."&gt;&lt;listener class="..."/&gt;&lt;/event&gt;</literal>
when there is exactly one listener for a particular event type.
</para>
<para>
Next, still in <literal>hibernate.cfg.xml</literal>, bind the permissions to roles:
</para>
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
<para>
The role names are the roles understood by your JACC provider.
</para>
</sect1>
</chapter>

View File

@ -1,685 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="example-mappings">
<title>Example: Various Mappings</title>
<para>
This chapters shows off some more complex association mappings.
</para>
<sect1 id="example-mappings-emp">
<title>Employer/Employee</title>
<para>
The following model of the relationship between <literal>Employer</literal> and
<literal>Employee</literal> uses an actual entity class (<literal>Employment</literal>)
to represent the association. This is done because there might be more than one
period of employment for the same two parties. Components are used to model monetary
values and employee names.
</para>
<mediaobject>
<imageobject role="html">
<imagedata fileref="../images/EmployerEmployee.png" format="PNG" align="center" />
</imageobject>
<imageobject role="fo">
<imagedata fileref="../images/EmployerEmployee.png" format="PNG" align="center" width="17cm" />
</imageobject>
</mediaobject>
<para>
Heres a possible mapping document:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Employer" table="employers">
<id name="id">
<generator class="sequence">
<param name="sequence">employer_id_seq</param>
</generator>
</id>
<property name="name"/>
</class>
<class name="Employment" table="employment_periods">
<id name="id">
<generator class="sequence">
<param name="sequence">employment_id_seq</param>
</generator>
</id>
<property name="startDate" column="start_date"/>
<property name="endDate" column="end_date"/>
<component name="hourlyRate" class="MonetaryAmount">
<property name="amount">
<column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
</property>
<property name="currency" length="12"/>
</component>
<many-to-one name="employer" column="employer_id" not-null="true"/>
<many-to-one name="employee" column="employee_id" not-null="true"/>
</class>
<class name="Employee" table="employees">
<id name="id">
<generator class="sequence">
<param name="sequence">employee_id_seq</param>
</generator>
</id>
<property name="taxfileNumber"/>
<component name="name" class="Name">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</component>
</class>
</hibernate-mapping>]]></programlisting>
<para>
And heres the table schema generated by <literal>SchemaExport</literal>.
</para>
<programlisting><![CDATA[create table employers (
id BIGINT not null,
name VARCHAR(255),
primary key (id)
)
create table employment_periods (
id BIGINT not null,
hourly_rate NUMERIC(12, 2),
currency VARCHAR(12),
employee_id BIGINT not null,
employer_id BIGINT not null,
end_date TIMESTAMP,
start_date TIMESTAMP,
primary key (id)
)
create table employees (
id BIGINT not null,
firstName VARCHAR(255),
initial CHAR(1),
lastName VARCHAR(255),
taxfileNumber VARCHAR(255),
primary key (id)
)
alter table employment_periods
add constraint employment_periodsFK0 foreign key (employer_id) references employers
alter table employment_periods
add constraint employment_periodsFK1 foreign key (employee_id) references employees
create sequence employee_id_seq
create sequence employment_id_seq
create sequence employer_id_seq]]></programlisting>
</sect1>
<sect1 id="example-mappings-authorwork">
<title>Author/Work</title>
<para>
Consider the following model of the relationships between <literal>Work</literal>,
<literal>Author</literal> and <literal>Person</literal>. We represent the relationship
between <literal>Work</literal> and <literal>Author</literal> as a many-to-many
association. We choose to represent the relationship between <literal>Author</literal>
and <literal>Person</literal> as one-to-one association. Another possibility would be to
have <literal>Author</literal> extend <literal>Person</literal>.
</para>
<mediaobject>
<imageobject role="html">
<imagedata fileref="../images/AuthorWork.png" format="PNG" align="center" />
</imageobject>
<imageobject role="fo">
<imagedata fileref="../images/AuthorWork.png" format="PNG" align="center" width="17cm" />
</imageobject>
</mediaobject>
<para>
The following mapping document correctly represents these relationships:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Work" table="works" discriminator-value="W">
<id name="id" column="id">
<generator class="native"/>
</id>
<discriminator column="type" type="character"/>
<property name="title"/>
<set name="authors" table="author_work">
<key column name="work_id"/>
<many-to-many class="Author" column name="author_id"/>
</set>
<subclass name="Book" discriminator-value="B">
<property name="text"/>
</subclass>
<subclass name="Song" discriminator-value="S">
<property name="tempo"/>
<property name="genre"/>
</subclass>
</class>
<class name="Author" table="authors">
<id name="id" column="id">
<!-- The Author must have the same identifier as the Person -->
<generator class="assigned"/>
</id>
<property name="alias"/>
<one-to-one name="person" constrained="true"/>
<set name="works" table="author_work" inverse="true">
<key column="author_id"/>
<many-to-many class="Work" column="work_id"/>
</set>
</class>
<class name="Person" table="persons">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
There are four tables in this mapping. <literal>works</literal>,
<literal>authors</literal> and <literal>persons</literal> hold work, author
and person data respectively. <literal>author_work</literal> is an association
table linking authors to works. Heres the table schema, as generated by
<literal>SchemaExport</literal>.
</para>
<programlisting><![CDATA[create table works (
id BIGINT not null generated by default as identity,
tempo FLOAT,
genre VARCHAR(255),
text INTEGER,
title VARCHAR(255),
type CHAR(1) not null,
primary key (id)
)
create table author_work (
author_id BIGINT not null,
work_id BIGINT not null,
primary key (work_id, author_id)
)
create table authors (
id BIGINT not null generated by default as identity,
alias VARCHAR(255),
primary key (id)
)
create table persons (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
alter table authors
add constraint authorsFK0 foreign key (id) references persons
alter table author_work
add constraint author_workFK0 foreign key (author_id) references authors
alter table author_work
add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
</sect1>
<sect1 id="example-mappings-customerorderproduct">
<title>Customer/Order/Product</title>
<para>
Now consider a model of the relationships between <literal>Customer</literal>,
<literal>Order</literal> and <literal>LineItem</literal> and <literal>Product</literal>.
There is a one-to-many association between <literal>Customer</literal> and
<literal>Order</literal>, but how should we represent <literal>Order</literal> /
<literal>LineItem</literal> / <literal>Product</literal>? I've chosen to map
<literal>LineItem</literal> as an association class representing the many-to-many
association between <literal>Order</literal> and <literal>Product</literal>. In
Hibernate, this is called a composite element.
</para>
<mediaobject>
<imageobject role="html">
<imagedata fileref="../images/CustomerOrderProduct.png" format="PNG" align="center" />
</imageobject>
<imageobject role="fo">
<imagedata fileref="../images/CustomerOrderProduct.png" format="PNG" align="center" width="17cm" />
</imageobject>
</mediaobject>
<para>
The mapping document:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Customer" table="customers">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="orders" inverse="true">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
</class>
<class name="Order" table="orders">
<id name="id">
<generator class="native"/>
</id>
<property name="date"/>
<many-to-one name="customer" column="customer_id"/>
<list name="lineItems" table="line_items">
<key column="order_id"/>
<list-index column="line_number"/>
<composite-element class="LineItem">
<property name="quantity"/>
<many-to-one name="product" column="product_id"/>
</composite-element>
</list>
</class>
<class name="Product" table="products">
<id name="id">
<generator class="native"/>
</id>
<property name="serialNumber"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
<literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> and
<literal>products</literal> hold customer, order, order line item and product data
respectively. <literal>line_items</literal> also acts as an association table linking
orders with products.
</para>
<programlisting><![CDATA[create table customers (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
create table orders (
id BIGINT not null generated by default as identity,
customer_id BIGINT,
date TIMESTAMP,
primary key (id)
)
create table line_items (
line_number INTEGER not null,
order_id BIGINT not null,
product_id BIGINT,
quantity INTEGER,
primary key (order_id, line_number)
)
create table products (
id BIGINT not null generated by default as identity,
serialNumber VARCHAR(255),
primary key (id)
)
alter table orders
add constraint ordersFK0 foreign key (customer_id) references customers
alter table line_items
add constraint line_itemsFK0 foreign key (product_id) references products
alter table line_items
add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
</sect1>
<sect1 id="misc">
<title>Miscellaneous example mappings</title>
<para>
These examples are all taken from the Hibernate test suite. You
will find many other useful example mappings there. Look in the
<literal>test</literal> folder of the Hibernate distribution.
</para>
<para>TODO: put words around this stuff</para>
<sect2 id="example-mappings-typed-onetone">
<title>"Typed" one-to-one association</title>
<programlisting><![CDATA[<class name="Person">
<id name="name"/>
<one-to-one name="address"
cascade="all">
<formula>name</formula>
<formula>'HOME'</formula>
</one-to-one>
<one-to-one name="mailingAddress"
cascade="all">
<formula>name</formula>
<formula>'MAILING'</formula>
</one-to-one>
</class>
<class name="Address" batch-size="2"
check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
<composite-id>
<key-many-to-one name="person"
column="personName"/>
<key-property name="type"
column="addressType"/>
</composite-id>
<property name="street" type="text"/>
<property name="state"/>
<property name="zip"/>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-composite-key">
<title>Composite key example</title>
<programlisting><![CDATA[<class name="Customer">
<id name="customerId"
length="10">
<generator class="assigned"/>
</id>
<property name="name" not-null="true" length="100"/>
<property name="address" not-null="true" length="200"/>
<list name="orders"
inverse="true"
cascade="save-update">
<key column="customerId"/>
<index column="orderNumber"/>
<one-to-many class="Order"/>
</list>
</class>
<class name="Order" table="CustomerOrder" lazy="true">
<synchronize table="LineItem"/>
<synchronize table="Product"/>
<composite-id name="id"
class="Order$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
</composite-id>
<property name="orderDate"
type="calendar_date"
not-null="true"/>
<property name="total">
<formula>
( select sum(li.quantity*p.price)
from LineItem li, Product p
where li.productId = p.productId
and li.customerId = customerId
and li.orderNumber = orderNumber )
</formula>
</property>
<many-to-one name="customer"
column="customerId"
insert="false"
update="false"
not-null="true"/>
<bag name="lineItems"
fetch="join"
inverse="true"
cascade="save-update">
<key>
<column name="customerId"/>
<column name="orderNumber"/>
</key>
<one-to-many class="LineItem"/>
</bag>
</class>
<class name="LineItem">
<composite-id name="id"
class="LineItem$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
<key-property name="productId" length="10"/>
</composite-id>
<property name="quantity"/>
<many-to-one name="order"
insert="false"
update="false"
not-null="true">
<column name="customerId"/>
<column name="orderNumber"/>
</many-to-one>
<many-to-one name="product"
insert="false"
update="false"
not-null="true"
column="productId"/>
</class>
<class name="Product">
<synchronize table="LineItem"/>
<id name="productId"
length="10">
<generator class="assigned"/>
</id>
<property name="description"
not-null="true"
length="200"/>
<property name="price" length="3"/>
<property name="numberAvailable"/>
<property name="numberOrdered">
<formula>
( select sum(li.quantity)
from LineItem li
where li.productId = productId )
</formula>
</property>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-composite-key-manytomany">
<title>Many-to-many with shared composite key attribute</title>
<programlisting><![CDATA[<class name="User" table="`User`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<set name="groups" table="UserGroup">
<key>
<column name="userName"/>
<column name="org"/>
</key>
<many-to-many class="Group">
<column name="groupName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
<class name="Group" table="`Group`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<property name="description"/>
<set name="users" table="UserGroup" inverse="true">
<key>
<column name="groupName"/>
<column name="org"/>
</key>
<many-to-many class="User">
<column name="userName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
]]></programlisting>
</sect2>
<sect2 id="example-mappings-content-discrimination">
<title>Content based discrimination</title>
<programlisting><![CDATA[<class name="Person"
discriminator-value="P">
<id name="id"
column="person_id"
unsaved-value="0">
<generator class="native"/>
</id>
<discriminator
type="character">
<formula>
case
when title is not null then 'E'
when salesperson is not null then 'C'
else 'P'
end
</formula>
</discriminator>
<property name="name"
not-null="true"
length="80"/>
<property name="sex"
not-null="true"
update="false"/>
<component name="address">
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</component>
<subclass name="Employee"
discriminator-value="E">
<property name="title"
length="20"/>
<property name="salary"/>
<many-to-one name="manager"/>
</subclass>
<subclass name="Customer"
discriminator-value="C">
<property name="comments"/>
<many-to-one name="salesperson"/>
</subclass>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-association-alternatekeys" revision="2">
<title>Associations on alternate keys</title>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" length="100"/>
<one-to-one name="address"
property-ref="person"
cascade="all"
fetch="join"/>
<set name="accounts"
inverse="true">
<key column="userId"
property-ref="userId"/>
<one-to-many class="Account"/>
</set>
<property name="userId" length="8"/>
</class>
<class name="Address">
<id name="id">
<generator class="hilo"/>
</id>
<property name="address" length="300"/>
<property name="zip" length="5"/>
<property name="country" length="25"/>
<many-to-one name="person" unique="true" not-null="true"/>
</class>
<class name="Account">
<id name="accountId" length="32">
<generator class="uuid"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
<property name="type" not-null="true"/>
</class>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

@ -1,388 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="example-parentchild">
<title>Example: Parent/Child</title>
<para>
One of the very first things that new users try to do with Hibernate is to model a parent / child type
relationship. There are two different approaches to this. For various reasons the most convenient
approach, especially for new users, is to model both <literal>Parent</literal> and <literal>Child</literal>
as entity classes with a <literal>&lt;one-to-many&gt;</literal> association from <literal>Parent</literal>
to <literal>Child</literal>. (The alternative approach is to declare the <literal>Child</literal> as a
<literal>&lt;composite-element&gt;</literal>.) Now, it turns out that default semantics of a one to many
association (in Hibernate) are much less close to the usual semantics of a parent / child relationship than
those of a composite element mapping. We will explain how to use a <emphasis>bidirectional one to many
association with cascades</emphasis> to model a parent / child relationship efficiently and elegantly.
It's not at all difficult!
</para>
<sect1 id="example-parentchild-collections">
<title>A note about collections</title>
<para>
Hibernate collections are considered to be a logical part of their owning entity; never of the
contained entities. This is a crucial distinction! It has the following consequences:
</para>
<itemizedlist>
<listitem>
<para>
When we remove / add an object from / to a collection, the version number of the collection owner
is incremented.
</para>
</listitem>
<listitem>
<para>
If an object that was removed from a collection is an instance of a value type (eg, a composite
element), that object will cease to be persistent and its state will be completely removed from
the database. Likewise, adding a value type instance to the collection will cause its state to be
immediately persistent.
</para>
</listitem>
<listitem>
<para>
On the other hand, if an entity is removed from a collection (a one-to-many or many-to-many
association), it will not be deleted, by default. This behaviour is completely consistent - a
change to the internal state of another entity should not cause the associated entity to vanish!
Likewise, adding an entity to a collection does not cause that entity to become persistent, by
default.
</para>
</listitem>
</itemizedlist>
<para>
Instead, the default behaviour is that adding an entity to a collection merely creates a link between
the two entities, while removing it removes the link. This is very appropriate for all sorts of cases.
Where it is not appropriate at all is the case of a parent / child relationship, where the life of the
child is bound to the life cycle of the parent.
</para>
</sect1>
<sect1 id="example-parentchild-bidir">
<title>Bidirectional one-to-many</title>
<para>
Suppose we start with a simple <literal>&lt;one-to-many&gt;</literal> association from
<literal>Parent</literal> to <literal>Child</literal>.
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
If we were to execute the following code
</para>
<programlisting><![CDATA[Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();]]></programlisting>
<para>
Hibernate would issue two SQL statements:
</para>
<itemizedlist>
<listitem>
<para>an <literal>INSERT</literal> to create the record for <literal>c</literal></para>
</listitem>
<listitem>
<para>
an <literal>UPDATE</literal> to create the link from <literal>p</literal> to
<literal>c</literal>
</para>
</listitem>
</itemizedlist>
<para>
This is not only inefficient, but also violates any <literal>NOT NULL</literal> constraint on the
<literal>parent_id</literal> column. We can fix the nullability constraint violation by specifying
<literal>not-null="true"</literal> in the collection mapping:
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
However, this is not the recommended solution.
</para>
<para>
The underlying cause of this behaviour is that the link (the foreign key <literal>parent_id</literal>)
from <literal>p</literal> to <literal>c</literal> is not considered part of the state of the
<literal>Child</literal> object and is therefore not created in the <literal>INSERT</literal>. So the
solution is to make the link part of the <literal>Child</literal> mapping.
</para>
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
<para>
(We also need to add the <literal>parent</literal> property to the <literal>Child</literal> class.)
</para>
<para>
Now that the <literal>Child</literal> entity is managing the state of the link, we tell the collection
not to update the link. We use the <literal>inverse</literal> attribute.
</para>
<programlisting><![CDATA[<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
The following code would be used to add a new <literal>Child</literal>
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();]]></programlisting>
<para>
And now, only one SQL <literal>INSERT</literal> would be issued!
</para>
<para>
To tighten things up a bit, we could create an <literal>addChild()</literal> method of
<literal>Parent</literal>.
</para>
<programlisting><![CDATA[public void addChild(Child c) {
c.setParent(this);
children.add(c);
}]]></programlisting>
<para>
Now, the code to add a <literal>Child</literal> looks like
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();]]></programlisting>
</sect1>
<sect1 id="example-parentchild-cascades">
<title>Cascading life cycle</title>
<para>
The explicit call to <literal>save()</literal> is still annoying. We will address this by
using cascades.
</para>
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
This simplifies the code above to
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();]]></programlisting>
<para>
Similarly, we don't need to iterate over the children when saving or deleting a <literal>Parent</literal>.
The following removes <literal>p</literal> and all its children from the database.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();]]></programlisting>
<para>
However, this code
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();]]></programlisting>
<para>
will not remove <literal>c</literal> from the database; it will ony remove the link to <literal>p</literal>
(and cause a <literal>NOT NULL</literal> constraint violation, in this case). You need to explicitly
<literal>delete()</literal> the <literal>Child</literal>.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();]]></programlisting>
<para>
Now, in our case, a <literal>Child</literal> can't really exist without its parent. So if we remove
a <literal>Child</literal> from the collection, we really do want it to be deleted. For this, we must
use <literal>cascade="all-delete-orphan"</literal>.
</para>
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Note: even though the collection mapping specifies <literal>inverse="true"</literal>, cascades are
still processed by iterating the collection elements. So if you require that an object be saved,
deleted or updated by cascade, you must add it to the collection. It is not enough to simply call
<literal>setParent()</literal>.
</para>
</sect1>
<sect1 id="example-parentchild-update">
<title>Cascades and <literal>unsaved-value</literal></title>
<para>
Suppose we loaded up a <literal>Parent</literal> in one <literal>Session</literal>, made some changes
in a UI action and wish to persist these changes in a new session by calling <literal>update()</literal>.
The <literal>Parent</literal> will contain a collection of childen and, since cascading update is enabled,
Hibernate needs to know which children are newly instantiated and which represent existing rows in the
database. Lets assume that both <literal>Parent</literal> and <literal>Child</literal> have genenerated
identifier properties of type <literal>Long</literal>. Hibernate will use the identifier and
version/timestamp property value to determine which of the children are new. (See
<xref linkend="objectstate-saveorupdate"/>.) <emphasis>In Hibernate3, it is no longer necessary to specify
an <literal>unsaved-value</literal> explicitly.</emphasis>
</para>
<para>
The following code will update <literal>parent</literal> and <literal>child</literal> and insert
<literal>newChild</literal>.
</para>
<programlisting><![CDATA[//parent and child were both loaded in a previous session
parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
session.update(parent);
session.flush();]]></programlisting>
<para>
Well, that's all very well for the case of a generated identifier, but what about assigned identifiers
and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to
distinguish between a newly instantiated object (with an identifier assigned by the user) and an
object loaded in a previous session. In this case, Hibernate will either use the timestamp or version
property, or will actually query the second-level cache or, worst case, the database, to see if the
row exists.
</para>
<!-- undocumenting
<para>
There is one further possibility. The <literal>Interceptor</literal> method named
<literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
newly instantiated objects. For example, you could define a base class for your persistent classes.
</para>
<programlisting><![CDATA[public class Persistent {
private boolean _saved = false;
public void onSave() {
_saved=true;
}
public void onLoad() {
_saved=true;
}
......
public boolean isSaved() {
return _saved;
}
}]]></programlisting>
<para>
(The <literal>saved</literal> property is non-persistent.)
Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
and <literal>onSave()</literal> as follows.
</para>
<programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
if (entity instanceof Persistent) {
return new Boolean( !( (Persistent) entity ).isSaved() );
}
else {
return null;
}
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
return false;
}
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
return false;
}]]></programlisting>
<para>
Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
</para>
-->
</sect1>
<sect1 id="example-parentchild-conclusion">
<title>Conclusion</title>
<para>
There is quite a bit to digest here and it might look confusing first time around. However, in practice,
it all works out very nicely. Most Hibernate applications use the parent / child pattern in many places.
</para>
<para>
We mentioned an alternative in the first paragraph. None of the above issues exist in the case of
<literal>&lt;composite-element&gt;</literal> mappings, which have exactly the semantics of a parent / child
relationship. Unfortunately, there are two big limitations to composite element classes: composite elements
may not own collections, and they should not be the child of any entity other than the unique parent.
</para>
</sect1>
</chapter>

View File

@ -1,457 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="example-weblog">
<title>Example: Weblog Application</title>
<sect1 id="example-weblog-classes">
<title>Persistent Classes</title>
<para>
The persistent classes represent a weblog, and an item posted
in a weblog. They are to be modelled as a standard parent/child
relationship, but we will use an ordered bag, instead of a set.
</para>
<programlisting><![CDATA[package eg;
import java.util.List;
public class Blog {
private Long _id;
private String _name;
private List _items;
public Long getId() {
return _id;
}
public List getItems() {
return _items;
}
public String getName() {
return _name;
}
public void setId(Long long1) {
_id = long1;
}
public void setItems(List list) {
_items = list;
}
public void setName(String string) {
_name = string;
}
}]]></programlisting>
<programlisting><![CDATA[package eg;
import java.text.DateFormat;
import java.util.Calendar;
public class BlogItem {
private Long _id;
private Calendar _datetime;
private String _text;
private String _title;
private Blog _blog;
public Blog getBlog() {
return _blog;
}
public Calendar getDatetime() {
return _datetime;
}
public Long getId() {
return _id;
}
public String getText() {
return _text;
}
public String getTitle() {
return _title;
}
public void setBlog(Blog blog) {
_blog = blog;
}
public void setDatetime(Calendar calendar) {
_datetime = calendar;
}
public void setId(Long long1) {
_id = long1;
}
public void setText(String string) {
_text = string;
}
public void setTitle(String string) {
_title = string;
}
}]]></programlisting>
</sect1>
<sect1 id="example-weblog-mappings">
<title>Hibernate Mappings</title>
<para>
The XML mappings should now be quite straightforward.
</para>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="Blog"
table="BLOGS">
<id
name="id"
column="BLOG_ID">
<generator class="native"/>
</id>
<property
name="name"
column="NAME"
not-null="true"
unique="true"/>
<bag
name="items"
inverse="true"
order-by="DATE_TIME"
cascade="all">
<key column="BLOG_ID"/>
<one-to-many class="BlogItem"/>
</bag>
</class>
</hibernate-mapping>]]></programlisting>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="BlogItem"
table="BLOG_ITEMS"
dynamic-update="true">
<id
name="id"
column="BLOG_ITEM_ID">
<generator class="native"/>
</id>
<property
name="title"
column="TITLE"
not-null="true"/>
<property
name="text"
column="TEXT"
not-null="true"/>
<property
name="datetime"
column="DATE_TIME"
not-null="true"/>
<many-to-one
name="blog"
column="BLOG_ID"
not-null="true"/>
</class>
</hibernate-mapping>]]></programlisting>
</sect1>
<sect1 id="example-weblog-code">
<title>Hibernate Code</title>
<para>
The following class demonstrates some of the kinds of things
we can do with these classes, using Hibernate.
</para>
<programlisting><![CDATA[package eg;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class BlogMain {
private SessionFactory _sessions;
public void configure() throws HibernateException {
_sessions = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class)
.buildSessionFactory();
}
public void exportTables() throws HibernateException {
Configuration cfg = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class);
new SchemaExport(cfg).create(true, true);
}
public Blog createBlog(String name) throws HibernateException {
Blog blog = new Blog();
blog.setName(name);
blog.setItems( new ArrayList() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public BlogItem createBlogItem(Blog blog, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setBlog(blog);
item.setDatetime( Calendar.getInstance() );
blog.getItems().add(item);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public BlogItem createBlogItem(Long blogid, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Blog blog = (Blog) session.load(Blog.class, blogid);
item.setBlog(blog);
blog.getItems().add(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public void updateBlogItem(BlogItem item, String text)
throws HibernateException {
item.setText(text);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public void updateBlogItem(Long itemid, String text)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
item.setText(text);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public List listAllBlogNamesAndItemCounts(int max)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"select blog.id, blog.name, count(blogItem) " +
"from Blog as blog " +
"left outer join blog.items as blogItem " +
"group by blog.name, blog.id " +
"order by max(blogItem.datetime)"
);
q.setMaxResults(max);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
public Blog getBlogAndAllItems(Long blogid)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
Blog blog = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"left outer join fetch blog.items " +
"where blog.id = :blogid"
);
q.setParameter("blogid", blogid);
blog = (Blog) q.uniqueResult();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public List listBlogsAndRecentItems() throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"inner join blog.items as blogItem " +
"where blogItem.datetime > :minDate"
);
Calendar cal = Calendar.getInstance();
cal.roll(Calendar.MONTH, false);
q.setCalendar("minDate", cal);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}]]></programlisting>
</sect1>
</chapter>

View File

@ -1,173 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="filters">
<title>Filtering data</title>
<para>
Hibernate3 provides an innovative new approach to handling data with "visibility" rules.
A <emphasis>Hibernate filter</emphasis> is a global, named, parameterized filter that may be
enabled or disabled for a particular Hibernate session.
</para>
<sect1 id="objectstate-filters" revision="1">
<title>Hibernate filters</title>
<para>
Hibernate3 adds the ability to pre-define filter criteria and attach those filters at both
a class and a collection level. A filter criteria is the ability to define a restriction clause
very similiar to the existing "where" attribute available on the class and various collection
elements. Except these filter conditions can be parameterized. The application can then make
the decision at runtime whether given filters should be enabled and what their parameter
values should be. Filters can be used like database views, but parameterized inside the
application.
</para>
<para>
In order to use filters, they must first be defined and then attached to the appropriate
mapping elements. To define a filter, use the <literal>&lt;filter-def/&gt;</literal> element
within a <literal>&lt;hibernate-mapping/&gt;</literal> element:
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>]]></programlisting>
<para>
Then, this filter can be attached to a class:
</para>
<programlisting><![CDATA[<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>]]></programlisting>
<para>
or, to a collection:
</para>
<programlisting><![CDATA[<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>]]></programlisting>
<para>
or, even to both (or multiples of each) at the same time.
</para>
<para>
The methods on <literal>Session</literal> are: <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, and <literal>disableFilter(String filterName)</literal>.
By default, filters are <emphasis>not</emphasis> enabled for a given session; they must be explcitly
enabled through use of the <literal>Session.enableFilter()</literal> method, which returns an
instance of the <literal>Filter</literal> interface. Using the simple filter defined above, this
would look like:
</para>
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
<para>
Note that methods on the org.hibernate.Filter interface do allow the method-chaining common to much of Hibernate.
</para>
<para>
A full example, using temporal data with an effective record date pattern:
</para>
<programlisting><![CDATA[<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
<class name="Employee" ...>
...
<many-to-one name="department" column="dept_id" class="Department"/>
<property name="effectiveStartDate" type="date" column="eff_start_dt"/>
<property name="effectiveEndDate" type="date" column="eff_end_dt"/>
...
<!--
Note that this assumes non-terminal records have an eff_end_dt set to
a max db date for simplicity-sake
-->
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>
<class name="Department" ...>
...
<set name="employees" lazy="true">
<key column="dept_id"/>
<one-to-many class="Employee"/>
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</set>
</class>]]></programlisting>
<para>
Then, in order to ensure that you always get back currently effective records, simply
enable the filter on the session prior to retrieving employee data:
</para>
<programlisting><![CDATA[Session session = ...;
session.enableFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
]]></programlisting>
<para>
In the HQL above, even though we only explicitly mentioned a salary constraint on the results,
because of the enabled filter the query will return only currently active employees who have
a salary greater than a million dollars.
</para>
<para>
Note: if you plan on using filters with outer joining (either through HQL or load fetching) be
careful of the direction of the condition expression. Its safest to set this up for left
outer joining; in general, place the parameter first followed by the column name(s) after
the operator.
</para>
<para>
After being defined a filter might be attached to multiple entities and/or
collections each with its own condition. That can be tedious when the
conditions are the same each time. Thus <literal>&lt;filter-def/&gt;</literal>
allows defining a default condition, either as an attribute or CDATA:
</para>
<programlisting><![CDATA[<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
<filter-def name="myOtherFilter">abc=xyz</filter-def>]]></programlisting>
<para>
This default condition will then be used whenever the filter is attached to something
without specifying a condition. Note that this means you can give a specific condition
as part of the attachment of the filter which overrides the default condition in that
particular case.
</para>
</sect1>
</chapter>

View File

@ -1,518 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="inheritance">
<title>Inheritance Mapping</title>
<sect1 id="inheritance-strategies" revision="3">
<title>The Three Strategies</title>
<para>
Hibernate supports the three basic inheritance mapping strategies:
</para>
<itemizedlist>
<listitem>
<para>
table per class hierarchy
</para>
</listitem>
<listitem>
<para>
table per subclass
</para>
</listitem>
<listitem>
<para>
table per concrete class
</para>
</listitem>
</itemizedlist>
<para>
In addition, Hibernate supports a fourth, slightly different kind of
polymorphism:
</para>
<itemizedlist>
<listitem>
<para>
implicit polymorphism
</para>
</listitem>
</itemizedlist>
<para>
It is possible to use different mapping strategies for different
branches of the same inheritance hierarchy, and then make use of implicit
polymorphism to achieve polymorphism across the whole hierarchy. However,
Hibernate does not support mixing <literal>&lt;subclass&gt;</literal>,
and <literal>&lt;joined-subclass&gt;</literal> and
<literal>&lt;union-subclass&gt;</literal> mappings under the same root
<literal>&lt;class&gt;</literal> element. It is possible to mix together
the table per hierarchy and table per subclass strategies, under the
the same <literal>&lt;class&gt;</literal> element, by combining the
<literal>&lt;subclass&gt;</literal> and <literal>&lt;join&gt;</literal>
elements (see below).
</para>
<para>
It is possible to define <literal>subclass</literal>, <literal>union-subclass</literal>,
and <literal>joined-subclass</literal> mappings in separate mapping documents, directly beneath
<literal>hibernate-mapping</literal>. This allows you to extend a class hierachy just by adding
a new mapping file. You must specify an <literal>extends</literal> attribute in the subclass mapping,
naming a previously mapped superclass. Note: Previously this feature made the ordering of the mapping
documents important. Since Hibernate3, the ordering of mapping files does not matter when using the
extends keyword. The ordering inside a single mapping file still needs to be defined as superclasses
before subclasses.
</para>
<programlisting><![CDATA[
<hibernate-mapping>
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</hibernate-mapping>]]></programlisting>
<sect2 id="inheritance-tableperclass" >
<title>Table per class hierarchy</title>
<para>
Suppose we have an interface <literal>Payment</literal>, with implementors
<literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
<literal>ChequePayment</literal>. The table per hierarchy mapping would
look like:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>]]></programlisting>
<para>
Exactly one table is required. There is one big limitation of this mapping
strategy: columns declared by the subclasses, such as <literal>CCTYPE</literal>,
may not have <literal>NOT NULL</literal> constraints.
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>Table per subclass</title>
<para>
A table per subclass mapping would look like:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
</class>]]></programlisting>
<para>
Four tables are required. The three subclass tables have primary
key associations to the superclass table (so the relational model
is actually a one-to-one association).
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
<title>Table per subclass, using a discriminator</title>
<para>
Note that Hibernate's implementation of table per subclass requires
no discriminator column. Other object/relational mappers use a
different implementation of table per subclass which requires a type
discriminator column in the superclass table. The approach taken by
Hibernate is much more difficult to implement but arguably more
correct from a relational point of view. If you would like to use
a discriminator column with the table per subclass strategy, you
may combine the use of <literal>&lt;subclass&gt;</literal> and
<literal>&lt;join&gt;</literal>, as follow:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
<join table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
<join table="CHEQUE_PAYMENT" fetch="select">
<key column="PAYMENT_ID"/>
...
</join>
</subclass>
</class>]]></programlisting>
<para>
The optional <literal>fetch="select"</literal> declaration tells Hibernate
not to fetch the <literal>ChequePayment</literal> subclass data using an
outer join when querying the superclass.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>Mixing table per class hierarchy with table per subclass</title>
<para>
You may even mix the table per hierarchy and table per subclass strategies
using this approach:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<join table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</join>
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>]]></programlisting>
<para>
For any of these mapping strategies, a polymorphic association to the root
<literal>Payment</literal> class is mapped using
<literal>&lt;many-to-one&gt;</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="2">
<title>Table per concrete class</title>
<para>
There are two ways we could go about mapping the table per concrete class
strategy. The first is to use <literal>&lt;union-subclass&gt;</literal>.
</para>
<programlisting><![CDATA[<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>]]></programlisting>
<para>
Three tables are involved for the subclasses. Each table defines columns for
all properties of the class, including inherited properties.
</para>
<para>
The limitation of this approach is that if a property is mapped on the
superclass, the column name must be the same on all subclass tables.
(We might relax this in a future release of Hibernate.) The identity
generator strategy is not allowed in union subclass inheritance, indeed
the primary key seed has to be shared accross all unioned subclasses
of a hierarchy.
</para>
<para>
If your superclass is abstract, map it with <literal>abstract="true"</literal>.
Of course, if it is not abstract, an additional table (defaults to
<literal>PAYMENT</literal> in the example above) is needed to hold instances
of the superclass.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Table per concrete class, using implicit polymorphism</title>
<para>
An alternative approach is to make use of implicit polymorphism:
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CREDIT_AMOUNT"/>
...
</class>
<class name="CashPayment" table="CASH_PAYMENT">
<id name="id" type="long" column="CASH_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CASH_AMOUNT"/>
...
</class>
<class name="ChequePayment" table="CHEQUE_PAYMENT">
<id name="id" type="long" column="CHEQUE_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</class>]]></programlisting>
<para>
Notice that nowhere do we mention the <literal>Payment</literal> interface
explicitly. Also notice that properties of <literal>Payment</literal> are
mapped in each of the subclasses. If you want to avoid duplication, consider
using XML entities
(e.g. <literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
in the <literal>DOCTYPE</literal> declartion and
<literal>&amp;allproperties;</literal> in the mapping).
</para>
<para>
The disadvantage of this approach is that Hibernate does not generate SQL
<literal>UNION</literal>s when performing polymorphic queries.
</para>
<para>
For this mapping strategy, a polymorphic association to <literal>Payment</literal>
is usually mapped using <literal>&lt;any&gt;</literal>.
</para>
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
<meta-value value="CREDIT" class="CreditCardPayment"/>
<meta-value value="CASH" class="CashPayment"/>
<meta-value value="CHEQUE" class="ChequePayment"/>
<column name="PAYMENT_CLASS"/>
<column name="PAYMENT_ID"/>
</any>]]></programlisting>
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>Mixing implicit polymorphism with other inheritance mappings</title>
<para>
There is one further thing to notice about this mapping. Since the subclasses
are each mapped in their own <literal>&lt;class&gt;</literal> element (and since
<literal>Payment</literal> is just an interface), each of the subclasses could
easily be part of another inheritance hierarchy! (And you can still use polymorphic
queries against the <literal>Payment</literal> interface.)
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="CREDIT_CARD" type="string"/>
<property name="amount" column="CREDIT_AMOUNT"/>
...
<subclass name="MasterCardPayment" discriminator-value="MDC"/>
<subclass name="VisaPayment" discriminator-value="VISA"/>
</class>
<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
<id name="id" type="long" column="TXN_ID">
<generator class="native"/>
</id>
...
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CASH_AMOUNT"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</joined-subclass>
</class>]]></programlisting>
<para>
Once again, we don't mention <literal>Payment</literal> explicitly. If we
execute a query against the <literal>Payment</literal> interface - for
example, <literal>from Payment</literal> - Hibernate
automatically returns instances of <literal>CreditCardPayment</literal>
(and its subclasses, since they also implement <literal>Payment</literal>),
<literal>CashPayment</literal> and <literal>ChequePayment</literal> but
not instances of <literal>NonelectronicTransaction</literal>.
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>Limitations</title>
<para>
There are certain limitations to the "implicit polymorphism" approach to
the table per concrete-class mapping strategy. There are somewhat less
restrictive limitations to <literal>&lt;union-subclass&gt;</literal>
mappings.
</para>
<para>
The following table shows the limitations of table per concrete-class
mappings, and of implicit polymorphism, in Hibernate.
</para>
<table frame="topbot">
<title>Features of inheritance mappings</title>
<tgroup cols='8' align='left' colsep='1' rowsep='1'>
<colspec colname='c1' colwidth="1*"/>
<colspec colname='c2' colwidth="1*"/>
<colspec colname='c3' colwidth="1*"/>
<colspec colname='c4' colwidth="1*"/>
<colspec colname='c5' colwidth="1*"/>
<colspec colname='c6' colwidth="1*"/>
<colspec colname='c7' colwidth="1*"/>
<colspec colname='c8' colwidth="1*"/>
<thead>
<row>
<entry>Inheritance strategy</entry>
<entry>Polymorphic many-to-one</entry>
<entry>Polymorphic one-to-one</entry>
<entry>Polymorphic one-to-many</entry>
<entry>Polymorphic many-to-many</entry>
<entry>Polymorphic <literal>load()/get()</literal></entry>
<entry>Polymorphic queries</entry>
<entry>Polymorphic joins</entry>
<entry>Outer join fetching</entry>
</row>
</thead>
<tbody>
<row>
<entry>table per class-hierarchy</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supported</emphasis></entry>
</row>
<row>
<entry>table per subclass</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supported</emphasis></entry>
</row>
<row>
<entry>table per concrete-class (union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (for <literal>inverse="true"</literal> only)</entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supported</emphasis></entry>
</row>
<row>
<entry>table per concrete class (implicit polymorphism)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><emphasis>not supported</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>

View File

@ -1,561 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="persistent-classes" revision="2">
<title>Persistent Classes</title>
<para>
Persistent classes are classes in an application that implement the entities
of the business problem (e.g. Customer and Order in an E-commerce application).
Not all instances of a persistent class are considered to be in the persistent
state - an instance may instead be transient or detached.
</para>
<para>
Hibernate works best if these classes follow some simple rules, also known
as the Plain Old Java Object (POJO) programming model. However, none of these
rules are hard requirements. Indeed, Hibernate3 assumes very little about
the nature of your persistent objects. You may express a domain model in other
ways: using trees of <literal>Map</literal> instances, for example.
</para>
<sect1 id="persistent-classes-pojo">
<title>A simple POJO example</title>
<para>
Most Java applications require a persistent class representing felines.
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private Date birthdate;
private Color color;
private char sex;
private float weight;
private int litterId;
private Cat mother;
private Set kittens = new HashSet();
private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}
void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
void setSex(char sex) {
this.sex=sex;
}
public char getSex() {
return sex;
}
void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}
void setMother(Cat mother) {
this.mother = mother;
}
public Cat getMother() {
return mother;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}]]></programlisting>
<para>
There are four main rules to follow here:
</para>
<sect2 id="persistent-classes-pojo-constructor" revision="1">
<title>Implement a no-argument constructor</title>
<para>
<literal>Cat</literal> has a no-argument constructor. All persistent classes must
have a default constructor (which may be non-public) so that Hibernate can instantiate
them using <literal>Constructor.newInstance()</literal>. We strongly recommend having a
default constructor with at least <emphasis>package</emphasis> visibility for runtime proxy
generation in Hibernate.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-identifier" revision="2">
<title>Provide an identifier property (optional)</title>
<para>
<literal>Cat</literal> has a property called <literal>id</literal>. This property
maps to the primary key column of a database table. The property might have been called
anything, and its type might have been any primitive type, any primitive "wrapper"
type, <literal>java.lang.String</literal> or <literal>java.util.Date</literal>. (If
your legacy database table has composite keys, you can even use a user-defined class
with properties of these types - see the section on composite identifiers later.)
</para>
<para>
The identifier property is strictly optional. You can leave them off and let Hibernate
keep track of object identifiers internally. We do not recommend this, however.
</para>
<para>
In fact, some functionality is available only to classes which declare an
identifier property:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Transitive reattachment for detached objects (cascade update or cascade
merge) - see <xref linkend="objectstate-transitive"/>
</para>
</listitem>
<listitem>
<para>
<literal>Session.saveOrUpdate()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>Session.merge()</literal>
</para>
</listitem>
</itemizedlist>
<para>
We recommend you declare consistently-named identifier properties on persistent
classes. We further recommend that you use a nullable (ie. non-primitive) type.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-final">
<title>Prefer non-final classes (optional)</title>
<para>
A central feature of Hibernate, <emphasis>proxies</emphasis>, depends upon the
persistent class being either non-final, or the implementation of an interface
that declares all public methods.
</para>
<para>
You can persist <literal>final</literal> classes that do not implement an interface
with Hibernate, but you won't be able to use proxies for lazy association fetching -
which will limit your options for performance tuning.
</para>
<para>
You should also avoid declaring <literal>public final</literal> methods on the
non-final classes. If you want to use a class with a <literal>public final</literal>
method, you must explicitly disable proxying by setting <literal>lazy="false"</literal>.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-accessors" revision="2">
<title>Declare accessors and mutators for persistent fields (optional)</title>
<para>
<literal>Cat</literal> declares accessor methods for all its persistent fields.
Many other ORM tools directly persist instance variables. We believe it is
better to provide an indirection between the relational schema and internal
data structures of the class. By default, Hibernate persists JavaBeans style
properties, and recognizes method names of the form <literal>getFoo</literal>,
<literal>isFoo</literal> and <literal>setFoo</literal>. You may switch to direct
field access for particular properties, if needed.
</para>
<para>
Properties need <emphasis>not</emphasis> be declared public - Hibernate can
persist a property with a default, <literal>protected</literal> or
<literal>private</literal> get / set pair.
</para>
</sect2>
</sect1>
<sect1 id="persistent-classes-inheritance">
<title>Implementing inheritance</title>
<para>
A subclass must also observe the first and second rules. It inherits its
identifier property from the superclass, <literal>Cat</literal>.
</para>
<programlisting><![CDATA[package eg;
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
protected void setName(String name) {
this.name=name;
}
}]]></programlisting>
</sect1>
<sect1 id="persistent-classes-equalshashcode" revision="1">
<title>Implementing <literal>equals()</literal> and <literal>hashCode()</literal></title>
<para>
You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
methods if you
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
intend to put instances of persistent classes in a <literal>Set</literal>
(the recommended way to represent many-valued associations)
<emphasis>and</emphasis>
</para>
</listitem>
<listitem>
<para>
intend to use reattachment of detached instances
</para>
</listitem>
</itemizedlist>
<para>
Hibernate guarantees equivalence of persistent identity (database row) and Java identity
only inside a particular session scope. So as soon as we mix instances retrieved in
different sessions, we must implement <literal>equals()</literal> and
<literal>hashCode()</literal> if we wish to have meaningful semantics for
<literal>Set</literal>s.
</para>
<para>
The most obvious way is to implement <literal>equals()</literal>/<literal>hashCode()</literal>
by comparing the identifier value of both objects. If the value is the same, both must
be the same database row, they are therefore equal (if both are added to a <literal>Set</literal>,
we will only have one element in the <literal>Set</literal>). Unfortunately, we can't use that
approach with generated identifiers! Hibernate will only assign identifier values to objects
that are persistent, a newly created instance will not have any identifier value! Furthermore,
if an instance is unsaved and currently in a <literal>Set</literal>, saving it will assign
an identifier value to the object. If <literal>equals()</literal> and <literal>hashCode()</literal>
are based on the identifier value, the hash code would change, breaking the contract of the
<literal>Set</literal>. See the Hibernate website for a full discussion of this problem. Note
that this is not a Hibernate issue, but normal Java semantics of object identity and equality.
</para>
<para>
We recommend implementing <literal>equals()</literal> and <literal>hashCode()</literal>
using <emphasis>Business key equality</emphasis>. Business key equality means that the
<literal>equals()</literal> method compares only the properties that form the business
key, a key that would identify our instance in the real world (a
<emphasis>natural</emphasis> candidate key):
</para>
<programlisting><![CDATA[public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}]]></programlisting>
<para>
Note that a business key does not have to be as solid as a database
primary key candidate (see <xref linkend="transactions-basics-identity"/>).
Immutable or unique properties are usually good
candidates for a business key.
</para>
</sect1>
<sect1 id="persistent-classes-dynamicmodels">
<title>Dynamic models</title>
<para>
<emphasis>Note that the following features are currently considered
experimental and may change in the near future.</emphasis>
</para>
<para>
Persistent entities don't necessarily have to be represented as POJO classes
or as JavaBean objects at runtime. Hibernate also supports dynamic models
(using <literal>Map</literal>s of <literal>Map</literal>s at runtime) and the
representation of entities as DOM4J trees. With this approach, you don't
write persistent classes, only mapping files.
</para>
<para>
By default, Hibernate works in normal POJO mode. You may set a default entity
representation mode for a particular <literal>SessionFactory</literal> using the
<literal>default_entity_mode</literal> configuration option (see
<xref linkend="configuration-optional-properties"/>.
</para>
<para>
The following examples demonstrates the representation using <literal>Map</literal>s.
First, in the mapping file, an <literal>entity-name</literal> has to be declared
instead of (or in addition to) a class name:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Note that even though associations are declared using target class names,
the target type of an associations may also be a dynamic entity instead
of a POJO.
</para>
<para>
After setting the default entity mode to <literal>dynamic-map</literal>
for the <literal>SessionFactory</literal>, we can at runtime work with
<literal>Map</literal>s of <literal>Map</literal>s:
</para>
<programlisting><![CDATA[Session s = openSession();
Transaction tx = s.beginTransaction();
Session s = openSession();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();]]></programlisting>
<para>
The advantages of a dynamic mapping are quick turnaround time for prototyping
without the need for entity class implementation. However, you lose compile-time
type checking and will very likely deal with many exceptions at runtime. Thanks
to the Hibernate mapping, the database schema can easily be normalized and sound,
allowing to add a proper domain model implementation on top later on.
</para>
<para>
Entity representation modes can also be set on a per <literal>Session</literal>
basis:
</para>
<programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
]]></programlisting>
<para>
Please note that the call to <literal>getSession()</literal> using an
<literal>EntityMode</literal> is on the <literal>Session</literal> API, not the
<literal>SessionFactory</literal>. That way, the new <literal>Session</literal>
shares the underlying JDBC connection, transaction, and other context
information. This means you don't have tocall <literal>flush()</literal>
and <literal>close()</literal> on the secondary <literal>Session</literal>, and
also leave the transaction and connection handling to the primary unit of work.
</para>
<para>
More information about the XML representation capabilities can be found
in <xref linkend="xml"/>.
</para>
</sect1>
<sect1 id="persistent-classes-tuplizers" revision="1">
<title>Tuplizers</title>
<para>
<literal>org.hibernate.tuple.Tuplizer</literal>, and its sub-interfaces, are responsible
for managing a particular representation of a piece of data, given that representation's
<literal>org.hibernate.EntityMode</literal>. If a given piece of data is thought of as
a data structure, then a tuplizer is the thing which knows how to create such a data structure
and how to extract values from and inject values into such a data structure. For example,
for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its
constructor and how to access the POJO properties using the defined property accessors.
There are two high-level types of Tuplizers, represented by the
<literal>org.hibernate.tuple.entity.EntityTuplizer</literal> and <literal>org.hibernate.tuple.component.ComponentTuplizer</literal>
interfaces. <literal>EntityTuplizer</literal>s are responsible for managing the above mentioned
contracts in regards to entities, while <literal>ComponentTuplizer</literal>s do the same for
components.
</para>
<para>
Users may also plug in their own tuplizers. Perhaps you require that a <literal>java.util.Map</literal>
implementation other than <literal>java.util.HashMap</literal> be used while in the
dynamic-map entity-mode; or perhaps you need to define a different proxy generation strategy
than the one used by default. Both would be achieved by defining a custom tuplizer
implementation. Tuplizers definitions are attached to the entity or component mapping they
are meant to manage. Going back to the example of our customer entity:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class entity-name="Customer">
<!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<!-- other properties -->
...
</class>
</hibernate-mapping>
public class CustomMapTuplizerImpl
extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
// override the buildInstantiator() method to plug in our custom map...
protected final Instantiator buildInstantiator(
org.hibernate.mapping.PersistentClass mappingInfo) {
return new CustomMapInstantiator( mappingInfo );
}
private static final class CustomMapInstantiator
extends org.hibernate.tuple.DynamicMapInstantitor {
// override the generateMap() method to return our custom map...
protected final Map generateMap() {
return new CustomMap();
}
}
}]]></programlisting>
</sect1>
<sect1 id="persistent-classes-extensions">
<title>Extentsions</title>
<para>
TODO: Document user-extension framework in the property and proxy packages
</para>
</sect1>
</chapter>

View File

@ -1,132 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<preface id="preface">
<title>Preface</title>
<para>
Working with object-oriented software and a relational database can be cumbersome
and time consuming in today's enterprise environments. Hibernate is an object/relational
mapping tool for Java environments. The term object/relational mapping (ORM) refers to
the technique of mapping a data representation from an object model to a relational
data model with a SQL-based schema.
</para>
<para>
Hibernate not only takes care of the mapping from Java classes to
database tables (and from Java data types to SQL data types), but also provides data
query and retrieval facilities and can significantly reduce development time otherwise
spent with manual data handling in SQL and JDBC.
</para>
<para>
Hibernates goal is to relieve the developer from 95 percent of common data persistence
related programming tasks. Hibernate may not be the best solution for data-centric
applications that only use stored-procedures to implement the business logic in the
database, it is most useful with object-oriented domain models and business logic in
the Java-based middle-tier. However, Hibernate can certainly help you to remove or
encapsulate vendor-specific SQL code and will help with the common task of result set
translation from a tabular representation to a graph of objects.
</para>
<para>
If you are new to Hibernate and Object/Relational Mapping or even Java,
please follow these steps:
</para>
<orderedlist>
<listitem>
<para>
Read <xref linkend="tutorial"/> for a tutorial with step-by-step
instructions. The source code for the tutorial is included in the
distribution in the <literal>doc/reference/tutorial/</literal>
directory.
</para>
</listitem>
<listitem>
<para>
Read <xref linkend="architecture"/> to understand the environments where
Hibernate can be used.
</para>
</listitem>
<listitem>
<para>
Have a look at the <literal>eg/</literal> directory in the Hibernate
distribution, it contains a simple standalone application. Copy your
JDBC driver to the <literal>lib/</literal> directory and edit
<literal>etc/hibernate.properties</literal>, specifying correct values for
your database. From a command prompt in the distribution directory,
type <literal>ant eg</literal> (using Ant), or under Windows, type
<literal>build eg</literal>.
</para>
</listitem>
<listitem>
<para>
Use this reference documentation as your primary source of information.
Consider reading <emphasis>Java Persistence with Hibernate</emphasis>
(http://www.manning.com/bauer2) if you need more help with application
design or if you prefer a step-by-step tutorial. Also visit
http://caveatemptor.hibernate.org and download the example application
for Java Persistence with Hibernate.
</para>
</listitem>
<listitem>
<para>
FAQs are answered on the Hibernate website.
</para>
</listitem>
<listitem>
<para>
Third party demos, examples, and tutorials are linked on the Hibernate
website.
</para>
</listitem>
<listitem>
<para>
The Community Area on the Hibernate website is a good resource for
design patterns and various integration solutions (Tomcat, JBoss AS,
Struts, EJB, etc.).
</para>
</listitem>
</orderedlist>
<para>
If you have questions, use the user forum linked on the Hibernate website. We also
provide a JIRA issue trackings system for bug reports and feature requests. If you
are interested in the development of Hibernate, join the developer mailing list. If
you are interested in translating this documentation into your language, contact us
on the developer mailing list.
</para>
<para>
Commercial development support, production support, and training for Hibernate is
available through JBoss Inc. (see http://www.hibernate.org/SupportTraining/).
Hibernate is a Professional Open Source project and a critical component of the
JBoss Enterprise Middleware System (JEMS) suite of products.
</para>
</preface>

View File

@ -1,463 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="querycriteria">
<title>Criteria Queries</title>
<para>
Hibernate features an intuitive, extensible criteria query API.
</para>
<sect1 id="querycriteria-creating">
<title>Creating a <literal>Criteria</literal> instance</title>
<para>
The interface <literal>org.hibernate.Criteria</literal> represents a query against
a particular persistent class. The <literal>Session</literal> is a factory for
<literal>Criteria</literal> instances.
</para>
<programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-narrowing">
<title>Narrowing the result set</title>
<para>
An individual query criterion is an instance of the interface
<literal>org.hibernate.criterion.Criterion</literal>. The class
<literal>org.hibernate.criterion.Restrictions</literal> defines
factory methods for obtaining certain built-in
<literal>Criterion</literal> types.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();]]></programlisting>
<para>
Restrictions may be grouped logically.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
) )
.list();]]></programlisting>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
.add( Restrictions.disjunction()
.add( Restrictions.isNull("age") )
.add( Restrictions.eq("age", new Integer(0) ) )
.add( Restrictions.eq("age", new Integer(1) ) )
.add( Restrictions.eq("age", new Integer(2) ) )
) )
.list();]]></programlisting>
<para>
There are quite a range of built-in criterion types (<literal>Restrictions</literal>
subclasses), but one that is especially useful lets you specify SQL directly.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
.list();]]></programlisting>
<para>
The <literal>{alias}</literal> placeholder with be replaced by the row alias
of the queried entity.
</para>
<para>
An alternative approach to obtaining a criterion is to get it from a
<literal>Property</literal> instance. You can create a <literal>Property</literal>
by calling <literal>Property.forName()</literal>.
</para>
<programlisting><![CDATA[
Property age = Property.forName("age");
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.disjunction()
.add( age.isNull() )
.add( age.eq( new Integer(0) ) )
.add( age.eq( new Integer(1) ) )
.add( age.eq( new Integer(2) ) )
) )
.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-ordering">
<title>Ordering the results</title>
<para>
You may order the results using <literal>org.hibernate.criterion.Order</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();]]></programlisting>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Property.forName("name").like("F%") )
.addOrder( Property.forName("name").asc() )
.addOrder( Property.forName("age").desc() )
.setMaxResults(50)
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-associations" revision="2">
<title>Associations</title>
<para>
You may easily specify constraints upon related entities by navigating
associations using <literal>createCriteria()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%") )
.createCriteria("kittens")
.add( Restrictions.like("name", "F%") )
.list();]]></programlisting>
<para>
note that the second <literal>createCriteria()</literal> returns a new
instance of <literal>Criteria</literal>, which refers to the elements of
the <literal>kittens</literal> collection.
</para>
<para>
The following, alternate form is useful in certain circumstances.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createAlias("kittens", "kt")
.createAlias("mate", "mt")
.add( Restrictions.eqProperty("kt.name", "mt.name") )
.list();]]></programlisting>
<para>
(<literal>createAlias()</literal> does not create a new instance of
<literal>Criteria</literal>.)
</para>
<para>
Note that the kittens collections held by the <literal>Cat</literal> instances
returned by the previous two queries are <emphasis>not</emphasis> pre-filtered
by the criteria! If you wish to retrieve just the kittens that match the
criteria, you must use a <literal>ResultTransformer</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
.list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
Cat kitten = (Cat) map.get("kt");
}]]></programlisting>
</sect1>
<sect1 id="querycriteria-dynamicfetching" revision="1">
<title>Dynamic association fetching</title>
<para>
You may specify association fetching semantics at runtime using
<literal>setFetchMode()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();]]></programlisting>
<para>
This query will fetch both <literal>mate</literal> and <literal>kittens</literal>
by outer join. See <xref linkend="performance-fetching"/> for more information.
</para>
</sect1>
<sect1 id="querycriteria-examples">
<title>Example queries</title>
<para>
The class <literal>org.hibernate.criterion.Example</literal> allows
you to construct a query criterion from a given instance.
</para>
<programlisting><![CDATA[Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();]]></programlisting>
<para>
Version properties, identifiers and associations are ignored. By default,
null valued properties are excluded.
</para>
<para>
You can adjust how the <literal>Example</literal> is applied.
</para>
<programlisting><![CDATA[Example example = Example.create(cat)
.excludeZeroes() //exclude zero valued properties
.excludeProperty("color") //exclude the property named "color"
.ignoreCase() //perform case insensitive string comparisons
.enableLike(); //use like for string comparisons
List results = session.createCriteria(Cat.class)
.add(example)
.list();]]></programlisting>
<para>
You can even use examples to place criteria upon associated objects.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.createCriteria("mate")
.add( Example.create( cat.getMate() ) )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-projection">
<title>Projections, aggregation and grouping</title>
<para>
The class <literal>org.hibernate.criterion.Projections</literal> is a
factory for <literal>Projection</literal> instances. We apply a
projection to a query by calling <literal>setProjection()</literal>.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.rowCount() )
.add( Restrictions.eq("color", Color.BLACK) )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount() )
.add( Projections.avg("weight") )
.add( Projections.max("weight") )
.add( Projections.groupProperty("color") )
)
.list();]]></programlisting>
<para>
There is no explicit "group by" necessary in a criteria query. Certain
projection types are defined to be <emphasis>grouping projections</emphasis>,
which also appear in the SQL <literal>group by</literal> clause.
</para>
<para>
An alias may optionally be assigned to a projection, so that the projected value
may be referred to in restrictions or orderings. Here are two different ways to
do this:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
.addOrder( Order.asc("colr") )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.groupProperty("color").as("colr") )
.addOrder( Order.asc("colr") )
.list();]]></programlisting>
<para>
The <literal>alias()</literal> and <literal>as()</literal> methods simply wrap a
projection instance in another, aliased, instance of <literal>Projection</literal>.
As a shortcut, you can assign an alias when you add the projection to a
projection list:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount(), "catCountByColor" )
.add( Projections.avg("weight"), "avgWeight" )
.add( Projections.max("weight"), "maxWeight" )
.add( Projections.groupProperty("color"), "color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
.createAlias("kittens", "kit")
.setProjection( Projections.projectionList()
.add( Projections.property("cat.name"), "catName" )
.add( Projections.property("kit.name"), "kitName" )
)
.addOrder( Order.asc("catName") )
.addOrder( Order.asc("kitName") )
.list();]]></programlisting>
<para>
You can also use <literal>Property.forName()</literal> to express projections:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Property.forName("name") )
.add( Property.forName("color").eq(Color.BLACK) )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount().as("catCountByColor") )
.add( Property.forName("weight").avg().as("avgWeight") )
.add( Property.forName("weight").max().as("maxWeight") )
.add( Property.forName("color").group().as("color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-detachedqueries">
<title>Detached queries and subqueries</title>
<para>
The <literal>DetachedCriteria</literal> class lets you create a query outside the scope
of a session, and then later execute it using some arbitrary <literal>Session</literal>.
</para>
<programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
.add( Property.forName("sex").eq('F') );
Session session = ....;
Transaction txn = session.beginTransaction();
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
txn.commit();
session.close();]]></programlisting>
<para>
A <literal>DetachedCriteria</literal> may also be used to express a subquery. Criterion
instances involving subqueries may be obtained via <literal>Subqueries</literal> or
<literal>Property</literal>.
</para>
<programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
.add( Property.forName("weight").gt(avgWeight) )
.list();]]></programlisting>
<programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
.add( Subqueries.geAll("weight", weights) )
.list();]]></programlisting>
<para>
Even correlated subqueries are possible:
</para>
<programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
.setProjection( Property.forName("weight").avg() )
.add( Property.forName("cat2.sex").eqProperty("cat.sex") );
session.createCriteria(Cat.class, "cat")
.add( Property.forName("weight").gt(avgWeightForSex) )
.list();]]></programlisting>
</sect1>
<!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
user objects - similar to setResultClass in JDO2. General use of ResultTransformer
could also be explained. -->
<sect1 id="query-criteria-naturalid">
<title>Queries by natural identifier</title>
<para>
For most queries, including criteria queries, the query cache is not very efficient,
because query cache invalidation occurs too frequently. However, there is one special
kind of query where we can optimize the cache invalidation algorithm: lookups by a
constant natural key. In some applications, this kind of query occurs frequently.
The criteria API provides special provision for this use case.
</para>
<para>
First, you should map the natural key of your entity using
<literal>&lt;natural-id&gt;</literal>, and enable use of the second-level cache.
</para>
<programlisting><![CDATA[<class name="User">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<natural-id>
<property name="name"/>
<property name="org"/>
</natural-id>
<property name="password"/>
</class>]]></programlisting>
<para>
Note that this functionality is not intended for use with entities with
<emphasis>mutable</emphasis> natural keys.
</para>
<para>
Next, enable the Hibernate query cache.
</para>
<para>
Now, <literal>Restrictions.naturalId()</literal> allows us to make use of
the more efficient cache algorithm.
</para>
<programlisting><![CDATA[session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")
.set("org", "hb")
).setCacheable(true)
.uniqueResult();]]></programlisting>
</sect1>
</chapter>

View File

@ -1,784 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="querysql" revision="2">
<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 (including stored
procedures) for all create, update, delete, and load operations.</para>
<sect1 id="querysql-creating" revision="4">
<title>Using a <literal>SQLQuery</literal></title>
<para>Execution of native SQL queries is controlled via the
<literal>SQLQuery</literal> interface, which is obtained by calling
<literal>Session.createSQLQuery()</literal>. The following describes how
to use this API for querying.</para>
<sect2>
<title>Scalar queries</title>
<para>The most basic SQL query is to get a list of scalars
(values).</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
]]></programlisting>
<para>These will both return a List of Object arrays (Object[]) with
scalar values for each column in the CATS table. Hibernate will use
ResultSetMetadata to deduce the actual order and types of the returned
scalar values.</para>
<para>To avoid the overhead of using
<literal>ResultSetMetadata</literal> or simply to be more explicit in
what is returned one can use <literal>addScalar()</literal>.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>the columns and types to return</para>
</listitem>
</itemizedlist>
<para>This will still return Object arrays, but now it will not use
<literal>ResultSetMetadata</literal> but will instead explicitly get the
ID, NAME and BIRTHDATE column as respectively a Long, String and a Short
from the underlying resultset. This also means that only these three
columns will be returned, even though the query is using
<literal>*</literal> and could return more than the three listed
columns.</para>
<para>It is possible to leave out the type information for all or some
of the scalars.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME")
.addScalar("BIRTHDATE")
]]></programlisting>
<para>This is essentially the same query as before, but now
<literal>ResultSetMetaData</literal> is used to decide the type of NAME
and BIRTHDATE where as the type of ID is explicitly specified.</para>
<para>How the java.sql.Types returned from ResultSetMetaData is mapped
to Hibernate types is controlled by the Dialect. If a specific type is
not mapped or does not result in the expected type it is possible to
customize it via calls to <literal>registerHibernateType</literal> in
the Dialect.</para>
</sect2>
<sect2>
<title>Entity queries</title>
<para>The above queries were all about returning scalar values,
basically returning the "raw" values from the resultset. The following
shows how to get entity objects from a native sql query via
<literal>addEntity()</literal>.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>the entity returned by the query</para>
</listitem>
</itemizedlist>
<para>Assuming that Cat is mapped as a class with the columns ID, NAME
and BIRTHDATE the above queries will both return a List where each
element is a Cat entity.</para>
<para>If the entity is mapped with a <literal>many-to-one</literal> to
another entity it is required to also return this when performing the
native query, otherwise a database specific "column not found" error
will occur. The additional columns will automatically be returned when
using the * notation, but we prefer to be explicit as in the following
example for a <literal>many-to-one</literal> to a
<literal>Dog</literal>:</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
]]></programlisting>
<para>This will allow cat.getDog() to function properly.</para>
</sect2>
<sect2>
<title>Handling associations and collections</title>
<para>It is possible to eagerly join in the <literal>Dog</literal> to
avoid the possible extra roundtrip for initializing the proxy. This is
done via the <literal>addJoin()</literal> method, which allows you to
join in an association or collection.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dog");
]]></programlisting>
<para>In this example the returned <literal>Cat</literal>'s will have
their <literal>dog</literal> property fully initialized without any
extra roundtrip to the database. Notice that we added a alias name
("cat") to be able to specify the target property path of the join. It
is possible to do the same eager joining for collections, e.g. if the
<literal>Cat</literal> had a one-to-many to <literal>Dog</literal>
instead.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");
]]></programlisting>
<para>
At this stage we are reaching the limits of what is possible with native queries without starting to
enhance the sql queries to make them usable in Hibernate; the problems starts to arise when returning
multiple entities of the same type or when the default alias/column names are not enough.
</para>
</sect2>
<sect2>
<title>Returning multiple entities</title>
<para>Until now the result set column names are assumed to be the same
as the column names specified in the mapping document. This can be
problematic for SQL queries which join multiple tables, since the same
column names may appear in more than one table.</para>
<para>Column alias injection is needed in the following query (which
most likely will fail):</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
]]></programlisting>
<para>The intention for this query is to return two Cat instances per
row, a cat and its mother. This will fail since there is a conflict of
names since they are mapped to the same column names and on some
databases the returned column aliases will most likely be on the form
"c.ID", "c.NAME", etc. which are not equal to the columns specified in
the mappings ("ID" and "NAME").</para>
<para>The following form is not vulnerable to column name
duplication:</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>the SQL query string, with placeholders for Hibernate to
inject column aliases</para>
</listitem>
<listitem>
<para>the entities returned by the query</para>
</listitem>
</itemizedlist>
<para>The {cat.*} and {mother.*} notation used above is a shorthand for
"all properties". Alternatively, you may list the columns explicitly, but
even in this case we 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
Cats and their mothers from a different table (cat_log) 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 ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
]]></programlisting>
<sect3 id="querysql-aliasreferences" revision="2">
<title>Alias and property references</title>
<para>For most cases the above alias injection is needed, but for
queries relating to more complex mappings like composite properties,
inheritance discriminators, collections etc. there are some specific
aliases to use to allow Hibernate to inject the proper aliases.</para>
<para>The following table shows the different possibilities of using
the alias injection. Note: the alias names in the result are examples,
each alias will have a unique and probably different name when
used.</para>
<table frame="topbot" id="aliasinjection-summary">
<title>Alias injection names</title>
<tgroup cols="3">
<colspec colwidth="1*" />
<colspec colwidth="1*" />
<colspec colwidth="2.5*" />
<thead>
<row>
<entry>Description</entry>
<entry>Syntax</entry>
<entry>Example</entry>
</row>
</thead>
<tbody>
<row>
<entry>A simple property</entry>
<entry><literal>{[aliasname].[propertyname]</literal></entry>
<entry><literal>A_NAME as {item.name}</literal></entry>
</row>
<row>
<entry>A composite property</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
<entry><literal>CURRENCY as {item.amount.currency}, VALUE as
{item.amount.value}</literal></entry>
</row>
<row>
<entry>Discriminator of an entity</entry>
<entry><literal>{[aliasname].class}</literal></entry>
<entry><literal>DISC as {item.class}</literal></entry>
</row>
<row>
<entry>All properties of an entity</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{item.*}</literal></entry>
</row>
<row>
<entry>A collection key</entry>
<entry><literal>{[aliasname].key}</literal></entry>
<entry><literal>ORGID as {coll.key}</literal></entry>
</row>
<row>
<entry>The id of an collection</entry>
<entry><literal>{[aliasname].id}</literal></entry>
<entry><literal>EMPID as {coll.id}</literal></entry>
</row>
<row>
<entry>The element of an collection</entry>
<entry><literal>{[aliasname].element}</literal></entry>
<entry><literal>XID as {coll.element}</literal></entry>
</row>
<row>
<entry>roperty of the element in the collection</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
<entry><literal>NAME as {coll.element.name}</literal></entry>
</row>
<row>
<entry>All properties of the element in the collection</entry>
<entry><literal>{[aliasname].element.*}</literal></entry>
<entry><literal>{coll.element.*}</literal></entry>
</row>
<row>
<entry>All properties of the the collection</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{coll.*}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
</sect2>
<sect2>
<title>Returning non-managed entities</title>
<para>It is possible to apply a ResultTransformer to native sql queries. Allowing it to e.g. return non-managed entities.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>a result transformer</para>
</listitem>
</itemizedlist>
<para>
The above query will return a list of <literal>CatDTO</literal> which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding
properties or fields.
</para>
</sect2>
<sect2>
<title>Handling inheritance</title>
<para>Native sql queries which query for entities that is mapped as part
of an inheritance must include all properties for the baseclass and all
it subclasses.</para>
</sect2>
<sect2>
<title>Parameters</title>
<para>Native sql queries support positional as well as named
parameters:</para>
<programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
</sect2>
</sect1>
<sect1 id="querysql-namedqueries" revision="3">
<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="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>]]></programlisting>
<programlisting><![CDATA[List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();]]></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.</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>A named SQL query may return a scalar value. You must declare 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>You can externalize the resultset mapping informations in a
<literal>&lt;resultset&gt;</literal> element to either reuse them across
several named queries or through the
<literal>setResultSetMapping()</literal> API.</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>You can alternatively use the resultset mapping information in your
hbm files directly in java code.</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();]]></programlisting>
<sect2 id="propertyresults">
<title>Using return-property to explicitly specify column/alias
names</title>
<para>With <literal>&lt;return-property&gt;</literal> you can explicitly
tell Hibernate what column aliases to use, instead of using the
<literal>{}</literal>-syntax to let Hibernate inject its own
aliases.</para>
<programlisting><![CDATA[<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>
]]></programlisting>
<para><literal>&lt;return-property&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.</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>Notice that in this example we used
<literal>&lt;return-property&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
<literal>&lt;return-discriminator&gt;</literal> to specify the
discriminator column.</para>
</sect2>
<sect2 id="sp_query" revision="1">
<title>Using stored procedures for querying</title>
<para>Hibernate 3 introduces 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:</para>
<programlisting><![CDATA[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;]]></programlisting>
<para>To use this query in Hibernate you need to map it via a named
query.</para>
<programlisting><![CDATA[<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>]]></programlisting>
<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>
<sect3 id="querysql-limits-storedprocedures" revision="1">
<title>Rules/limitations for using stored procedures</title>
<para>To use stored procedures with Hibernate the procedures/functions
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 for each database, since database vendors have
different stored procedure semantics/syntax.</para>
<para>Stored procedure queries can't be paged with
<literal>setFirstResult()/setMaxResults()</literal>.</para>
<para>Recommended call form is standard SQL92: <literal>{ ? = call
functionName(&lt;parameters&gt;) }</literal> or <literal>{ ? = call
procedureName(&lt;parameters&gt;}</literal>. Native call syntax is not
supported.</para>
<para>For Oracle the following rules apply:</para>
<itemizedlist spacing="compact">
<listitem>
<para>A function must return a result set. The first parameter of
a procedure must be an <literal>OUT</literal> that returns a
result set. This is done by using a
<literal>SYS_REFCURSOR</literal> type in Oracle 9 or 10. In Oracle
you need to define a <literal>REF CURSOR</literal> type, see
Oracle literature.</para>
</listitem>
</itemizedlist>
<para>For Sybase or MS SQL server the following rules apply:</para>
<itemizedlist spacing="compact">
<listitem>
<para>The procedure must return a result set. Note that since
these servers can/will 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.</para>
</listitem>
<listitem>
<para>If you can enable <literal>SET NOCOUNT ON</literal> in your
procedure it will probably be more efficient, but this is not a
requirement.</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</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. This will of course reduce the portability of
your mapping if you use database specific SQL.</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 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.hibernate.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 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="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.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>This even works with stored procedures.</para>
<para>You may even define a query for collection loading:</para>
<programlisting><![CDATA[<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>]]></programlisting>
<programlisting><![CDATA[<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>]]></programlisting>
<para>You could even define an entity loader that loads a collection by
join fetching:</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>]]></programlisting>
</sect1>
</chapter>

View File

@ -1,631 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="toolsetguide" revision="2">
<title>Toolset Guide</title>
<para>
Roundtrip engineering with Hibernate is possible using a set of Eclipse plugins,
commandline tools, as well as Ant tasks.
</para>
<para>
The <emphasis>Hibernate Tools</emphasis> currently include plugins for the Eclipse
IDE as well as Ant tasks for reverse engineering of existing databases:
</para>
<itemizedlist>
<listitem><para>
<emphasis>Mapping Editor:</emphasis> An editor for Hibernate XML mapping files,
supporting auto-completion and syntax highlighting. It also supports semantic
auto-completion for class names and property/field names, making it much more versatile than a normal XML editor.
</para></listitem>
<listitem><para>
<emphasis>Console:</emphasis> The console is a new view in Eclipse. In addition to
a tree overview of your console configurations, you also get an interactive view
of your persistent classes and their relationships. The console allows you to
execute HQL queries against your database and browse the result directly in
Eclipse.
</para></listitem>
<listitem><para>
<emphasis>Development Wizards:</emphasis> Several wizards are provided with the
Hibernate Eclipse tools; you can use a wizard to quickly generate Hibernate configuration
(cfg.xml) files, or you may even completely reverse engineer an existing database schema
into POJO source files and Hibernate mapping files. The reverse engineering wizard
supports customizable templates.
</para></listitem>
<listitem><para>
<emphasis>Ant Tasks:</emphasis>
</para></listitem>
</itemizedlist>
<para>
Please refer to the <emphasis>Hibernate Tools</emphasis> package and it's documentation
for more information.
</para>
<para>
However, the Hibernate main package comes bundled with an integrated tool (it can even
be used from "inside" Hibernate on-the-fly): <emphasis>SchemaExport</emphasis> aka
<literal>hbm2ddl</literal>.
</para>
<sect1 id="toolsetguide-s1" revision="2">
<title>Automatic schema generation</title>
<para>
DDL may be generated from your mapping files by a Hibernate utility. The generated
schema includes referential integrity constraints (primary and foreign keys) for
entity and collection tables. Tables and sequences are also created for mapped
identifier generators.
</para>
<para>
You <emphasis>must</emphasis> specify a SQL <literal>Dialect</literal> via the
<literal>hibernate.dialect</literal> property when using this tool, as DDL
is highly vendor specific.
</para>
<para>
First, customize your mapping files to improve the generated schema.
</para>
<sect2 id="toolsetguide-s1-2" revision="3">
<title>Customizing the schema</title>
<para>
Many Hibernate mapping elements define optional attributes named <literal>length</literal>,
<literal>precision</literal> and <literal>scale</literal>. You may set the length, precision
and scale of a column with this attribute.
</para>
<programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
<programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
<para>
Some tags also accept a <literal>not-null</literal> attribute (for generating a
<literal>NOT NULL</literal> constraint on table columns) and a <literal>unique</literal>
attribute (for generating <literal>UNIQUE</literal> constraint on table columns).
</para>
<programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
<programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
<para>
A <literal>unique-key</literal> attribute may be used to group columns in
a single unique key constraint. Currently, the specified value of the
<literal>unique-key</literal> attribute is <emphasis>not</emphasis> used
to name the constraint in the generated DDL, only to group the columns in
the mapping file.
</para>
<programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
<property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
<para>
An <literal>index</literal> attribute specifies the name of an index that
will be created using the mapped column or columns. Multiple columns may be
grouped into the same index, simply by specifying the same index name.
</para>
<programlisting><![CDATA[<property name="lastName" index="CustName"/>
<property name="firstName" index="CustName"/>]]></programlisting>
<para>
A <literal>foreign-key</literal> attribute may be used to override the name
of any generated foreign key constraint.
</para>
<programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
<para>
Many mapping elements also accept a child <literal>&lt;column&gt;</literal> element.
This is particularly useful for mapping multi-column types:
</para>
<programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
<column name="last" not-null="true" index="bar_idx" length="30"/>
<column name="first" not-null="true" index="bar_idx" length="20"/>
<column name="initial"/>
</property>]]></programlisting>
<para>
The <literal>default</literal> attribute lets you specify a default value for
a column (you should assign the same value to the mapped property before
saving a new instance of the mapped class).
</para>
<programlisting><![CDATA[<property name="credits" type="integer" insert="false">
<column name="credits" default="10"/>
</property>]]></programlisting>
<programlisting><![CDATA[<version name="version" type="integer" insert="false">
<column name="version" default="0"/>
</property>]]></programlisting>
<para>
The <literal>sql-type</literal> attribute allows the user to override the default
mapping of a Hibernate type to SQL datatype.
</para>
<programlisting><![CDATA[<property name="balance" type="float">
<column name="balance" sql-type="decimal(13,3)"/>
</property>]]></programlisting>
<para>
The <literal>check</literal> attribute allows you to specify a check constraint.
</para>
<programlisting><![CDATA[<property name="foo" type="integer">
<column name="foo" check="foo > 10"/>
</property>]]></programlisting>
<programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
...
<property name="bar" type="float"/>
</class>]]></programlisting>
<table frame="topbot" id="schemattributes-summary" revision="2">
<title>Summary</title>
<tgroup cols="3">
<colspec colwidth="1*"/>
<colspec colwidth="1*"/>
<colspec colwidth="2.5*"/>
<thead>
<row>
<entry>Attribute</entry>
<entry>Values</entry>
<entry>Interpretation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>length</literal></entry>
<entry>number</entry>
<entry>column length</entry>
</row>
<row>
<entry><literal>precision</literal></entry>
<entry>number</entry>
<entry>column decimal precision</entry>
</row>
<row>
<entry><literal>scale</literal></entry>
<entry>number</entry>
<entry>column decimal scale</entry>
</row>
<row>
<entry><literal>not-null</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>specfies that the column should be non-nullable</entry>
</row>
<row>
<entry><literal>unique</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>specifies that the column should have a unique constraint</entry>
</row>
<row>
<entry><literal>index</literal></entry>
<entry><literal>index_name</literal></entry>
<entry>specifies the name of a (multi-column) index</entry>
</row>
<row>
<entry><literal>unique-key</literal></entry>
<entry><literal>unique_key_name</literal></entry>
<entry>specifies the name of a multi-column unique constraint</entry>
</row>
<row>
<entry><literal>foreign-key</literal></entry>
<entry><literal>foreign_key_name</literal></entry>
<entry>
specifies the name of the foreign key constraint generated
for an association, for a <literal>&lt;one-to-one&gt;</literal>,
<literal>&lt;many-to-one&gt;</literal>, <literal>&lt;key&gt;</literal>,
or <literal>&lt;many-to-many&gt;</literal> mapping element. Note that
<literal>inverse="true"</literal> sides will not be considered
by <literal>SchemaExport</literal>.
</entry>
</row>
<row>
<entry><literal>sql-type</literal></entry>
<entry><literal>SQL column type</literal></entry>
<entry>
overrides the default column type (attribute of
<literal>&lt;column&gt;</literal> element only)
</entry>
</row>
<row>
<entry><literal>default</literal></entry>
<entry>SQL expression</entry>
<entry>
specify a default value for the column
</entry>
</row>
<row>
<entry><literal>check</literal></entry>
<entry>SQL expression</entry>
<entry>
create an SQL check constraint on either column or table
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The <literal>&lt;comment&gt;</literal> element allows you to specify comments
for the generated schema.
</para>
<programlisting><![CDATA[<class name="Customer" table="CurCust">
<comment>Current customers only</comment>
...
</class>]]></programlisting>
<programlisting><![CDATA[<property name="balance">
<column name="bal">
<comment>Balance in USD</comment>
</column>
</property>]]></programlisting>
<para>
This results in a <literal>comment on table</literal> or
<literal>comment on column</literal> statement in the generated
DDL (where supported).
</para>
</sect2>
<sect2 id="toolsetguide-s1-3" revision="2">
<title>Running the tool</title>
<para>
The <literal>SchemaExport</literal> tool writes a DDL script to standard out and/or
executes the DDL statements.
</para>
<para>
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
</para>
<table frame="topbot">
<title><literal>SchemaExport</literal> Command Line Options</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Option</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>don't output the script to stdout</entry>
</row>
<row>
<entry><literal>--drop</literal></entry>
<entry>only drop the tables</entry>
</row>
<row>
<entry><literal>--create</literal></entry>
<entry>only create the tables</entry>
</row>
<row>
<entry><literal>--text</literal></entry>
<entry>don't export to the database</entry>
</row>
<row>
<entry><literal>--output=my_schema.ddl</literal></entry>
<entry>output the ddl script to a file</entry>
</row>
<row>
<entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
<entry>select a <literal>NamingStrategy</literal></entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>read Hibernate configuration from an XML file</entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>read database properties from a file</entry>
</row>
<row>
<entry><literal>--format</literal></entry>
<entry>format the generated SQL nicely in the script</entry>
</row>
<row>
<entry><literal>--delimiter=;</literal></entry>
<entry>set an end of line delimiter for the script</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
You may even embed <literal>SchemaExport</literal> in your application:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaExport(cfg).create(false, true);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-4">
<title>Properties</title>
<para>
Database properties may be specified
</para>
<itemizedlist spacing="compact">
<listitem>
<para>as system properties with <literal>-D</literal><emphasis>&lt;property&gt;</emphasis></para>
</listitem>
<listitem>
<para>in <literal>hibernate.properties</literal></para>
</listitem>
<listitem>
<para>in a named properties file with <literal>--properties</literal></para>
</listitem>
</itemizedlist>
<para>
The needed properties are:
</para>
<table frame="topbot">
<title>SchemaExport Connection Properties</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Property Name</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>hibernate.connection.driver_class</literal></entry>
<entry>jdbc driver class</entry>
</row>
<row>
<entry><literal>hibernate.connection.url</literal></entry>
<entry>jdbc url</entry>
</row>
<row>
<entry><literal>hibernate.connection.username</literal></entry>
<entry>database user</entry>
</row>
<row>
<entry><literal>hibernate.connection.password</literal></entry>
<entry>user password</entry>
</row>
<row>
<entry><literal>hibernate.dialect</literal></entry>
<entry>dialect</entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2 id="toolsetguide-s1-5">
<title>Using Ant</title>
<para>
You can call <literal>SchemaExport</literal> from your Ant build script:
</para>
<programlisting><![CDATA[<target name="schemaexport">
<taskdef name="schemaexport"
classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
classpathref="class.path"/>
<schemaexport
properties="hibernate.properties"
quiet="no"
text="no"
drop="no"
delimiter=";"
output="schema-export.sql">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaexport>
</target>]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-6" revision="2">
<title>Incremental schema updates</title>
<para>
The <literal>SchemaUpdate</literal> tool will update an existing schema with "incremental" changes.
Note that <literal>SchemaUpdate</literal> depends heavily upon the JDBC metadata API, so it will
not work with all JDBC drivers.
</para>
<para>
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
</para>
<table frame="topbot">
<title><literal>SchemaUpdate</literal> Command Line Options</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Option</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>don't output the script to stdout</entry>
</row>
<row>
<entry><literal>--text</literal></entry>
<entry>don't export the script to the database</entry>
</row>
<row>
<entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
<entry>select a <literal>NamingStrategy</literal></entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>read database properties from a file</entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>specify a <literal>.cfg.xml</literal> file</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
You may embed <literal>SchemaUpdate</literal> in your application:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaUpdate(cfg).execute(false);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-7">
<title>Using Ant for incremental schema updates</title>
<para>
You can call <literal>SchemaUpdate</literal> from the Ant script:
</para>
<programlisting><![CDATA[<target name="schemaupdate">
<taskdef name="schemaupdate"
classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
classpathref="class.path"/>
<schemaupdate
properties="hibernate.properties"
quiet="no">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemaupdate>
</target>]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-8" revision="1">
<title>Schema validation</title>
<para>
The <literal>SchemaValidator</literal> tool will validate that the existing database schema "matches"
your mapping documents. Note that <literal>SchemaValidator</literal> depends heavily upon the JDBC
metadata API, so it will not work with all JDBC drivers. This tool is extremely useful for testing.
</para>
<para>
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
</para>
<table frame="topbot">
<title><literal>SchemaValidator</literal> Command Line Options</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Option</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
<entry>select a <literal>NamingStrategy</literal></entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>read database properties from a file</entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>specify a <literal>.cfg.xml</literal> file</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
You may embed <literal>SchemaValidator</literal> in your application:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaValidator(cfg).validate();]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-9">
<title>Using Ant for schema validation</title>
<para>
You can call <literal>SchemaValidator</literal> from the Ant script:
</para>
<programlisting><![CDATA[<target name="schemavalidate">
<taskdef name="schemavalidator"
classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
classpathref="class.path"/>
<schemavalidator
properties="hibernate.properties">
<fileset dir="src">
<include name="**/*.hbm.xml"/>
</fileset>
</schemavalidator>
</target>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

@ -1,313 +0,0 @@
<?xml version='1.0' encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Middleware LLC.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="xml">
<title>XML Mapping</title>
<para><emphasis>
Note that this is an experimental feature in Hibernate 3.0 and is under
extremely active development.
</emphasis></para>
<sect1 id="xml-intro" revision="1">
<title>Working with XML data</title>
<para>
Hibernate lets you work with persistent XML data in much the same way
you work with persistent POJOs. A parsed XML tree can be thought of
as just another way to represent the relational data at the object level,
instead of POJOs.
</para>
<para>
Hibernate supports dom4j as API for manipulating XML trees. You can write
queries that retrieve dom4j trees from the database and have any
modification you make to the tree automatically synchronized to the
database. You can even take an XML document, parse it using dom4j, and
write it to the database with any of Hibernate's basic operations:
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
(merging is not yet supported).
</para>
<para>
This feature has many applications including data import/export,
externalization of entity data via JMS or SOAP and XSLT-based reporting.
</para>
<para>
A single mapping may be used to simultaneously map properties of a class
and nodes of an XML document to the database, or, if there is no class to map,
it may be used to map just the XML.
</para>
<sect2 id="xml-intro-mapping">
<title>Specifying XML and class mapping together</title>
<para>
Here is an example of mapping a POJO and XML simultaneously:
</para>
<programlisting><![CDATA[<class name="Account"
table="ACCOUNTS"
node="account">
<id name="accountId"
column="ACCOUNT_ID"
node="@id"/>
<many-to-one name="customer"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"/>
<property name="balance"
column="BALANCE"
node="balance"/>
...
</class>]]></programlisting>
</sect2>
<sect2 id="xml-onlyxml">
<title>Specifying only an XML mapping</title>
<para>
Here is an example where there is no POJO class:
</para>
<programlisting><![CDATA[<class entity-name="Account"
table="ACCOUNTS"
node="account">
<id name="id"
column="ACCOUNT_ID"
node="@id"
type="string"/>
<many-to-one name="customerId"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"
entity-name="Customer"/>
<property name="balance"
column="BALANCE"
node="balance"
type="big_decimal"/>
...
</class>]]></programlisting>
<para>
This mapping allows you to access the data as a dom4j tree, or as a graph of
property name/value pairs (java <literal>Map</literal>s). The property names
are purely logical constructs that may be referred to in HQL queries.
</para>
</sect2>
</sect1>
<sect1 id="xml-mapping" revision="1">
<title>XML mapping metadata</title>
<para>
Many Hibernate mapping elements accept the <literal>node</literal> attribute.
This let's you specify the name of an XML attribute or element that holds the
property or entity data. The format of the <literal>node</literal> attribute
must be one of the following:
</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>"element-name"</literal> - map to the named XML element</para>
</listitem>
<listitem>
<para><literal>"@attribute-name"</literal> - map to the named XML attribute</para>
</listitem>
<listitem>
<para><literal>"."</literal> - map to the parent element</para>
</listitem>
<listitem>
<para>
<literal>"element-name/@attribute-name"</literal> -
map to the named attribute of the named element
</para>
</listitem>
</itemizedlist>
<para>
For collections and single valued associations, there is an additional
<literal>embed-xml</literal> attribute. If <literal>embed-xml="true"</literal>,
the default, the XML tree for the associated entity (or collection of value type)
will be embedded directly in the XML tree for the entity that owns the association.
Otherwise, if <literal>embed-xml="false"</literal>, then only the referenced
identifier value will appear in the XML for single point associations and
collections will simply not appear at all.
</para>
<para>
You should be careful not to leave <literal>embed-xml="true"</literal> for
too many associations, since XML does not deal well with circularity!
</para>
<programlisting><![CDATA[<class name="Customer"
table="CUSTOMER"
node="customer">
<id name="id"
column="CUST_ID"
node="@id"/>
<map name="accounts"
node="."
embed-xml="true">
<key column="CUSTOMER_ID"
not-null="true"/>
<map-key column="SHORT_DESC"
node="@short-desc"
type="string"/>
<one-to-many entity-name="Account"
embed-xml="false"
node="account"/>
</map>
<component name="name"
node="name">
<property name="firstName"
node="first-name"/>
<property name="initial"
node="initial"/>
<property name="lastName"
node="last-name"/>
</component>
...
</class>]]></programlisting>
<para>
in this case, we have decided to embed the collection of account ids, but not
the actual account data. The following HQL query:
</para>
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
<para>
Would return datasets such as this:
</para>
<programlisting><![CDATA[<customer id="123456789">
<account short-desc="Savings">987632567</account>
<account short-desc="Credit Card">985612323</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>]]></programlisting>
<para>
If you set <literal>embed-xml="true"</literal> on the <literal>&lt;one-to-many&gt;</literal>
mapping, the data might look more like this:
</para>
<programlisting><![CDATA[<customer id="123456789">
<account id="987632567" short-desc="Savings">
<customer id="123456789"/>
<balance>100.29</balance>
</account>
<account id="985612323" short-desc="Credit Card">
<customer id="123456789"/>
<balance>-2370.34</balance>
</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>]]></programlisting>
</sect1>
<sect1 id="xml-manipulation" revision="1">
<title>Manipulating XML data</title>
<para>
Let's rearead and update XML documents in the application. We do this by
obtaining a dom4j session:
</para>
<programlisting><![CDATA[Document doc = ....;
Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
List results = dom4jSession
.createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
.list();
for ( int i=0; i<results.size(); i++ ) {
//add the customer data to the XML document
Element customer = (Element) results.get(i);
doc.add(customer);
}
tx.commit();
session.close();]]></programlisting>
<programlisting><![CDATA[Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
Element cust = (Element) dom4jSession.get("Customer", customerId);
for ( int i=0; i<results.size(); i++ ) {
Element customer = (Element) results.get(i);
//change the customer name in the XML and database
Element name = customer.element("name");
name.element("first-name").setText(firstName);
name.element("initial").setText(initial);
name.element("last-name").setText(lastName);
}
tx.commit();
session.close();]]></programlisting>
<para>
It is extremely useful to combine this feature with Hibernate's <literal>replicate()</literal>
operation to implement XML-based data import/export.
</para>
</sect1>
</chapter>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -1,429 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
[
<!ATTLIST svg
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
]>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="354.331"
height="336.614"
id="svg1">
<defs
id="defs3">
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop128" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop129" />
</linearGradient>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient130"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
<radialGradient
cx="0.5"
cy="0.5"
fx="0.5"
fy="0.5"
r="0.5"
id="radialGradient131"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
</defs>
<g
transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
style="font-size:12;"
id="g659">
<rect
width="212.257"
height="57.2441"
x="17.9576"
y="100.132"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect137" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect132" />
</g>
<rect
width="325.86"
height="63.6537"
x="17.4083"
y="15.194"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect136" />
<rect
width="325.86"
height="63.6537"
x="13.6713"
y="12.4966"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect126" />
<g
transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
style="font-size:12;"
id="g164">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect138" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect133" />
</g>
<text
x="170.824753"
y="58.402939"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text183">
<tspan
x="170.824997"
y="58.402901"
id="tspan360">
Application</tspan>
</text>
<text
x="178.076340"
y="364.281433"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text197">
<tspan
x="178.076004"
y="364.281006"
id="tspan421">
Database</tspan>
</text>
<text
x="68.605331"
y="138.524582"
transform="scale(0.823795,0.823795)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text216">
<tspan
x="68.605301"
y="138.524994"
id="tspan384">
SessionFactory</tspan>
</text>
<rect
width="67.0014"
height="101.35"
x="196.927"
y="89.2389"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect387" />
<rect
width="67.0014"
height="101.35"
x="194.633"
y="86.4389"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect388" />
<text
x="249.108841"
y="173.885559"
transform="scale(0.823795,0.823795)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text389">
<tspan
x="249.108994"
y="173.886002"
id="tspan392">
Session</tspan>
</text>
<rect
width="73.0355"
height="101.35"
x="270.995"
y="90.0018"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect395" />
<rect
width="73.0355"
height="101.35"
x="267.869"
y="87.2018"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect396" />
<text
x="328.593658"
y="174.715622"
transform="scale(0.823795,0.823795)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text397">
<tspan
x="328.593994"
y="174.716003"
id="tspan563">
Transaction</tspan>
</text>
<g
transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
style="font-size:12;"
id="g565">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect566" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect567" />
</g>
<text
x="25.592752"
y="204.497803"
transform="scale(0.823795,0.823795)"
style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text568">
<tspan
x="25.592800"
y="204.498001"
id="tspan662">
TransactionFactory</tspan>
</text>
<g
transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
style="font-size:12;"
id="g573">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect574" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect575" />
</g>
<text
x="134.030670"
y="205.532791"
transform="scale(0.823795,0.823795)"
style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text576">
<tspan
x="134.031006"
y="205.533005"
id="tspan664">
ConnectionProvider</tspan>
</text>
<g
transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
style="font-size:12;"
id="g587">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect588" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect589" />
</g>
<rect
width="90.951"
height="44.4829"
x="25.6196"
y="206.028"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect594" />
<rect
width="90.951"
height="44.4829"
x="24.4229"
y="204.135"
style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
id="rect595" />
<text
x="85.575645"
y="282.300354"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text596">
<tspan
x="85.575600"
y="282.299988"
id="tspan607">
JNDI</tspan>
</text>
<rect
width="90.951"
height="44.4829"
x="236.937"
y="206.791"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect610" />
<rect
width="90.951"
height="44.4829"
x="235.741"
y="204.898"
style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
id="rect611" />
<text
x="342.093201"
y="283.226410"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text612">
<tspan
x="342.092987"
y="283.226013"
id="tspan621">
JTA</tspan>
</text>
<rect
width="90.951"
height="44.4829"
x="130.134"
y="206.791"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect616" />
<rect
width="90.951"
height="44.4829"
x="128.937"
y="204.898"
style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
id="rect617" />
<text
x="212.445343"
y="283.226410"
transform="scale(0.823795,0.823795)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text618">
<tspan
x="212.445007"
y="283.226013"
id="tspan623">
JDBC</tspan>
</text>
<g
transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
style="font-size:12;"
id="g637">
<g
transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
id="g167">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect134" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect135" />
</g>
<text
x="33.749969"
y="50.589706"
style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text188">
<tspan
x="33.750000"
y="50.589699"
id="tspan635">
Transient Objects</tspan>
</text>
</g>
<g
transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
style="font-size:12;"
id="g644">
<g
transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
id="g364">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect365" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect366" />
</g>
<text
x="277.123230"
y="85.155571"
style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text367">
<tspan
x="277.122986"
y="85.155602"
id="tspan631">
Persistent</tspan>
<tspan
x="277.122986"
y="96.155602"
id="tspan633">
Objects</tspan>
</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,334 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
[
<!ATTLIST svg
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
]>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="318.898"
height="248.031"
id="svg1">
<defs
id="defs3">
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop128" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop129" />
</linearGradient>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient130"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
<radialGradient
cx="0.5"
cy="0.5"
fx="0.5"
fy="0.5"
r="0.5"
id="radialGradient131"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
</defs>
<rect
width="291.837"
height="57.0074"
x="17.3169"
y="18.646"
style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect136" />
<rect
width="291.837"
height="57.0074"
x="13.9703"
y="16.2302"
style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect126" />
<g
transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
style="font-size:12;"
id="g161">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect137" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect132" />
</g>
<g
transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
style="font-size:12;"
id="g164">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect138" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect133" />
</g>
<g
transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
style="font-size:12;"
id="g167">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect134" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect135" />
</g>
<text
x="302.277679"
y="65.943230"
transform="scale(0.73778,0.73778)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text183">
<tspan
x="302.277954"
y="65.943184"
id="tspan360">
Application</tspan>
</text>
<text
x="36.235924"
y="63.796055"
transform="scale(0.73778,0.73778)"
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text188">
<tspan
x="36.235950"
y="63.796051"
id="tspan427">
Transient Objects</tspan>
</text>
<text
x="180.416245"
y="290.543701"
transform="scale(0.73778,0.73778)"
style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text197">
<tspan
x="180.415939"
y="290.543549"
id="tspan421">
Database</tspan>
</text>
<text
x="25.037701"
y="179.154755"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text216">
<tspan
x="25.037655"
y="179.154648"
id="tspan384">
SessionFactory</tspan>
</text>
<g
transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
style="font-size:12;"
id="g386">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect387" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect388" />
</g>
<g
transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
style="font-size:12;"
id="g364">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect365" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect366" />
</g>
<text
x="202.746506"
y="102.992203"
transform="scale(0.73778,0.73778)"
style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
id="text367">
<tspan
x="202.746948"
y="102.992249"
id="tspan423">
Persistent</tspan>
<tspan
x="202.746948"
y="116.992355"
id="tspan425">
Objects</tspan>
</text>
<text
x="174.458496"
y="180.080795"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text389">
<tspan
x="174.458618"
y="180.080338"
id="tspan392">
Session</tspan>
</text>
<g
transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
style="font-size:12;"
id="g394">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect395" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect396" />
</g>
<text
x="260.413269"
y="179.154739"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text397">
<tspan
x="260.412964"
y="179.154343"
id="tspan400">
JDBC</tspan>
</text>
<g
transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
style="font-size:12;"
id="g405">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect406" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect407" />
</g>
<text
x="320.606903"
y="179.154739"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text408">
<tspan
x="320.606964"
y="179.154343"
id="tspan417">
JNDI</tspan>
</text>
<g
transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
style="font-size:12;"
id="g411">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect412" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect413" />
</g>
<text
x="377.096313"
y="179.154739"
transform="scale(0.73778,0.73778)"
style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text414">
<tspan
x="377.096008"
y="179.154999"
id="tspan145">
JTA</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -1,250 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
[
<!ATTLIST svg
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
]>
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="248.031"
height="248.031"
id="svg1">
<defs
id="defs3">
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop128" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop129" />
</linearGradient>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
id="linearGradient130"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
<radialGradient
cx="0.5"
cy="0.5"
fx="0.5"
fy="0.5"
r="0.5"
id="radialGradient131"
xlink:href="#linearGradient127"
gradientUnits="objectBoundingBox"
spreadMethod="pad" />
</defs>
<g
transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
style="font-size:12;"
id="g158">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="17.3527"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect136" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="15.3883"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect126" />
</g>
<g
transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
style="font-size:12;"
id="g161">
<rect
width="285.502"
height="118.523"
x="16.6979"
y="99.2053"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect137" />
<rect
width="285.502"
height="118.523"
x="13.4238"
y="95.9309"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect132" />
</g>
<g
transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
style="font-size:12;"
id="g164">
<rect
width="285.502"
height="77.2688"
x="16.6979"
y="222.966"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect138" />
<rect
width="285.502"
height="77.2688"
x="14.7335"
y="221.002"
transform="translate(-1.30962,-1.30992)"
style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
id="rect133" />
</g>
<g
transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
style="font-size:12;"
id="g167">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect134" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect135" />
</g>
<text
x="105.392174"
y="56.568123"
transform="scale(0.771934,0.771934)"
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text183">
<tspan
x="105.392273"
y="56.568146"
id="tspan186">
Application</tspan>
</text>
<text
x="81.820183"
y="103.149330"
transform="scale(0.771934,0.771934)"
style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text188">
<tspan
x="81.820213"
y="103.149727"
id="tspan206">
Persistent Objects</tspan>
</text>
<text
x="111.548180"
y="278.927887"
transform="scale(0.771934,0.771934)"
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text197">
<tspan
x="111.547874"
y="278.927551"
id="tspan200">
Database</tspan>
</text>
<text
x="94.436180"
y="153.805740"
transform="scale(0.771934,0.771934)"
style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
id="text216">
<tspan
x="94.436180"
y="153.805740"
id="tspan221">
HIBERNATE</tspan>
</text>
<g
transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
style="font-size:12;"
id="g254">
<g
transform="translate(4.58374,2.61928)"
id="g176">
<g
transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
id="g170">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect171" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect172" />
</g>
<g
transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
id="g173">
<rect
width="199.065"
height="61.5532"
x="61.8805"
y="68.4288"
style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
id="rect174" />
<rect
width="199.065"
height="61.5532"
x="59.2613"
y="65.8095"
style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
id="rect175" />
</g>
</g>
<text
x="47.259438"
y="182.367538"
style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
id="text191">
<tspan
x="47.259399"
y="182.367996"
id="tspan212">
hibernate.</tspan>
<tspan
x="47.259399"
y="194.367996"
id="tspan214">
properties</tspan>
</text>
<text
x="198.523010"
y="188.260941"
style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
id="text194">
<tspan
id="tspan195">
XML Mapping</tspan>
</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.5 KiB