HHH-3556: Envers documentation partially migrated
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15500 1b8cb986-b30d-0410-93ca-fae66ebed9b2
|
@ -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>
|
||||
|
|
@ -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><key></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><column></literal> element, but on the <literal><key></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>
|
||||
|
|
@ -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>
|
|
@ -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><component></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><natural-id></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>
|
||||
|
|
@ -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><component></literal> element allows a <literal><parent></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><element></literal> tag with a
|
||||
<literal><composite-element></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><nested-composite-element></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><set></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><list></literal>, <literal><map></literal>,
|
||||
<literal><bag></literal> or <literal><idbag></literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A special case of a composite element is a composite element with a nested
|
||||
<literal><many-to-one></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><composite-map-key></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><composite-id></literal> tag (with nested
|
||||
<literal><key-property></literal> elements) in place of the usual
|
||||
<literal><id></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><column></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><one-to-many></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><dynamic-component></literal> mapping are identical
|
||||
to <literal><component></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>
|
|
@ -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><listener/></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><listener type="..." class="..."/></literal> is just a shorthand
|
||||
for <literal><event type="..."><listener class="..."/></event></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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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><one-to-many></literal> association from <literal>Parent</literal>
|
||||
to <literal>Child</literal>. (The alternative approach is to declare the <literal>Child</literal> as a
|
||||
<literal><composite-element></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><one-to-many></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><composite-element></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>
|
|
@ -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>
|
||||
|
|
@ -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><filter-def/></literal> element
|
||||
within a <literal><hibernate-mapping/></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><filter-def/></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>
|
||||
|
|
@ -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><subclass></literal>,
|
||||
and <literal><joined-subclass></literal> and
|
||||
<literal><union-subclass></literal> mappings under the same root
|
||||
<literal><class></literal> element. It is possible to mix together
|
||||
the table per hierarchy and table per subclass strategies, under the
|
||||
the same <literal><class></literal> element, by combining the
|
||||
<literal><subclass></literal> and <literal><join></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><subclass></literal> and
|
||||
<literal><join></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><many-to-one></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><union-subclass></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>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
|
||||
in the <literal>DOCTYPE</literal> declartion and
|
||||
<literal>&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><any></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><class></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><union-subclass></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><many-to-one></literal></entry>
|
||||
<entry><literal><one-to-one></literal></entry>
|
||||
<entry><literal><one-to-many></literal></entry>
|
||||
<entry><literal><many-to-many></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><many-to-one></literal></entry>
|
||||
<entry><literal><one-to-one></literal></entry>
|
||||
<entry><literal><one-to-many></literal></entry>
|
||||
<entry><literal><many-to-many></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><many-to-one></literal></entry>
|
||||
<entry><literal><one-to-one></literal></entry>
|
||||
<entry><literal><one-to-many></literal> (for <literal>inverse="true"</literal> only)</entry>
|
||||
<entry><literal><many-to-many></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><any></literal></entry>
|
||||
<entry><emphasis>not supported</emphasis></entry>
|
||||
<entry><emphasis>not supported</emphasis></entry>
|
||||
<entry><literal><many-to-any></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>
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -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><natural-id></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>
|
|
@ -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><return-join></literal> and
|
||||
<literal><load-collection></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><return-scalar></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><resultset></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><return-property></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><return-property></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><return-property></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><return-discriminator></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><return-join></literal> and
|
||||
<literal><load-collection></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(<parameters>) }</literal> or <literal>{ ? = call
|
||||
procedureName(<parameters>}</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><sql-insert></literal>,
|
||||
<literal><sql-delete></literal>, and
|
||||
<literal><sql-update></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>
|
|
@ -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><column></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><one-to-one></literal>,
|
||||
<literal><many-to-one></literal>, <literal><key></literal>,
|
||||
or <literal><many-to-many></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><column></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><comment></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><property></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>
|
||||
|
|
@ -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><one-to-many></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>
|
||||
|
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 9.1 KiB |
|
@ -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 |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 6.7 KiB |
|
@ -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 |
Before Width: | Height: | Size: 8.2 KiB |
|
@ -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 |