SVN layout migration for core/trunk

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@11723 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2007-06-29 19:23:53 +00:00
parent d8d6d82e30
commit 6eee4be625
329 changed files with 150382 additions and 0 deletions

176
documentation/manual/README Normal file
View File

@ -0,0 +1,176 @@
THE HIBERNATE DOCUMENTATION
christian@hibernate.org
COPYRIGHT NOTICE: This documentation system and all its source files are
licensed under the GNU Lesser Public License (LGPL). Authors and translators
retain the copyright of their work. All font and other build files (the DocBook
system) are property of their respective copyright holders. Some of the files
(especially font files) might require a license from the respective vendor; you
are responsible to check and obtain these licenses as necessary before you use
and/or distribute these files.
Preface
The Hibernate documentation is a modular documentation, it uses
various XML files (written with the DocBook DTD) and a Java-based
build process to generate HTML and PDF output. Use a simple text
editor with XML support, such as JEdit, to edit the source files. You
will need Java and Ant installed for the output generation. The toolset
is Java only and should work on any operating system.
Note: Always use 4 spaces to indent, no tabstops (code examples will
be broken otherwise).
1. How to get it
Check out a copy of Hibernate from the repository. A regular
Hibernate download will not contain the build process for the
documentation, only the PDF/HTML output, use the repository!
See http://www.hibernate.org/Download/DownloadOverview
2. Working on the original language
The original and master language is English, hence the "en" subdirectory
in /doc/reference/ is authorative. We use "id" and "revision" attributes on
XML elements to track changes. Here are the rules, they are mandatory:
2a. Changing existing content involves an update of the "revision" of the XML
element you are working on (e.g. a <sect1>, <sect2> or even a <para>).
If a <sect1> has a revision="1", you update it to "2" after updating the
content in that section.
You can also add a revision attribute to an element if there is none,
start with revision="1". You should not add a revision attribute to each
paragraph, try to only add/use revision attributes to sections. You can'
t add a revision attribute to elements without an "id" attribute!
2b. Adding new content involves adding new elements (even new files), such
as <sect1>, <para> and so on. Any new element (or its new parent element)
needs an "id" attribute if the new content is to be included in the change
tracking. If you add a section, give it a unique short text
identifer, look at the parent element's identifier for the common prefix.
2c. Deleting content involves removing old elements. Just remove them and
make sure that the parent elements revision is updated, if the removed
element did not itself have an identifer and a revision. If you remove an
element with its own identifier, everything is fine and no other changes are
necessary.
3. Starting a new language
If you start a translation for a new language, you have to copy
the default language (English) and start an initial translation.
3a. First, duplicate the default language "en" by duplicating the directory
/doc/reference/en. For example, a new German translation
will be a copy of that directory in /doc/reference/de. We use the ISO
codes to name the language subdirectories.
3b. You also have to add your new language to the language build file,
/doc/reference/build.xml. Look for the lines that have a "TRANSLATOR"
comment and duplicate them. Change the default "en" to your language
code, every language listed here will be included in both the PDF/HTML
generation and the revision diff change tracking reports (discussed later).
4. The initial translation
If you just copied the default language, start translating the DocBook
XML modules and illustrations in the new language subdirectory. For
example, all modules for German would be in /doc/reference/de/modules
and all illustrations in /doc/reference/de/images, note that you also have
to translate the master.xml in your language subdirectory.
The initial translation is straightforward: Translate all modules and
all illustrations, but don't add any files, don't add any new XML elements
(like a section or a chapter, not even a paragraph). Simply translate
sentence by sentence. This is very important.
Note that every DocBook XML file needs an encoding, specific to a
language. Add a line like this at the top of every file, if it doesn't exist:
<?xml version='1.0' encoding="iso-8859-1"?>
You can use UTF-8 or any other character set, please experiment with
the builds to see what works for you.
If you need a new section or paragraph, because your translation requires
more explanation, you can add it if you also add an "id" and a "revision"
to that new section or paragraph.
For example, if you add a new <para> element to the existing document,
give it an identifier, a short unique string that extends the identifier
string of the parent element: <para id="queryhql-projection-specialnote">
would be a special paragraph in the <sect1 id="queryhql-projection">
section in the chapter <chapter id="queryhql">.
Never add a new element in a translated version without also adding a new
unique identifier value! Also, you have to mark this new element as "only
relevant in the translated version". Simply set the "revision" attribute of
your new element to "-1". For example, set the previously created
paragraph to "only relevant in the translation" by declaring
<para id="queryhql-projection-specialnote" revision="-1">.
Changes to that paragraph will not be tracked, it is your responsibility to
watch out for neccessary updates. Any element with revision="-1" will not be
tracked.
5. Updating translated documentation
Translators get updates by updating their working directory from the
repository. As a translator you will get an e-mail from us when translation
is required, you can then update your copy. Or, subscribe to the commit
mailing list to get all updates automatically.
The documentation tools can generate a report after you updated
from the repository and show you what needs to be translated and/or removed
in your local translation copy. To generate that report, run "ant all.revdiff"
in the doc/reference/ subdirectory. Click on the generated HTML report
file for your language and you will see what has to be updated and/or
removed.
If the report indicates that content in the original has been removed,
simply remove the identified XML element from your language modules.
If the report detects a new revision, open the file that has been updated
in your translation, find the identified XML element and update/translate
its contents. Important: Make sure you also update the "revision"
attribute of that XML element by setting it to the same version as in
the original file, hence both the original XML file and your translated
file should have the same revision number for all elements. If an
XML element in your translation doesn't have a revision, but the original
file has, add a new "revision" attribute to your XML element.
The HTML report shows the identifiers and revisions for both the original
and the translated files, use it to compare.
Rerun the "ant all.revdiff" report generation as often as you like until
no more differences are detected. You should always try to get your
copy clean, with all updated revisions and all identified elements
synchronzied.
6. Committing a translation
All translators will be asked to submit their translated versions from
time to time. This will be a manual process, you will get an e-mail from
the Hibernate team and simply send your language subdirectory as
a ZIP file to us. It will then be integrated in the main Hibernate
distribution and on the website. Or, you can contact us for commit access
to the repository, where you can maintain a translation directly.
7. Generating PDF and HTML output
The documentation is generated with the target 'ant all.doc'.
To build the reference docs for a particular language only, use
"ant -Dlang=en", for example, and call either lang.all, lang.docpdf,
lang.dochtml, or lang.dochtmlsingle for the target of your choice.
You can also call lang.section-check to track down missing identifiers in
a particular language, or you can call lang.revdiff to get a difference
report for a particular language, compared with the English reference.

View File

@ -0,0 +1,65 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-manual</artifactId>
<version>3.3.0.beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hibernate-manual-${translation}</artifactId>
<packaging>docbook</packaging>
<name>Hibernate Manual (${translation})</name>
<build>
<plugins>
<plugin>
<groupId>org.jboss.maven.plugins</groupId>
<artifactId>maven-jboss-docbook-plugin</artifactId>
<version>2.0-SNAPSHOT</version>
<extensions>true</extensions>
<dependencies>
<dependency>
<groupId>com.redhat</groupId>
<artifactId>docbook-xslt</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
<configuration>
<sourceDocumentName>master.xml</sourceDocumentName>
<formats>
<format>
<formatName>pdf</formatName>
<stylesheetResource>/standard/fopdf.xsl</stylesheetResource>
<finalName>Hibernate_Reference.pdf</finalName>
</format>
<format>
<formatName>eclipse</formatName>
</format>
<format>
<formatName>html</formatName>
<stylesheetResource>/standard/html.xsl</stylesheetResource>
<finalName>index.html</finalName>
</format>
</formats>
<options>
<xincludeSupported>true</xincludeSupported>
<xmlTransformerType>saxon</xmlTransformerType>
<!-- needed for uri-resolvers; can be ommitted if using 'current' uri scheme -->
<!-- could also locate the docbook dependency and inspect its version... -->
<docbookVersion>1.72.0</docbookVersion>
</options>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<translation>en-US</translation>
</properties>
</project>

View File

@ -0,0 +1,183 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
"../support/docbook-dtd/docbookx.dtd"
[
<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
<!ENTITY architecture SYSTEM "modules/architecture.xml">
<!ENTITY configuration SYSTEM "modules/configuration.xml">
<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
<!ENTITY session-api SYSTEM "modules/session_api.xml">
<!ENTITY transactions SYSTEM "modules/transactions.xml">
<!ENTITY events SYSTEM "modules/events.xml">
<!ENTITY batch SYSTEM "modules/batch.xml">
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
<!ENTITY filters SYSTEM "modules/filters.xml">
<!ENTITY xml SYSTEM "modules/xml.xml">
<!ENTITY performance SYSTEM "modules/performance.xml">
<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
]>
<book lang="en">
<bookinfo>
<title>HIBERNATE - Relational Persistence for Idiomatic Java</title>
<subtitle>Hibernate Reference Documentation</subtitle>
<releaseinfo>3.2 cr3</releaseinfo>
</bookinfo>
<toc/>
<preface id="preface" revision="2">
<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>Hibernate in Action</emphasis>
(http://www.manning.com/bauer) 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 Hibernate in Action.
</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>
&tutorial;
&architecture;
&configuration;
&persistent-classes;
&basic-mapping;
&collection-mapping;
&association-mapping;
&component-mapping;
&inheritance-mapping;
&session-api;
&transactions;
&events;
&batch;
&query-hql;
&query-criteria;
&query-sql;
&filters;
&xml;
&performance;
&toolset-guide;
&example-parentchild;
&example-weblog;
&example-mappings;
&best-practices;
</book>

View File

@ -0,0 +1,355 @@
<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="../shared/images/overview.gif" format="GIF" 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="../shared/images/lite.gif" format="GIF" 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="../shared/images/full_cream.gif" format="GIF" 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 corrsponding 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 lifecycle
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 programatic transaction demarcation in plain JSE without JTA, you are adviced 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 two corresponding
short names, "jta", "thread", and "managed".
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,623 @@
<chapter id="associations">
<title>Association Mappings</title>
<sect1 id="assoc-intro" revision="1">
<title>Introduction</title>
<para>
Association mappings are the often most difficult thing to get right. In
this section we'll go through the canonical cases one by one, starting
with unidirectional mappings, and then considering the bidirectional cases.
We'll use <literal>Person</literal> and <literal>Address</literal> in all
the examples.
</para>
<para>
We'll classify associations by whether or not they map to an intervening
join table, and by multiplicity.
</para>
<para>
Nullable foreign keys are not considered good practice in traditional data
modelling, so all our examples use not null foreign keys. This is not a
requirement of Hibernate, and the mappings will all work if you drop the
nullability constraints.
</para>
</sect1>
<sect1 id="assoc-unidirectional" revision="1">
<title>Unidirectional associations</title>
<sect2 id="assoc-unidirectional-m21">
<title>many to one</title>
<para>
A <emphasis>unidirectional many-to-one association</emphasis> is the most
common kind of unidirectional association.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-121">
<title>one to one</title>
<para>
A <emphasis>unidirectional one-to-one association on a foreign key</emphasis>
is almost identical. The only difference is the column unique constraint.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
A <emphasis>unidirectional one-to-one association on a primary key</emphasis>
usually uses a special id generator. (Notice that we've reversed the direction
of the association in this example.)
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-12m">
<title>one to many</title>
<para>
A <emphasis>unidirectional one-to-many association on a foreign key</emphasis>
is a very unusual case, and is not really recommended.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId bigint not null )
]]></programlisting>
<para>
We think it's better to use a join table for this kind of association.
</para>
</sect2>
</sect1>
<sect1 id="assoc-unidirectional-join" revision="1">
<title>Unidirectional associations with join tables</title>
<sect2 id="assoc-unidirectional-join-12m">
<title>one to many</title>
<para>
A <emphasis>unidirectional one-to-many association on a join table</emphasis>
is much preferred. Notice that by specifying <literal>unique="true"</literal>,
we have changed the multiplicity from many-to-many to one-to-many.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-m21">
<title>many to one</title>
<para>
A <emphasis>unidirectional many-to-one association on a join table</emphasis>
is quite common when the association is optional.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-121">
<title>one to one</title>
<para>
A <emphasis>unidirectional one-to-one association on a join table</emphasis>
is extremely unusual, but possible.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-unidirectional-join-m2m">
<title>many to many</title>
<para>
Finally, we have a <emphasis>unidirectional many-to-many association</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-bidirectional" revision="1">
<title>Bidirectional associations</title>
<sect2 id="assoc-bidirectional-m21" revision="2">
<title>one to many / many to one</title>
<para>
A <emphasis>bidirectional many-to-one association</emphasis> is the
most common kind of association. (This is the standard parent/child
relationship.)
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
If you use a <literal>List</literal> (or other indexed collection) you need
to set the <literal>key</literal> column of the foreign key to <literal>not null</literal>,
and let Hibernate manage the association from the collections side to maintain the index
of each element (making the other side virtually inverse by setting
<literal>update="false"</literal> and <literal>insert="false"</literal>):
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id"/>
...
<many-to-one name="address"
column="addressId"
not-null="true"
insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class>]]></programlisting>
<para>
It is important that you define <literal>not-null="true"</literal> on the
<literal>&lt;key&gt;</literal> element of the collection mapping if the
underlying foreign key column is <literal>NOT NULL</literal>. Don't only
declare <literal>not-null="true"</literal> on a possible nested
<literal>&lt;column&gt;</literal> element, but on the <literal>&lt;key&gt;</literal>
element.
</para>
</sect2>
<sect2 id="assoc-bidirectional-121">
<title>one to one</title>
<para>
A <emphasis>bidirectional one-to-one association on a foreign key</emphasis>
is quite common.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
A <emphasis>bidirectional one-to-one association on a primary key</emphasis>
uses the special id generator.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-bidirectional-join" revision="1">
<title>Bidirectional associations with join tables</title>
<sect2 id="assoc-bidirectional-join-12m">
<title>one to many / many to one</title>
<para>
A <emphasis>bidirectional one-to-many association on a join table</emphasis>.
Note that the <literal>inverse="true"</literal> can go on either end of the
association, on the collection, or on the join.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-join-121">
<title>one to one</title>
<para>
A <emphasis>bidirectional one-to-one association on a join table</emphasis>
is extremely unusual, but possible.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="person"
column="personId"
not-null="true"
unique="true"/>
</join>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
<sect2 id="assoc-bidirectional-join-m2m" revision="1">
<title>many to many</title>
<para>
Finally, we have a <emphasis>bidirectional many-to-many association</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>]]></programlisting>
<programlisting><![CDATA[
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
]]></programlisting>
</sect2>
</sect1>
<sect1 id="assoc-complex">
<title>More complex association mappings</title>
<para>
More complex association joins are <emphasis>extremely</emphasis> rare.
Hibernate makes it possible to handle more complex situations using
SQL fragments embedded in the mapping document. For example, if a table
with historical account information data defines
<literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
and <literal>effectiveStartDate</literal>columns, mapped as follows:
</para>
<programlisting><![CDATA[<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
<para>
Then we can map an association to the <emphasis>current</emphasis> instance
(the one with null <literal>effectiveEndDate</literal>) using:
</para>
<programlisting><![CDATA[<many-to-one name="currentAccountInfo"
property-ref="currentAccountKey"
class="AccountInfo">
<column name="accountNumber"/>
<formula>'1'</formula>
</many-to-one>]]></programlisting>
<para>
In a more complex example, imagine that the association between
<literal>Employee</literal> and <literal>Organization</literal> is maintained
in an <literal>Employment</literal> table full of historical employment data.
Then an association to the employee's <emphasis>most recent</emphasis> employer
(the one with the most recent <literal>startDate</literal>) might be mapped this way:
</para>
<programlisting><![CDATA[<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer"
class="Organization"
column="orgId"/>
</join>]]></programlisting>
<para>
You can get quite creative with this functionality, but it is usually more practical
to handle these kinds of cases using HQL or a criteria query.
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,347 @@
<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 lifecycle 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 (<xref linkend="queryhql">HQL</xref>).
</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 <xref linkend="queryhql-joins-forms">joins</xref> (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
<xref linkend="mapping-declaration-version">version</xref>
or the <xref linkend="mapping-declaration-timestamp">timestamp</xref> property values
for the affected entities; this is in keeping with the EJB3 specification. However,
you can force Hibernate to properly reset the <literal>version</literal> or
<literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
keyword.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
are not allowed in conjunction with a <literal>update versioned</literal> statement.
</para>
<para>
To execute an HQL <literal>DELETE</literal>, use the same <literal>Query.executeUpdate()</literal>
method:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer c where c.name = :oldName";
// or String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
The <literal>int</literal> value returned by the <literal>Query.executeUpdate()</literal>
method indicate the number of entities effected by the operation. Consider this may or may not
correlate to the number of rows effected in the database. An HQL bulk operation might result in
multiple actual SQL statements being executed, for joined-subclass, for example. The returned
number indicates the number of actual entities affected by the statement. Going back to the
example of joined-subclass, a delete against one of the subclasses may actually result
in deletes against not just the table to which that subclass is mapped, but also the "root"
table and potentially joined-subclass tables further down the inheritence hierarchy.
</para>
<para>
The pseudo-syntax for <literal>INSERT</literal> statements is:
<literal>INSERT INTO EntityName properties_list select_statement</literal>. Some
points to note:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Only the INSERT INTO ... SELECT ... form is supported; not the INSERT INTO ... VALUES ... form.
</para>
<para>
The properties_list is analogous to the <literal>column speficiation</literal>
in the SQL <literal>INSERT</literal> statement. For entities involved in mapped
inheritence, only properties directly defined on that given class-level can be
used in the properties_list. Superclass properties are not allowed; and subclass
properties do not make sense. In other words, <literal>INSERT</literal>
statements are inherently non-polymorphic.
</para>
</listitem>
<listitem>
<para>
select_statement can be any valid HQL select query, with the caveat that the return types
must match the types expected by the insert. Currently, this is checked during query
compilation rather than allowing the check to relegate to the database. Note however
that this might cause problems between Hibernate <literal>Type</literal>s which are
<emphasis>equivalent</emphasis> as opposed to <emphasis>equal</emphasis>. This might cause
issues with mismatches between a property defined as a <literal>org.hibernate.type.DateType</literal>
and a property defined as a <literal>org.hibernate.type.TimestampType</literal>, even though the
database might not make a distinction or might be able to handle the conversion.
</para>
</listitem>
<listitem>
<para>
For the id property, the insert statement gives you two options. You can either
explicitly specify the id property in the properties_list (in which case its value
is taken from the corresponding select expression) or omit it from the properties_list
(in which case a generated value is used). This later option is only available when
using id generators that operate in the database; attempting to use this option with
any "in memory" type generators will cause an exception during parsing. Note that
for the purposes of this discussion, in-database generators are considered to be
<literal>org.hibernate.id.SequenceGenerator</literal> (and its subclasses) and
any implementors of <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
The most notable exception here is <literal>org.hibernate.id.TableHiLoGenerator</literal>,
which cannot be used because it does not expose a selectable way to get its values.
</para>
</listitem>
<listitem>
<para>
For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
the insert statement gives you two options. You can either specify the property in the
properties_list (in which case its value is taken from the corresponding select expressions)
or omit it from the properties_list (in which case the <literal>seed value</literal> defined
by the <literal>org.hibernate.type.VersionType</literal> is used).
</para>
</listitem>
</itemizedlist>
<para>
An example HQL <literal>INSERT</literal> statement execution:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,223 @@
<chapter id="best-practices" revision="3">
<title>Best Practices</title>
<variablelist spacing="compact">
<varlistentry>
<term>Write fine-grained classes and map them using <literal>&lt;component&gt;</literal>.</term>
<listitem>
<para>
Use an <literal>Address</literal> class to encapsulate <literal>street</literal>,
<literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
This encourages code reuse and simplifies refactoring.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Declare identifier properties on persistent classes.</term>
<listitem>
<para>
Hibernate makes identifier properties optional. There are all sorts of reasons why
you should use them. We recommend that identifiers be 'synthetic' (generated, with
no business meaning).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Identify natural keys.</term>
<listitem>
<para>
Identify natural keys for all entities, and map them using
<literal>&lt;natural-id&gt;</literal>. Implement <literal>equals()</literal> and
<literal>hashCode()</literal> to compare the properties that make up the natural key.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Place each class mapping in its own file.</term>
<listitem>
<para>
Don't use a single monolithic mapping document. Map <literal>com.eg.Foo</literal> in
the file <literal>com/eg/Foo.hbm.xml</literal>. This makes particularly good sense in
a team environment.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Load mappings as resources.</term>
<listitem>
<para>
Deploy the mappings along with the classes they map.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Consider externalising query strings.</term>
<listitem>
<para>
This is a good practice if your queries call non-ANSI-standard SQL functions.
Externalising the query strings to mapping files will make the application more
portable.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Use bind variables.</term>
<listitem>
<para>
As in JDBC, always replace non-constant values by "?". Never use string manipulation to
bind a non-constant value in a query! Even better, consider using named parameters in
queries.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Don't manage your own JDBC connections.</term>
<listitem>
<para>
Hibernate lets the application manage JDBC connections. This approach should be considered
a last-resort. If you can't use the built-in connections providers, consider providing your
own implementation of <literal>org.hibernate.connection.ConnectionProvider</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Consider using a custom type.</term>
<listitem>
<para>
Suppose you have a Java type, say from some library, that needs to be persisted but doesn't
provide the accessors needed to map it as a component. You should consider implementing
<literal>org.hibernate.UserType</literal>. This approach frees the application
code from implementing transformations to / from a Hibernate type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Use hand-coded JDBC in bottlenecks.</term>
<listitem>
<para>
In performance-critical areas of the system, some kinds of operations might benefit from
direct JDBC. But please, wait until you <emphasis>know</emphasis> something is a bottleneck.
And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might
be worth opening a Hibernate <literal>Session</literal> and using that JDBC connection. That
way you can still use the same transaction strategy and underlying connection provider.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Understand <literal>Session</literal> flushing.</term>
<listitem>
<para>
From time to time the Session synchronizes its persistent state with the database. Performance will
be affected if this process occurs too often. You may sometimes minimize unnecessary flushing by
disabling automatic flushing or even by changing the order of queries and other operations within a
particular transaction.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>In a three tiered architecture, consider using detached objects.</term>
<listitem>
<para>
When using a servlet / session bean architecture, you could pass persistent objects loaded in
the session bean to and from the servlet / JSP layer. Use a new session to service each request.
Use <literal>Session.merge()</literal> or <literal>Session.saveOrUpdate()</literal> to
synchronize objects with the database.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>In a two tiered architecture, consider using long persistence contexts.</term>
<listitem>
<para>
Database Transactions have to be as short as possible for best scalability. However, it is often
neccessary to implement long running <emphasis>application transactions</emphasis>, a single
unit-of-work from the point of view of a user. An application transaction might span several
client request/response cycles. It is common to use detached objects to implement application
transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain
a single open persistence contact (session) for the whole lifecycle 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>

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,658 @@
<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="fo">
<imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
</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="fo">
<imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
</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="fo">
<imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
The mapping document:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Customer" table="customers">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="orders" inverse="true">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
</class>
<class name="Order" table="orders">
<id name="id">
<generator class="native"/>
</id>
<property name="date"/>
<many-to-one name="customer" column="customer_id"/>
<list name="lineItems" table="line_items">
<key column="order_id"/>
<list-index column="line_number"/>
<composite-element class="LineItem">
<property name="quantity"/>
<many-to-one name="product" column="product_id"/>
</composite-element>
</list>
</class>
<class name="Product" table="products">
<id name="id">
<generator class="native"/>
</id>
<property name="serialNumber"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
<literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> and
<literal>products</literal> hold customer, order, order line item and product data
respectively. <literal>line_items</literal> also acts as an association table linking
orders with products.
</para>
<programlisting><![CDATA[create table customers (
id BIGINT not null generated by default as identity,
name VARCHAR(255),
primary key (id)
)
create table orders (
id BIGINT not null generated by default as identity,
customer_id BIGINT,
date TIMESTAMP,
primary key (id)
)
create table line_items (
line_number INTEGER not null,
order_id BIGINT not null,
product_id BIGINT,
quantity INTEGER,
primary key (order_id, line_number)
)
create table products (
id BIGINT not null generated by default as identity,
serialNumber VARCHAR(255),
primary key (id)
)
alter table orders
add constraint ordersFK0 foreign key (customer_id) references customers
alter table line_items
add constraint line_itemsFK0 foreign key (product_id) references products
alter table line_items
add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
</sect1>
<sect1 id="misc">
<title>Miscellaneous example mappings</title>
<para>
These examples are all taken from the Hibernate test suite. You
will find many other useful example mappings there. Look in the
<literal>test</literal> folder of the Hibernate distribution.
</para>
<para>TODO: put words around this stuff</para>
<sect2 id="example-mappings-typed-onetone">
<title>"Typed" one-to-one association</title>
<programlisting><![CDATA[<class name="Person">
<id name="name"/>
<one-to-one name="address"
cascade="all">
<formula>name</formula>
<formula>'HOME'</formula>
</one-to-one>
<one-to-one name="mailingAddress"
cascade="all">
<formula>name</formula>
<formula>'MAILING'</formula>
</one-to-one>
</class>
<class name="Address" batch-size="2"
check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
<composite-id>
<key-many-to-one name="person"
column="personName"/>
<key-property name="type"
column="addressType"/>
</composite-id>
<property name="street" type="text"/>
<property name="state"/>
<property name="zip"/>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-composite-key">
<title>Composite key example</title>
<programlisting><![CDATA[<class name="Customer">
<id name="customerId"
length="10">
<generator class="assigned"/>
</id>
<property name="name" not-null="true" length="100"/>
<property name="address" not-null="true" length="200"/>
<list name="orders"
inverse="true"
cascade="save-update">
<key column="customerId"/>
<index column="orderNumber"/>
<one-to-many class="Order"/>
</list>
</class>
<class name="Order" table="CustomerOrder" lazy="true">
<synchronize table="LineItem"/>
<synchronize table="Product"/>
<composite-id name="id"
class="Order$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
</composite-id>
<property name="orderDate"
type="calendar_date"
not-null="true"/>
<property name="total">
<formula>
( select sum(li.quantity*p.price)
from LineItem li, Product p
where li.productId = p.productId
and li.customerId = customerId
and li.orderNumber = orderNumber )
</formula>
</property>
<many-to-one name="customer"
column="customerId"
insert="false"
update="false"
not-null="true"/>
<bag name="lineItems"
fetch="join"
inverse="true"
cascade="save-update">
<key>
<column name="customerId"/>
<column name="orderNumber"/>
</key>
<one-to-many class="LineItem"/>
</bag>
</class>
<class name="LineItem">
<composite-id name="id"
class="LineItem$Id">
<key-property name="customerId" length="10"/>
<key-property name="orderNumber"/>
<key-property name="productId" length="10"/>
</composite-id>
<property name="quantity"/>
<many-to-one name="order"
insert="false"
update="false"
not-null="true">
<column name="customerId"/>
<column name="orderNumber"/>
</many-to-one>
<many-to-one name="product"
insert="false"
update="false"
not-null="true"
column="productId"/>
</class>
<class name="Product">
<synchronize table="LineItem"/>
<id name="productId"
length="10">
<generator class="assigned"/>
</id>
<property name="description"
not-null="true"
length="200"/>
<property name="price" length="3"/>
<property name="numberAvailable"/>
<property name="numberOrdered">
<formula>
( select sum(li.quantity)
from LineItem li
where li.productId = productId )
</formula>
</property>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-composite-key-manytomany">
<title>Many-to-many with shared composite key attribute</title>
<programlisting><![CDATA[<class name="User" table="`User`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<set name="groups" table="UserGroup">
<key>
<column name="userName"/>
<column name="org"/>
</key>
<many-to-many class="Group">
<column name="groupName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
<class name="Group" table="`Group`">
<composite-id>
<key-property name="name"/>
<key-property name="org"/>
</composite-id>
<property name="description"/>
<set name="users" table="UserGroup" inverse="true">
<key>
<column name="groupName"/>
<column name="org"/>
</key>
<many-to-many class="User">
<column name="userName"/>
<formula>org</formula>
</many-to-many>
</set>
</class>
]]></programlisting>
</sect2>
<sect2 id="example-mappings-content-discrimination">
<title>Content based discrimination</title>
<programlisting><![CDATA[<class name="Person"
discriminator-value="P">
<id name="id"
column="person_id"
unsaved-value="0">
<generator class="native"/>
</id>
<discriminator
type="character">
<formula>
case
when title is not null then 'E'
when salesperson is not null then 'C'
else 'P'
end
</formula>
</discriminator>
<property name="name"
not-null="true"
length="80"/>
<property name="sex"
not-null="true"
update="false"/>
<component name="address">
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</component>
<subclass name="Employee"
discriminator-value="E">
<property name="title"
length="20"/>
<property name="salary"/>
<many-to-one name="manager"/>
</subclass>
<subclass name="Customer"
discriminator-value="C">
<property name="comments"/>
<many-to-one name="salesperson"/>
</subclass>
</class>]]></programlisting>
</sect2>
<sect2 id="example-mappings-association-alternatekeys" revision="2">
<title>Associations on alternate keys</title>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" length="100"/>
<one-to-one name="address"
property-ref="person"
cascade="all"
fetch="join"/>
<set name="accounts"
inverse="true">
<key column="userId"
property-ref="userId"/>
<one-to-many class="Account"/>
</set>
<property name="userId" length="8"/>
</class>
<class name="Address">
<id name="id">
<generator class="hilo"/>
</id>
<property name="address" length="300"/>
<property name="zip" length="5"/>
<property name="country" length="25"/>
<many-to-one name="person" unique="true" not-null="true"/>
</class>
<class name="Account">
<id name="accountId" length="32">
<generator class="uuid"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
<property name="type" not-null="true"/>
</class>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

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

View File

@ -0,0 +1,430 @@
<chapter id="example-weblog">
<title>Example: Weblog Application</title>
<sect1 id="example-weblog-classes">
<title>Persistent Classes</title>
<para>
The persistent classes represent a weblog, and an item posted
in a weblog. They are to be modelled as a standard parent/child
relationship, but we will use an ordered bag, instead of a set.
</para>
<programlisting><![CDATA[package eg;
import java.util.List;
public class Blog {
private Long _id;
private String _name;
private List _items;
public Long getId() {
return _id;
}
public List getItems() {
return _items;
}
public String getName() {
return _name;
}
public void setId(Long long1) {
_id = long1;
}
public void setItems(List list) {
_items = list;
}
public void setName(String string) {
_name = string;
}
}]]></programlisting>
<programlisting><![CDATA[package eg;
import java.text.DateFormat;
import java.util.Calendar;
public class BlogItem {
private Long _id;
private Calendar _datetime;
private String _text;
private String _title;
private Blog _blog;
public Blog getBlog() {
return _blog;
}
public Calendar getDatetime() {
return _datetime;
}
public Long getId() {
return _id;
}
public String getText() {
return _text;
}
public String getTitle() {
return _title;
}
public void setBlog(Blog blog) {
_blog = blog;
}
public void setDatetime(Calendar calendar) {
_datetime = calendar;
}
public void setId(Long long1) {
_id = long1;
}
public void setText(String string) {
_text = string;
}
public void setTitle(String string) {
_title = string;
}
}]]></programlisting>
</sect1>
<sect1 id="example-weblog-mappings">
<title>Hibernate Mappings</title>
<para>
The XML mappings should now be quite straightforward.
</para>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="Blog"
table="BLOGS">
<id
name="id"
column="BLOG_ID">
<generator class="native"/>
</id>
<property
name="name"
column="NAME"
not-null="true"
unique="true"/>
<bag
name="items"
inverse="true"
order-by="DATE_TIME"
cascade="all">
<key column="BLOG_ID"/>
<one-to-many class="BlogItem"/>
</bag>
</class>
</hibernate-mapping>]]></programlisting>
<programlisting><![CDATA[<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class
name="BlogItem"
table="BLOG_ITEMS"
dynamic-update="true">
<id
name="id"
column="BLOG_ITEM_ID">
<generator class="native"/>
</id>
<property
name="title"
column="TITLE"
not-null="true"/>
<property
name="text"
column="TEXT"
not-null="true"/>
<property
name="datetime"
column="DATE_TIME"
not-null="true"/>
<many-to-one
name="blog"
column="BLOG_ID"
not-null="true"/>
</class>
</hibernate-mapping>]]></programlisting>
</sect1>
<sect1 id="example-weblog-code">
<title>Hibernate Code</title>
<para>
The following class demonstrates some of the kinds of things
we can do with these classes, using Hibernate.
</para>
<programlisting><![CDATA[package eg;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class BlogMain {
private SessionFactory _sessions;
public void configure() throws HibernateException {
_sessions = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class)
.buildSessionFactory();
}
public void exportTables() throws HibernateException {
Configuration cfg = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class);
new SchemaExport(cfg).create(true, true);
}
public Blog createBlog(String name) throws HibernateException {
Blog blog = new Blog();
blog.setName(name);
blog.setItems( new ArrayList() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public BlogItem createBlogItem(Blog blog, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setBlog(blog);
item.setDatetime( Calendar.getInstance() );
blog.getItems().add(item);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public BlogItem createBlogItem(Long blogid, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Blog blog = (Blog) session.load(Blog.class, blogid);
item.setBlog(blog);
blog.getItems().add(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public void updateBlogItem(BlogItem item, String text)
throws HibernateException {
item.setText(text);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public void updateBlogItem(Long itemid, String text)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
item.setText(text);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public List listAllBlogNamesAndItemCounts(int max)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"select blog.id, blog.name, count(blogItem) " +
"from Blog as blog " +
"left outer join blog.items as blogItem " +
"group by blog.name, blog.id " +
"order by max(blogItem.datetime)"
);
q.setMaxResults(max);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
public Blog getBlogAndAllItems(Long blogid)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
Blog blog = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"left outer join fetch blog.items " +
"where blog.id = :blogid"
);
q.setParameter("blogid", blogid);
blog = (Blog) q.uniqueResult();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public List listBlogsAndRecentItems() throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"inner join blog.items as blogItem " +
"where blogItem.datetime > :minDate"
);
Calendar cal = Calendar.getInstance();
cal.roll(Calendar.MONTH, false);
q.setCalendar("minDate", cal);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,146 @@
<chapter id="filters">
<title>Filtering data</title>
<para>
Hibernate3 provides an innovative new approach to handling data with "visibility" rules.
A <emphasis>Hibernate filter</emphasis> is a global, named, parameterized filter that may be
enabled or disabled for a particular Hibernate session.
</para>
<sect1 id="objectstate-filters" revision="1">
<title>Hibernate filters</title>
<para>
Hibernate3 adds the ability to pre-define filter criteria and attach those filters at both
a class and a collection level. A filter criteria is the ability to define a restriction clause
very similiar to the existing "where" attribute available on the class and various collection
elements. Except these filter conditions can be parameterized. The application can then make
the decision at runtime whether given filters should be enabled and what their parameter
values should be. Filters can be used like database views, but parameterized inside the
application.
</para>
<para>
In order to use filters, they must first be defined and then attached to the appropriate
mapping elements. To define a filter, use the <literal>&lt;filter-def/&gt;</literal> element
within a <literal>&lt;hibernate-mapping/&gt;</literal> element:
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>]]></programlisting>
<para>
Then, this filter can be attached to a class:
</para>
<programlisting><![CDATA[<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>]]></programlisting>
<para>
or, to a collection:
</para>
<programlisting><![CDATA[<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>]]></programlisting>
<para>
or, even to both (or multiples of each) at the same time.
</para>
<para>
The methods on <literal>Session</literal> are: <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, and <literal>disableFilter(String filterName)</literal>.
By default, filters are <emphasis>not</emphasis> enabled for a given session; they must be explcitly
enabled through use of the <literal>Session.enabledFilter()</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.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
]]></programlisting>
<para>
In the HQL above, even though we only explicitly mentioned a salary constraint on the results,
because of the enabled filter the query will return only currently active employees who have
a salary greater than a million dollars.
</para>
<para>
Note: if you plan on using filters with outer joining (either through HQL or load fetching) be
careful of the direction of the condition expression. Its safest to set this up for left
outer joining; in general, place the parameter first followed by the column name(s) after
the operator.
</para>
<para>
After being defined a filter might be attached to multiple entities and/or
collections each with its own condition. That can be tedious when the
conditions are the same each time. Thus <literal>&lt;filter-def/&gt;</literal>
allows defining a default condition, either as an attribute or CDATA:
</para>
<programlisting><![CDATA[<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
<filter-def name="myOtherFilter">abc=xyz</filter-def>]]></programlisting>
<para>
This default condition will then be used whenever the filter is attached to something
without specifying a condition. Note that this means you can give a specific condition
as part of the attachment of the filter which overrides the default condition in that
particular case.
</para>
</sect1>
</chapter>

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,531 @@
<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>
<para>
TODO: Document user-extension framework in the property and proxy packages
</para>
</chapter>

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,758 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<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>ResultSetMetdata</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>
<p>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.</p>
</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 specificed 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 explicity, but
even in this case we let Hibernate inject the SQL column aliases for
each property. The placeholder for a column alias is just the property
name qualified by the table alias. In the following example, we retrieve
Cats and their mothers from a different table (cat_log) to the one
declared in the mapping metadata. Notice that we may even use the
property aliases in the where clause if we like.</para>
<programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
]]></programlisting>
<sect3 id="querysql-aliasreferences" revision="2">
<title>Alias and property references</title>
<para>For most cases the above alias injection is needed, but for
queries relating to more complex mappings like composite properties,
inheritance discriminators, collections etc. there are some specific
aliases to use to allow Hibernate to inject the proper aliases.</para>
<para>The following table shows the different possibilities of using
the alias injection. Note: the alias names in the result are examples,
each alias will have a unique and probably different name when
used.</para>
<table frame="topbot" id="aliasinjection-summary">
<title>Alias injection names</title>
<tgroup cols="3">
<colspec colwidth="1*" />
<colspec colwidth="1*" />
<colspec colwidth="2.5*" />
<thead>
<row>
<entry>Description</entry>
<entry>Syntax</entry>
<entry>Example</entry>
</row>
</thead>
<tbody>
<row>
<entry>A simple property</entry>
<entry><literal>{[aliasname].[propertyname]</literal></entry>
<entry><literal>A_NAME as {item.name}</literal></entry>
</row>
<row>
<entry>A composite property</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
<entry><literal>CURRENCY as {item.amount.currency}, VALUE as
{item.amount.value}</literal></entry>
</row>
<row>
<entry>Discriminator of an entity</entry>
<entry><literal>{[aliasname].class}</literal></entry>
<entry><literal>DISC as {item.class}</literal></entry>
</row>
<row>
<entry>All properties of an entity</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{item.*}</literal></entry>
</row>
<row>
<entry>A collection key</entry>
<entry><literal>{[aliasname].key}</literal></entry>
<entry><literal>ORGID as {coll.key}</literal></entry>
</row>
<row>
<entry>The id of an collection</entry>
<entry><literal>{[aliasname].id}</literal></entry>
<entry><literal>EMPID as {coll.id}</literal></entry>
</row>
<row>
<entry>The element of an collection</entry>
<entry><literal>{[aliasname].element}</literal></entry>
<entry><literal>XID as {coll.element}</literal></entry>
</row>
<row>
<entry>roperty of the element in the collection</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
<entry><literal>NAME as {coll.element.name}</literal></entry>
</row>
<row>
<entry>All properties of the element in the collection</entry>
<entry><literal>{[aliasname].element.*}</literal></entry>
<entry><literal>{coll.element.*}</literal></entry>
</row>
<row>
<entry>All properties of the the collection</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{coll.*}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
</sect2>
<sect2>
<title>Returning non-managed entities</title>
<para>It is possible to apply a ResultTransformer to native sql queries. Allowing it to e.g. return non-managed entities.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>a result transformer</para>
</listitem>
</itemizedlist>
<para>
The above query will return a list of <literal>CatDTO</literal> which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding
properties or fields.
</para>
</sect2>
<sect2>
<title>Handling inheritance</title>
<para>Native sql queries which query for entities that is mapped as part
of an inheritance must include all properties for the baseclass and all
it subclasses.</para>
</sect2>
<sect2>
<title>Parameters</title>
<para>Native sql queries support positional as well as named
parameters:</para>
<programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
</sect2>
</sect1>
<sect1 id="querysql-namedqueries" revision="3">
<title>Named SQL queries</title>
<para>Named SQL queries may be defined in the mapping document and called
in exactly the same way as a named HQL query. In this case, we do
<emphasis>not</emphasis> need to call
<literal>addEntity()</literal>.</para>
<programlisting><![CDATA[<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<programlisting><![CDATA[List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();]]></programlisting>
<para>The <literal>&lt;return-join&gt;</literal> and
<literal>&lt;load-collection&gt;</literal> elements are used to join
associations and define queries which initialize collections,
respectively.</para>
<programlisting><![CDATA[<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<para>A named SQL query may return a scalar value. You must declare the
column alias and Hibernate type using the
<literal>&lt;return-scalar&gt;</literal> element:</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>]]></programlisting>
<para>You can externalize the resultset mapping informations in a
<literal>&lt;resultset&gt;</literal> element to either reuse them accross
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},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<para>You can alternatively use the resultset mapping information in your
hbm files directly in java code.</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();]]></programlisting>
<sect2 id="propertyresults">
<title>Using return-property to explicitly specify column/alias
names</title>
<para>With <literal>&lt;return-property&gt;</literal> you can explicitly
tell Hibernate what column aliases to use, instead of using the
<literal>{}</literal>-syntax to let Hibernate inject its own
aliases.</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
]]></programlisting>
<para><literal>&lt;return-property&gt;</literal> also works with
multiple columns. This solves a limitation with the
<literal>{}</literal>-syntax which can not allow fine grained control of
multi-column properties.</para>
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>]]></programlisting>
<para>Notice that in this example we used
<literal>&lt;return-property&gt;</literal> in combination with the
<literal>{}</literal>-syntax for injection. Allowing users to choose how
they want to refer column and properties.</para>
<para>If your mapping has a discriminator you must use
<literal>&lt;return-discriminator&gt;</literal> to specify the
discriminator column.</para>
</sect2>
<sect2 id="sp_query" revision="1">
<title>Using stored procedures for querying</title>
<para>Hibernate 3 introduces support for queries via stored procedures
and functions. Most of the following documentation is equivalent for
both. The stored procedure/function must return a resultset as the first
out-parameter to be able to work with Hibernate. An example of such a
stored function in Oracle 9 and higher is as follows:</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;]]></programlisting>
<para>To use this query in Hibernate you need to map it via a named
query.</para>
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
</sql-query>]]></programlisting>
<para>Notice stored procedures currently only return scalars and
entities. <literal>&lt;return-join&gt;</literal> and
<literal>&lt;load-collection&gt;</literal> are not supported.</para>
<sect3 id="querysql-limits-storedprocedures" revision="1">
<title>Rules/limitations for using stored procedures</title>
<para>To use stored procedures with Hibernate the procedures/functions
have to follow some rules. If they do not follow those rules they are
not usable with Hibernate. If you still want to use these procedures
you have to execute them via <literal>session.connection()</literal>.
The rules are different for each database, since database vendors have
different stored procedure semantics/syntax.</para>
<para>Stored procedure queries can't be paged with
<literal>setFirstResult()/setMaxResults()</literal>.</para>
<para>Recommended call form is standard SQL92: <literal>{ ? = call
functionName(&lt;parameters&gt;) }</literal> or <literal>{ ? = call
procedureName(&lt;parameters&gt;}</literal>. Native call syntax is not
supported.</para>
<para>For Oracle the following rules apply:</para>
<itemizedlist spacing="compact">
<listitem>
<para>A function must return a result set. The first parameter of
a procedure must be an <literal>OUT</literal> that returns a
result set. This is done by using a
<literal>SYS_REFCURSOR</literal> type in Oracle 9 or 10. In Oracle
you need to define a <literal>REF CURSOR</literal> type, see
Oracle literature.</para>
</listitem>
</itemizedlist>
<para>For Sybase or MS SQL server the following rules apply:</para>
<itemizedlist spacing="compact">
<listitem>
<para>The procedure must return a result set. Note that since
these servers can/will return multiple result sets and update
counts, Hibernate will iterate the results and take the first
result that is a result set as its return value. Everything else
will be discarded.</para>
</listitem>
<listitem>
<para>If you can enable <literal>SET NOCOUNT ON</literal> in your
procedure it will probably be more efficient, but this is not a
requirement.</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</sect1>
<sect1 id="querysql-cud">
<title>Custom SQL for create, update and delete</title>
<para>Hibernate3 can use custom SQL statements for create, update, and
delete operations. The class and collection persisters in Hibernate
already contain a set of configuration time generated strings (insertsql,
deletesql, updatesql etc.). The mapping tags
<literal>&lt;sql-insert&gt;</literal>,
<literal>&lt;sql-delete&gt;</literal>, and
<literal>&lt;sql-update&gt;</literal> override these strings:</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>]]></programlisting>
<para>The SQL is directly executed in your database, so you are free to
use any dialect you like. This will of course reduce the portability of
your mapping if you use database specific SQL.</para>
<para>Stored procedures are supported if the <literal>callable</literal>
attribute is set:</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>The order of the positional parameters are currently vital, as they
must be in the same sequence as Hibernate expects them.</para>
<para>You can see the expected order by enabling debug logging for the
<literal>org.hibernate.persister.entity</literal> level. With this level
enabled Hibernate will print out the static SQL that is used to create,
update, delete etc. entities. (To see the expected sequence, remember to
not include your custom SQL in the mapping files as that will override the
Hibernate generated static sql.)</para>
<para>The stored procedures are in most cases (read: better do it than
not) required to return the number of rows inserted/updated/deleted, as
Hibernate has some runtime checks for the success of the statement.
Hibernate always registers the first statement parameter as a numeric
output parameter for the CUD operations:</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
<title>Custom SQL for loading</title>
<para>You may also declare your own SQL (or HQL) queries for entity
loading:</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
</sql-query>]]></programlisting>
<para>This is just a named query declaration, as discussed earlier. You
may reference this named query in a class mapping:</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>This even works with stored procedures.</para>
<para>You may even define a query for collection loading:</para>
<programlisting><![CDATA[<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>]]></programlisting>
<programlisting><![CDATA[<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>]]></programlisting>
<para>You could even define an entity loader that loads a collection by
join fetching:</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>]]></programlisting>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,286 @@
<chapter id="xml">
<title>XML Mapping</title>
<para><emphasis>
Note that this is an experimental feature in Hibernate 3.0 and is under
extremely active development.
</emphasis></para>
<sect1 id="xml-intro" revision="1">
<title>Working with XML data</title>
<para>
Hibernate lets you work with persistent XML data in much the same way
you work with persistent POJOs. A parsed XML tree can be thought of
as just another way to represent the relational data at the object level,
instead of POJOs.
</para>
<para>
Hibernate supports dom4j as API for manipulating XML trees. You can write
queries that retrieve dom4j trees from the database and have any
modification you make to the tree automatically synchronized to the
database. You can even take an XML document, parse it using dom4j, and
write it to the database with any of Hibernate's basic operations:
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
(merging is not yet supported).
</para>
<para>
This feature has many applications including data import/export,
externalization of entity data via JMS or SOAP and XSLT-based reporting.
</para>
<para>
A single mapping may be used to simultaneously map properties of a class
and nodes of an XML document to the database, or, if there is no class to map,
it may be used to map just the XML.
</para>
<sect2 id="xml-intro-mapping">
<title>Specifying XML and class mapping together</title>
<para>
Here is an example of mapping a POJO and XML simultaneously:
</para>
<programlisting><![CDATA[<class name="Account"
table="ACCOUNTS"
node="account">
<id name="accountId"
column="ACCOUNT_ID"
node="@id"/>
<many-to-one name="customer"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"/>
<property name="balance"
column="BALANCE"
node="balance"/>
...
</class>]]></programlisting>
</sect2>
<sect2 id="xml-onlyxml">
<title>Specifying only an XML mapping</title>
<para>
Here is an example where there is no POJO class:
</para>
<programlisting><![CDATA[<class entity-name="Account"
table="ACCOUNTS"
node="account">
<id name="id"
column="ACCOUNT_ID"
node="@id"
type="string"/>
<many-to-one name="customerId"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"
entity-name="Customer"/>
<property name="balance"
column="BALANCE"
node="balance"
type="big_decimal"/>
...
</class>]]></programlisting>
<para>
This mapping allows you to access the data as a dom4j tree, or as a graph of
property name/value pairs (java <literal>Map</literal>s). The property names
are purely logical constructs that may be referred to in HQL queries.
</para>
</sect2>
</sect1>
<sect1 id="xml-mapping" revision="1">
<title>XML mapping metadata</title>
<para>
Many Hibernate mapping elements accept the <literal>node</literal> attribute.
This let's you specify the name of an XML attribute or element that holds the
property or entity data. The format of the <literal>node</literal> attribute
must be one of the following:
</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>"element-name"</literal> - map to the named XML element</para>
</listitem>
<listitem>
<para><literal>"@attribute-name"</literal> - map to the named XML attribute</para>
</listitem>
<listitem>
<para><literal>"."</literal> - map to the parent element</para>
</listitem>
<listitem>
<para>
<literal>"element-name/@attribute-name"</literal> -
map to the named attribute of the named element
</para>
</listitem>
</itemizedlist>
<para>
For collections and single valued associations, there is an additional
<literal>embed-xml</literal> attribute. If <literal>embed-xml="true"</literal>,
the default, the XML tree for the associated entity (or collection of value type)
will be embedded directly in the XML tree for the entity that owns the association.
Otherwise, if <literal>embed-xml="false"</literal>, then only the referenced
identifier value will appear in the XML for single point associations and
collections will simply not appear at all.
</para>
<para>
You should be careful not to leave <literal>embed-xml="true"</literal> for
too many associations, since XML does not deal well with circularity!
</para>
<programlisting><![CDATA[<class name="Customer"
table="CUSTOMER"
node="customer">
<id name="id"
column="CUST_ID"
node="@id"/>
<map name="accounts"
node="."
embed-xml="true">
<key column="CUSTOMER_ID"
not-null="true"/>
<map-key column="SHORT_DESC"
node="@short-desc"
type="string"/>
<one-to-many entity-name="Account"
embed-xml="false"
node="account"/>
</map>
<component name="name"
node="name">
<property name="firstName"
node="first-name"/>
<property name="initial"
node="initial"/>
<property name="lastName"
node="last-name"/>
</component>
...
</class>]]></programlisting>
<para>
in this case, we have decided to embed the collection of account ids, but not
the actual account data. The following HQL query:
</para>
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
<para>
Would return datasets such as this:
</para>
<programlisting><![CDATA[<customer id="123456789">
<account short-desc="Savings">987632567</account>
<account short-desc="Credit Card">985612323</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>]]></programlisting>
<para>
If you set <literal>embed-xml="true"</literal> on the <literal>&lt;one-to-many&gt;</literal>
mapping, the data might look more like this:
</para>
<programlisting><![CDATA[<customer id="123456789">
<account id="987632567" short-desc="Savings">
<customer id="123456789"/>
<balance>100.29</balance>
</account>
<account id="985612323" short-desc="Credit Card">
<customer id="123456789"/>
<balance>-2370.34</balance>
</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>]]></programlisting>
</sect1>
<sect1 id="xml-manipulation" revision="1">
<title>Manipulating XML data</title>
<para>
Let's rearead and update XML documents in the application. We do this by
obtaining a dom4j session:
</para>
<programlisting><![CDATA[Document doc = ....;
Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
List results = dom4jSession
.createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
.list();
for ( int i=0; i<results.size(); i++ ) {
//add the customer data to the XML document
Element customer = (Element) results.get(i);
doc.add(customer);
}
tx.commit();
session.close();]]></programlisting>
<programlisting><![CDATA[Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
Element cust = (Element) dom4jSession.get("Customer", customerId);
for ( int i=0; i<results.size(); i++ ) {
Element customer = (Element) results.get(i);
//change the customer name in the XML and database
Element name = customer.element("name");
name.element("first-name").setText(firstName);
name.element("initial").setText(initial);
name.element("last-name").setText(lastName);
}
tx.commit();
session.close();]]></programlisting>
<para>
It is extremely useful to combine this feature with Hibernate's <literal>replicate()</literal>
operation to implement XML-based data import/export.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,97 @@
A {
color: #003399;
}
A:active {
color: #003399;
}
A:visited {
color: #888888;
}
P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
color: #000000;
}
TD, TH, SPAN {
color: #000000;
}
BLOCKQUOTE {
margin-right: 0px;
}
H1, H2, H3, H4, H5, H6 {
color: #000000;
font-weight:500;
margin-top:10px;
padding-top:15px;
}
TABLE {
border-collapse: collapse;
border-spacing:0;
border: 1px thin black;
empty-cells: hide;
}
TD {
padding: 4pt;
}
H1 { font-size: 150%; }
H2 { font-size: 140%; }
H3 { font-size: 110%; font-weight: bold; }
H4 { font-size: 110%; font-weight: bold;}
H5 { font-size: 100%; font-style: italic; }
H6 { font-size: 100%; font-style: italic; }
TT {
font-size: 90%;
font-family: "Courier New", Courier, monospace;
color: #000000;
}
PRE {
font-size: 100%;
padding: 5px;
border-style: solid;
border-width: 1px;
border-color: #CCCCCC;
background-color: #F4F4F4;
}
UL, OL, LI {
list-style: disc;
}
HR {
width: 100%;
height: 1px;
background-color: #CCCCCC;
border-width: 0px;
padding: 0px;
color: #CCCCCC;
}
.variablelist {
padding-top: 10;
padding-bottom:10;
margin:0;
}
.itemizedlist, UL {
padding-top: 0;
padding-bottom:0;
margin:0;
}
.term {
font-weight:bold;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,429 @@
<?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>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,334 @@
<?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>

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -0,0 +1,250 @@
<?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>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-manual</artifactId>
<version>3.3.0.beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hibernate-manual-${translation}</artifactId>
<packaging>pom</packaging>
<name>Hibernate Manual (${translation})</name>
<properties>
<translation>es-ES</translation>
</properties>
</project>

View File

@ -0,0 +1,203 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
"../support/docbook-dtd/docbookx.dtd"
[
<!ENTITY quickstart SYSTEM "modules/quickstart.xml">
<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
<!ENTITY architecture SYSTEM "modules/architecture.xml">
<!ENTITY configuration SYSTEM "modules/configuration.xml">
<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
<!ENTITY session-api SYSTEM "modules/session_api.xml">
<!ENTITY transactions SYSTEM "modules/transactions.xml">
<!ENTITY events SYSTEM "modules/events.xml">
<!ENTITY batch SYSTEM "modules/batch.xml">
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
<!ENTITY filters SYSTEM "modules/filters.xml">
<!ENTITY xml SYSTEM "modules/xml.xml">
<!ENTITY performance SYSTEM "modules/performance.xml">
<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
]>
<book lang="es">
<bookinfo>
<title>HIBERNATE - Persistencia Relacional para Java Idiom&#x00e1;tico</title>
<subtitle>Documentaci&#x00f3;n de Referencia de Hibernate</subtitle>
<releaseinfo>3.0.5</releaseinfo>
</bookinfo>
<toc/>
<preface id="preface" revision="2">
<title>Prefacio</title>
<para>
Advertencia! Esta es una versi&#x00f3;n traducida del ingl&#x00e9;s de
la documentaci&#x00e9;n de referencia de Hibernate. La versi&#x00f3;n
traducida puede no estar actualizada! Sin embargo, las diferencias
deber&#x00ed;an ser s&#x00f3;lo menores. Consulta la documentaci&#x00f3;n
de referencia en ingl&#x00e9;s si est&#x00e1;s perdiendo informaci&#x00f3;n
o encuentras alg&#x00fa;n error de traducci&#x00f3;n. Si quieres colaborar con
una traducci&#x00f3;n en particular, cont&#x00e1;ctanos en la lista de correo
de desarrolladores de Hibernate.
</para>
<para>
Traductor(es): Bernardo Antonio Buffa Colom&#x00e9; &lt;kreimer@bbs.frc.utn.edu.ar&gt;
<!--,
Antonio L&#x00f3;pez Gota &lt;antoniogota@gmail.com&gt; -->
</para>
<para>
Trabajar con software orientado a objetos y una base de datos relacional puede ser
inc&#x00f3;modo y consumir tiempo en los entornos de empresa de hoy. Hibernate es una
herramienta de mapeo objeto/relacional para entornos Java. El t&#x00e9;rmino mapeo
objeto/relacional (MOR) hace referencia a la t&#x00e9;cnica de mapear una
representaci&#x00f3;n de datos desde un modelo de objetos a un modelo de datos relacional
con un esquema basado en SQL.
</para>
<para>
Hibernate no s&#x00f3;lo se encarga de mapear de clases Java a tablas de base de datos
(y de tipos de datos de Java a tipos de datos SQL), sino que tambi&#x00e9;n provee
facilidades de consulta y recuperaci&#x00f3;n de datos y puede reducir significativamente
el tiempo de desarrollo que de otra forma se gasta en el manejo de los datos en SQL y JDBC.
</para>
<para>
La meta de Hibernate es relevar al desarrollador del 95 por ciento de las tareas comunes
relacionadas a la programaci&#x00f3;n de la persistencia de los datos.
Hibernate puede no ser la mejor soluci&#x00f3;n para aplicaciones que usan solamente
procedimientos almacenados para implementar la l&#x00f3;gica de negocio en la base de
datos, es mas &#x00fa;til con modelos de dominio orientados a objetos y l&#x00f3;gica de
negocio en middle-tier basada en Java. Sin embargo, Hibernate ciertamente puede ayudarte
a quitar o encapsular c&#x00f3;digo SQL espec&#x00ed;fico de vendedor y ayudar&#x00e1;
con la tarea com&#x00fa;n de traducci&#x00f3;n de resultados desde una representaci&#x00f3;n
tabular a un grafo de objetos.
</para>
<para>
Si eres nuevo en Hibernate y lo del Mapeo Objeto/Relacional o incluso en Java,
sigue por favor estos pasos:
</para>
<orderedlist>
<listitem>
<para>
Lee <xref linkend="quickstart"/> para un tutorial de 30 minutos, usando Tomcat.
</para>
</listitem>
<listitem>
<para>
Lee <xref linkend="architecture"/> para entender los entornos en los que
puede ser usado Hibernate.
</para>
</listitem>
<listitem>
<para>
Dale una mirada al directorio <literal>eg/</literal> en la distribuci&#x00f3;n
de Hibernate, contiene una aplicaci&#x00f3;n independiente simple.
Copia tu driver JDBC al directorio <literal>lib/</literal> y edita
<literal>etc/hibernate.properties</literal>, especificando los valores
correctos para tu base de datos. Desde l&#x00ed;nea de comandos en el
directorio de la distribuci&#x00f3;n, tipea <literal>ant eg</literal>
(usando Ant), o bajo Windows, tipea <literal>build eg</literal>.
</para>
</listitem>
<listitem>
<para>
Usa esta documentaci&#x00f3;n de referencia como tu fuente de informaci&#x00f3;n
primaria. Ten en consideraci&#x00f3;n leer <emphasis>Hibernate in Action</emphasis>
(http://www.manning.com/bauer) si necesitas mas ayuda con el dise&#x00f1;o
de aplicaciones o si prefieres un tutorial paso a paso.
Visita tambi&#x00e9;n http://caveatemptor.hibernate.org y descarga la aplicaci&#x00f3;n
de ejemplo para Hibernate in Action.
</para>
</listitem>
<listitem>
<para>
Los FAQs son respondidos en el sitio web de Hibernate.
</para>
</listitem>
<listitem>
<para>
En el sitio web de Hibernate hay enlaces a demos de terceros, ejemplos
y tutoriales.
</para>
</listitem>
<listitem>
<para>
El Area de Comunidad en el sitio web de Hibernate es una buena fuente
de patrones de dise&#x00f1;o y varias soluciones de integraci&#x00f3;n
(Tomcat, JBoss, Struts, EJB, etc.).
</para>
</listitem>
</orderedlist>
<para>
Si tienes preguntas, usa el foro de usuarios enlazado en el sitio web de Hibernate.
Tambi&#x00e9;n proveemos un sistema de seguimiento JIRA para reportes de defectos y
peticiones de nuevas caracter&#x00ed;sticas.
Si estas interesado en el desarrollo de Hibernate, &#x00fa;nete a la lista de correo
de desarrolladores. Si estas interesado en traducir esta documentaci&#x00f3;n a tu
lenguaje, cont&#x00e1;ctanos en la lista de correo de desarrolladores.
</para>
<para>
A trav&#x00e9;s de JBoss Inc. (see http://www.hibernate.org/SupportTraining/) hay
disponibilidad de soporte comercial de desarrollo, soporte de producci&#x00f3;n y
entrenamiento en Hibernate.
Hibernate es un proyecto de la suite de productos de c&#x00f3;digo abierto
JBoss Professional.
</para>
</preface>
&quickstart;
&tutorial;
&architecture;
&configuration;
&persistent-classes;
&basic-mapping;
&collection-mapping;
&association-mapping;
&component-mapping;
&inheritance-mapping;
&session-api;
&transactions;
&events;
&batch;
&query-hql;
&query-criteria;
&query-sql;
&filters;
&xml;
&performance;
&toolset-guide;
&example-parentchild;
&example-weblog;
&example-mappings;
&best-practices;
</book>

View File

@ -0,0 +1,279 @@
<chapter id="architecture">
<title>Arquitectura</title>
<sect1 id="architecture-overview" revision="1">
<title>Visi&#x00f3;n General</title>
<para>
Una visi&#x00f3;n a (muy) alto nivel de la arquitectura de Hibernate:
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/overview.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
Este diagrama muestra a Hibernate usando la base de datos y los datos de
configuraci&#x00f3;n para proveer servicios de persistencia (y objetos
persistentes) a la aplicaci&#x00f3;n.
</para>
<para>
Nos gustar&#x00ed;a mostrar una vista m&#x00e1;s detallada de la arquitectura de tiempo
de ejecuci&#x00f3;n. Desafortunadamente, Hibernate es flexible y soporta diferentes
enfoques. Mostraremos los dos extremos. En la arquitectura "sencilla", es la
aplicaci&#x00f3;n la que provee su propias conexiones JDBC y gestiona sus propias
transacciones. Este enfoque usa un m&#x00ed;nimo subconjunto de la API de Hibernate:
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/lite.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
La arquitectura "full cream" abstrae a la aplicaci&#x00f3;n de las APIs
de JDBC/JTA y deja que Hibernate se encargue de los detalles.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
He aqu&#x00ed; algunas definiciones de los objetos en los diagramas:
<variablelist spacing="compact">
<varlistentry>
<term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
<listitem>
<para>
Cach&#x00e9; threadsafe (inmutable) de mapeos compilados para
una sola base de datos. Es una f&#x00e1;brica de <literal>Session</literal>
y un cliente de <literal>ConnectionProvider</literal>. Opcionalmente,
puede mantener una cach&#x00e9; (de segundo nivel) de datos reusables
entre transacciones, a un nivel de proceso o de cluster.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Session (<literal>org.hibernate.Session</literal>)</term>
<listitem>
<para>
Objeto mono-hebra, de corta vida que representa una conversaci&#x00f3;n
entre la aplicaci&#x00f3;n y el almacenamiento persistente. Envuelve una
conexi&#x00f3;n JDBC. Es una f&#x00e1;brica de <literal>Transaction</literal>.
Mantiene una cach&#x00e9; requerida (de primer nivel) de objetos persistentes,
usada mientras se navega el grafo de objetos o se recuperen objetos por
identificador.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Objetos y colecciones persistentes</term>
<listitem>
<para>
Objetos de corta vida, mono-hebra conteniendo estado persistente y
funci&#x00f3;nalidad de negocio. Estos pueden ser JavaBeans/POJOs
(Plain Old Java Objects, o sea, cualquier objeto Java), la &#x00fa;nica
cosa especial en ellos es que estan asociados actualmente con una
(y s&#x00f3;lo una) <literal>Session</literal>. Tan pronto como la
<literal>Session</literal> sea cerrada, ser&#x00e1;n separados y
estar&#x00e1;n libres para ser usados en cualquier capa de aplicaci&#x00f3;n.
(por ejemplo, directamente como objetos de transferencia de datos hacia
y desde la capa de presentaci&#x00f3;n).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Objetos y colecciones transitorios y separados</term>
<listitem>
<para>
Instancias de clases persistentes que no estan acutualmente asociadas
con una <literal>Session</literal>. Pueden haber sido instanciadas por
la aplicaci&#x00f3;n y (a&#x00fa;n) no haber sido hechas persistentes,
o pueden haber sido instanciadas por una <literal>Session</literal> cerrada.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
<listitem>
<para>
(Opcional) Un objeto de corta vida, mono-hebra, usado por la aplicaci&#x00f3;n
para especificar unidades at&#x00f3;micas de trabajo. Abstrae a la aplicaci&#x00f3;n
de las subyacentes transacciones JDBC, JTA o CORBA. En algunos casos, una
<literal>Session</literal> puede extenderse sobre varias <literal>Transaction</literal>s.
Sin embargo, la demarcaci&#x00f3;n de la transacci&#x00f3;n, ya sea usando la API
subyacente o <literal>Transaction</literal>, nunca es opcional!
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
<listitem>
<para>
(Opcional) Una f&#x00e1;brica (y pool) de conexiones JDBC. Abstrae a la aplicaci&#x00f3;n
del <literal>Datasource</literal> o <literal>DriverManager</literal> subyacente.
No se expone a la aplicaci&#x00f3;n, pero puede ser extendido/implementado por
el desarrollador.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
<listitem>
<para>
(Opcional) Una f&#x00e1;brica de instancias de <literal>Transaction</literal>.
No se expone a la aplicaci&#x00f3;n, pero puede ser extendido/implementado por
el desarrollador.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>Interfaces de Extensi&#x00f3;n</emphasis></term>
<listitem>
<para>
Hibernate ofrece muchas interfaces de extensi&#x00f3;n opcional que puedes
implementar para modificar a medida el comportamiento de tu capa de persistencia.
Para m&#x00e1;s detalles, mira la documentaci&#x00f3;n de la API.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Dada una arquitectura "sencilla", la aplicaci&#x00f3;n pasa por alto las APIs
de <literal>Transaction</literal>/<literal>TransactionFactory</literal> y/o
<literal>ConnectionProvider</literal>, para hablar directamente a JTA o JDBC.
</para>
</sect1>
<sect1 id="architecture-states" revision="1">
<title>Estados de instancia</title>
<para>
Una instancia de una clase persistente puede estar en uno de tres estados
diferentes, definidos respecto de su <emphasis>contexto de persistencia</emphasis>.
El objeto <literal>Session</literal> de Hibernate es el contexto de persistencia:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>transitorio</term>
<listitem>
<para>
La instancia no est&#x00e1; y nunca estuvo asociada con
un contexto de persistencia. No tiene identidad persistente
(valor de clave primaria).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>persistente</term>
<listitem>
<para>
La instancia est&#x00e1; actualmente asociada con un
contexto de persistencia. Tiene una identidad persistente
(valor de clave primaria) y, quiz&#x00e1;s, una fila
correspondiente en la base de datos. Para un contexto de
persistencia en particular, Hibernate <emphasis>garantiza</emphasis>
que la identidad persistente es equivalente a la identidad
Java (localizaci&#x00f3;n en memoria del objeto).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>separado</term>
<listitem>
<para>
La instancia estuvo una vez asociada con un contexto
de persistencia, pero ese contexto fue cerrado, o la
instancia fue serializada a otro proceso. Tiene una
identidad persistente y, quiz&#x00e1;s, una fila correspondiente
en la base de datos. Para las instancias separadas,
Hibernate no establece ninguna garant&#x00ed;a sobre
la relaci&#x00f3;n entre identidad persistente e identidad Java.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 id="architecture-jmx" revision="1">
<title>Integraci&#x00f3;n JMX</title>
<para>
JMX es el est&#x00e1;ndar J2EE para la gesti&#x00f3;n de componentes Java. Hibernate
puede ser gestionado por medio de un servicio est&#x00e1;ndar JMX.
Proveemos una implementaci&#x00f3;n de MBean en la distribuci&#x00f3;n,
<literal>org.hibernate.jmx.HibernateService</literal>.
</para>
<para>
Para ejemplo de c&#x00f3;mo desplegar Hibernate como un servicio JMX en un Servidor
de Aplicaciones JBoss, por favor, mira la Gu&#x00ed;a del Usuario de JBoss.
En JBoss AS, tienes adem&#x00e1;s estos beneficios si despliegas usando JMX:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Gesti&#x00f3;n de Sesi&#x00f3;n:</emphasis> El ciclo de vida de la <literal>Session</literal>
de Hibernate puede estar autom&#x00e1;ticamente ligado al &#x00e1;mbito de una transacci&#x00f3;n
JTA. Esto significa que ya no tienes que abrir ni cerrar la <literal>Session</literal> manualmente,
esto pasa a ser trabajo de un interceptor EJB de JBoss. Adem&#x00e1;s tampoco tienes
que preocuparte m&#x00e1;s de la demarcaci&#x00f3;n de la transacci&#x00f3;n (a menos que
que quieras escribir una capa de persitencia portable, por supuesto, usa la API de
<literal>Transaction</literal> de Hibernate para esto). Para acceder a una
<literal>Session</literal> llama al <literal>HibernateContext</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Despliegue de HAR:</emphasis> Usualmente despliegas el servicio JMX de Hibernate
usando un descriptor de despliegue de servicio de JBoss (en un fichero EAR y/o SAR), que soporta
todas las opciones de configuraci&#x00f3;n usuales de una <literal>SessionFactory</literal> de
Hibernate. Sin embargo, todav&#x00ed;a tienes que nombrar todos tus ficheros de mapeo en el
descriptor de despliegue. Si decides usar el depliegue de HAR opcional, JBoss detectar&#x00e1;
autom&#x00e1;ticamente todos los ficheros de mapeo en tu fichero HAR.
</para>
</listitem>
</itemizedlist>
<para>
Para m&#x00e1;s informaci&#x00f3;n sobre estas opciones, consulta la
Gu&#x00ed;a de Usuario del JBoss AS.
</para>
<para>
Otra funcionalidad disponible como un servicio JMX son las estad&#x00ed;sticas en
tiempo de ejecuci&#x00f3;n de Hibernate. Mira <xref linkend="configuration-optional-statistics"/>.
</para>
</sect1>
<sect1 id="architecture-jca" revision="1">
<title>Soporte JCA:</title>
<para>
Hiberate puede adem&#x00e1;s ser configurado como un conector JCA. Por favor mira el
sitio web para m&#x00e1;s detalles. Por favor ten en cuenta que el soporte de JCA
de Hibernate est&#x00e1; a&#x00fa;n considerado experimental.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,527 @@
<chapter id="associations">
<title>Mapeos de Asociaci&#x00f3;n</title>
<sect1 id="assoc-intro" revision="1">
<title>Introducci&#x00f3;n</title>
<para>
Los mapeos de asociaci&#x00f3;n son frecuentemente las cosas mas dif&#x00ed;ciles
de hacer correctamente. En esta secci&#x00f3;n iremos a trav&#x00e9;s de los casos
can&#x00f3;nicos uno a uno, comenzando con los mapeos unidireccionales, y considerando
luego los casos bidireccionales. Usaremos <literal>Person</literal> y <literal>Address</literal>
en todos los ejemplos.
</para>
<para>
Clasificaremos las asociaciones por cuanto mapeen o no a una tabla
de uni&#x00f3;n interviniente, y por su multiplicidad.
</para>
<para>
Las claves for&#x00e1;neas que aceptan valores nulos (en adelante, nullables)
no son consideradas una buena pr&#x00e1;ctica en el modelado tradicional de datos,
as&#x00ed; que todos nuestros ejemplos usan claves for&#x00e1;neas no nullables.
Esto no es un requerimiento de Hibernate, y todos los mapeos funcionar&#x00e1;n
si quitas las restricciones de nulabilidad.
</para>
</sect1>
<sect1 id="assoc-unidirectional" revision="1">
<title>Asociaciones Unidireccionales</title>
<sect2 id="assoc-unidirectional-m21">
<title>muchos a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n unidireccional muchos-a-uno</emphasis> es el tipo
m&#x00e1;s com&#x00fa;n de asociaciones unidireccionales.
</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>uno a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n unidireccional uno-a-uno en una clave primaria</emphasis>
es casi id&#x00e9;ntica. La &#x00fa;nica diferencia es la restricci&#x00f3;n de unicidad
de la columna.
</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>
Usualmente, una <emphasis>asociaci&#x00f3;n unidireccional uno-a-uno en una
clave primaria</emphasis> usa un generador de id especial. (Observa que hemos
invertido el sentido de la asociaci&#x00f3;n en este ejemplo).
</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>uno a muchos</title>
<para>
Una <emphasis>asociaci&#x00f3;n unidireccional uno-a-muchos en una clave for&#x00e1;nea</emphasis>
es un caso muy inusual, y realmente no est&#x00e1; recomendada.
</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>
Creemos que es mejor usar una tabla de uni&#x00f3;n para este tipo de asociaci&#x00f3;n.
</para>
</sect2>
</sect1>
<sect1 id="assoc-unidirectional-join" revision="1">
<title>Asociaciones unidireccionales con tablas de uni&#x00f3;n</title>
<sect2 id="assoc-unidirectional-join-12m">
<title>uno a muchos</title>
<para>
Una <emphasis>asociaci&#x00f3;n unidireccional uno-a-muchos en una tabla de uni&#x00f3;n</emphasis>
es m&#x00e1;s preferible. Observa que especificando <literal>unique="true"</literal>, hemos
cambiado la multiplicidad de muchos-a-muchos a uno-a-muchos.
</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>muchos a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n unidireccional muchos-a-uno en una tabla de uni&#x00f3;n</emphasis>
es bastante com&#x00fa;n cuando la asociaci&#x00f3;n es opcional.
</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>uno a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n unidireccional uno-a-uno en una tabla de uni&#x00f3;n</emphasis>
es inusual en extremo, pero posible.
</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>muchos a muchos</title>
<para>
Finalmente, tenemos una <emphasis>asociaci&#x00f3;n unidireccional muchos-a-muchos</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>Asociaciones Bidireccionales</title>
<sect2 id="assoc-bidirectional-m21">
<title>uno a muchos / muchos a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n bidireccional muchos-a-uno</emphasis> es
el tipo m&#x00e1;s com&#x00fa;n de asociaci&#x00f3;n. (Esta es la relaci&#x00f3;n
est&#x00e1;ndar padre/hijo.)
</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>
</sect2>
<sect2 id="assoc-bidirectional-121">
<title>uno a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n bidireccional uno-a-uno en una clave for&#x00e1;nea</emphasis>
es bastante com&#x00fa;n.
</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>
Una <emphasis>asociaci&#x00f3;n bidireccional uno-a-uno en una clave primaria</emphasis>
usa el generador de id especial.
</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>Asociaciones bidireccionales con tablas de uni&#x00f3;n</title>
<sect2 id="assoc-bidirectional-join-12m">
<title>uno a muchos / muchos a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n bidireccional uno-a-muchos en una tabla de uni&#x00f3;n</emphasis>.
Observa que el <literal>inverse="true"</literal> puede ir a cualquier lado de la asociaci&#x00f3;n,
en la colecci&#x00f3;n, o en la uni&#x00f3;n.
</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>uno a uno</title>
<para>
Una <emphasis>asociaci&#x00f3;n bidireccional uno-a-uno en una tabla de uni&#x00f3;n</emphasis>
es inusual en extremo, pero posible.
</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="address"
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">
<title>muchos a muchos</title>
<para>
Finalmente, tenemos una <emphasis>asociaci&#x00f3;n bidireccional muchos-a-muchos</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<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">
<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>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,192 @@
<chapter id="batch">
<title>Procesamiento por lotes</title>
<para>
Un enfoque ingenuo para insertar 100.000 filas en la base de datos usando Hibernate podría verse así:
</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>
Esto podría caer sobre una <literal>OutOfMemoryException</literal> en algún sitio
cerca de la fila 50.000. Esto es porque Hibernate tiene en caché todas las instancias
de <literal>Customer</literal> recién instanciadas en el caché de nivel de sesión.
</para>
<para>
En este capítulo te mostraremos cómo evitar este problema. Primero, sin embargo,
si estás haciendo procesamiento por lotes (batch processing), es absolutamente crítico
que habilites el uso de loteo JDBC, si pretendes lograr un rendimiento razonable.
Establece el tamaño de lote JDBC a un número razonable (digamos 10-50):
</para>
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
<para>
Podrías además querer hacer este tipo de trabajo en un proceso donde la interacción con el caché de
segundo nivel esté completamente deshabilitado:
</para>
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
<sect1 id="batch-inserts">
<title>Inserciones en lote</title>
<para>
Al hacer persistentes objetos nuevos, debes limpiar con <literal>flush()</literal> y
llamar a <literal>clear()</literal> en la sesión regularmente, para controlar el tamaño
del caché de primer nivel.
</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>Actualizaciones en lote</title>
<para>
Para recuperar y actualizar datos se aplican las mismas ideas. Adicionalmente, necesitas usar
<literal>scroll()</literal> para sacar ventaja de los cursores del lado del servidor en consultas
que devuelvan muchas filas de datos.
</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-direct">
<title>update/delete en masa</title>
<para>
Como ya se ha discutido, el mapeo objeto/relacional automático y transparente se refiere
al manejo de estado de objetos. Esto implica que el estado del objeto está disponible
en memoria, por lo tanto actualizar o borrar (usando <literal>UPDATE</literal> y
<literal>DELETE</literal> de SQL) datos directamente en la base de datos no afectará el
estado en memoria. Sin embargo, Hibernate provee métodos para la ejecución de sentencias
del estilo de <literal>UPDATE</literal> y <literal>DELETE</literal> de SQL que se realizan
a través del Lenguaje de Consulta de Hibernate (Hibernate Query Language o
<xref linkend="queryhql">HQL</xref>).
</para>
<para>
La pseudo-sintáxis para sentencias <literal>UPDATE</literal> y <literal>DELETE</literal> es:
<literal>( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?</literal>. Algunos puntos
a tener en cuenta:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
En la cláusula-from, la palabra clave FROM es opcional
</para>
</listitem>
<listitem>
<para>
Puede haber sólo una clase mencionada en la cláusula-from, y <emphasis>no puede</emphasis>
tener un alias.
</para>
</listitem>
<listitem>
<para>
No puede especificarse ningún join (bien implícito o explícito) en una consulta masiva de HQL.
Pueden usarse subconsultas en la cláusula-where.
</para>
</listitem>
<listitem>
<para>
La cláusula-where es también opcional.
</para>
</listitem>
</itemizedlist>
<para>
Como un ejemplo, para ejecutar un <literal>UPDATE</literal> HQL, usa el
método <literal>Query.executeUpdate()</literal>:
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
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>
Para ejecutar un <literal>DELETE</literal> HQL, usa el mismo método <literal>Query.executeUpdate()</literal>
(el método está nombrado para aquellos familiarizados con
<literal>PreparedStatement.executeUpdate()</literal> de JDBC):
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
<para>
El valor <literal>int</literal> devuelto por el método <literal>Query.executeUpdate()</literal>
indica el número de entidades afectadas por la operación. Considera que esto puede o no
correlacionarse al número de filas afectadas en la base de datos. Una operación masiva HQL podría
resultar en que se ejecuten múltiples sentencias de SQL reales, para joined-subclass, por ejemplo.
El número devuelto indica el número de entidades reales afectadas por la sentencia. Volviendo al
ejemplo de joined-subclass, un borrado contra una de las subclases puede resultar realmente en
borrados contra no sólo la tabla a la que está mapeada esa subclase, sino también la tabla "raíz"
y potencialmente tablas de joined-subclass más debajo en la jerarquía de herencia.
</para>
<para>
Ten en cuenta que existen actualmente unas pocas limitaciones con las operaciones HQL masivas,
que serán atendidas en lanzamientos futuros; consulta la hoja de ruta de JIRA para más detalles.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,229 @@
<chapter id="best-practices" revision="3">
<title>Mejores Pr&#x00e1;cticas</title>
<variablelist spacing="compact">
<varlistentry>
<term>Escribe clase finamente granularizadas y mapealas usando <literal>&lt;component&gt;</literal>.</term>
<listitem>
<para>
Usa una clase <literal>Direcci&#x00f3;n</literal> para encapsular <literal>calle</literal>,
<literal>distrito</literal>, <literal>estado</literal>, <literal>c&#x00f3;digo postal</literal>.
Esto alienta la reutilizaci&#x00f3;n de c&#x00f3;digo y simplifica el refactoring.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Declara las propiedades identificadoras en clases persistentes.</term>
<listitem>
<para>
Hibernate hace opcionales las propiedades identificadoras. Existen todo tipo de razones
por las que debes usarlas. Recomendamos que los identificadores sean 'sint&#x00e9;ticos'
(generados, sin ning&#x00fa;n significado de negocio).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Identifica las claves naturales.</term>
<listitem>
<para>
Identifica las claves naturales de todas las entidades, y mapealas usando
<literal>&lt;natural-id&gt;</literal>. Implementa <literal>equals()</literal> y
<literal>hashCode()</literal> para comparar las propiedades que componen la clave natural.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Coloca cada mapeo de clase en su propio fichero.</term>
<listitem>
<para>
No uses un solo documento monol&#x00ed;tico de mapeo. Mapea <literal>com.eg.Foo</literal> en
el fichero <literal>com/eg/Foo.hbm.xml</literal>. Esto tiene sentido particularmente en un
ambiente de equipo.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Carga los mapeos como recursos.</term>
<listitem>
<para>
Despliega los mapeos junto a las clases que mapean.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Considera externalizar las cadenas de consulta.</term>
<listitem>
<para>
Esta es una buena pr&#x00e1;ctica si tus consultas llaman a funciones SQL que no son del
est&#x00e1;ndar ANSI. Externalizar las cadenas de consulta a ficheros de mapeo har&#x00e1; la
aplicaci&#x00f3;n m&#x00e1;s portable.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Usa variables de ligado.</term>
<listitem>
<para>
Igual que en JDBC, siempre remplaza valores no constantes con "?". &#x00a1;Nunca uses manipulaci&#x00f3;n
de cadenas para ligar un valor no constante en una consulta! Incluso mejor, considera usar
par&#x00e1;metros con nombre en las consultas.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>No manejes tus propias conexiones JDBC.</term>
<listitem>
<para>
Hibernate deja a la aplicaci&#x00f3;n administre las conexiones JDBC. Este enfoque debe considerarse
como &#x00fa;ltimo recurso. Si no puedes usar los provedores de conexi&#x00f3;n prefabricados, considera
prover tu propia implementaci&#x00f3;n de <literal>org.hibernate.connection.ConnectionProvider</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Considera usar un tipo personalizado.</term>
<listitem>
<para>
Sup&#x00f3;n que tienes un tipo Java, digamos de alguna biblioteca, que necesita hacerse persistente
pero no provee los m&#x00e9;todos de acceso necesarios para mapearlo como un componente. Debes considerar
implementar <literal>org.hibernate.UserType</literal>. Este enfoque libera al c&#x00f3;digo de aplicaci&#x00f3;n
de implementar transformaciones a / desde un tipo Hibernate.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Usa JDBC codificado a mano en cuellos de botella.</term>
<listitem>
<para>
En &#x00e1;reas del sistema de rendimiento cr&#x00ed;tico, algunos tipos de operaciones podr&#x00ed;an beneficiarse
del JDBC directo. Pero por favor, espero hasta que <emphasis>sepas</emphasis> que algo es
un cuello de botella. Y no asumas que el JDBC directo es necesariamente m&#x00e1;s r&#x00e1;pido. Si necesitas
usar JDBC directo, podr&#x00ed;a ser valioso abrir una <literal>Session</literal> de Hibernate y usar esa
conexi&#x00f3;n JDBC. De esta forma puedes usar a&#x00fa;n la misma estrategia de transacci&#x00f3;n y el mismo
proveedor de conexiones subyacente.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Comprende la limpieza (flushing) de <literal>Session</literal>.</term>
<listitem>
<para>
De vez en cuando la sesi&#x00f3;n sincroniza su estado persistente con la base de datos. El rendimiento
se ver&#x00e1; afectado si este proceso ocurre demasiado frecuentemente. A veces puedes minimizar
limpieza innecesaria deshabilitando la limpieza autom&#x00e1;tica o incluso cambiando el orden de las
consultas u otras operaciones en una transacci&#x00f3;n en particular.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>En una aplicaci&#x00f3;n en tres gradas, considera usar objetos separados.</term>
<listitem>
<para>
Al usar una arquitectura de servlet / sesi&#x00f3;n, puedes pasar objetos persistentes en el bean de
sesi&#x00f3;n hacia y desde la capa de servlet / JSP. Usa una sesi&#x00f3;n nueva para atender el servicio de cada
petici&#x00f3;n. Usa <literal>Session.merge()</literal> o <literal>Session.saveOrUpdate()</literal> para
sincronizar los objetos con la base de datos.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>En una arquitectura en dos gradas, considera usar contexto de persistencia largos.</term>
<listitem>
<para>
Las transacciones de base de datos tienen que ser tan cortas como sea posible. Sin embargo,
frecuentemente es necesario implementar <emphasis>transacciones de aplicaci&#x00f3;n</emphasis>
ejecut&#x00e1;ndose en largo, una sola unidad de trabajo desde el punto de vista de un usuario.
Una transacci&#x00f3;n de aplicaci&#x00f3;n puede abarcar muchos ciclos petici&#x00f3;n/respuesta del cliente.
Es com&#x00fa;n usar objetos separados para implementar transacciones de aplicaci&#x00f3;n. Una alternativa,
extremadamente apropiada en arquitecturas en dos gradas, es mantener un solo contacto de persistencia
abierto (sesi&#x00f3;n) para todo el ciclo de vida de la transacci&#x00f3;n de aplicaci&#x00f3;n y simplemente
desconectar de la conexi&#x00f3;n JDBC al final de cada petici&#x00f3;n, y reconectar al comienzo de la
petici&#x00f3;n subsecuente. Nunca compartas una &#x00fa;nica sesi&#x00f3;n a trav&#x00e9;s de m&#x00e1;s de una transacci&#x00f3;n
de aplicaci&#x00f3;n, o estar&#x00e1;s trabajando con datos añejos.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>No trates la excepciones como recuperables.</term>
<listitem>
<para>
Esto es m&#x00e1;s una pr&#x00e1;ctica necesaria que una "mejor" pr&#x00e1;ctica. Cuando ocurra una excepci&#x00f3;n,
deshaz (rollback) la <literal>Transaction</literal> y cierra la <literal>Session</literal>.
Si no lo haces, Hibernate no puede garantizar que el estado en memoria representa con exactitud
el estado persistente. Como un caso especial de esto, no uses <literal>Session.load()</literal>
para determinar si una instancia con el identificador dado existe en la base de datos. En cambio,
usa <literal>Session.get()</literal> o una consulta.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Prefiere la recuperaci&#x00f3;n perezosa para las asociaciones.</term>
<listitem>
<para>
Usa escasamente la recuperaci&#x00f3;n temprana. Usa proxies y colecciones perezosas para la mayor&#x00ed;a
de asociaciones a clases probablemente no est&#x00e9;n mantenidas en el cach&#x00e9; de segundo nivel. Para
las asociaciones a clases en cach&#x00e9;, donde hay una probabilidad de acceso a cach&#x00e9; extremadamente
alta, deshabilita expl&#x00ed;citamente la recuperaci&#x00f3;n temprana usando <literal>lazy="false"</literal>.
Cuando sea apropiada la recuperaci&#x00f3;n por uni&#x00f3;n (join fetching) para un caso de uso en particular,
usa una consulta con un <literal>left join fetch</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Usa el patr&#x00f3;n <emphasis>sesi&#x00f3;n abierta en vista</emphasis>, o una <emphasis>fase de ensamblado</emphasis>
disciplinada para evitar problemas con datos no recuperados.
</term>
<listitem>
<para>
Hibernate liberal al desarrollador de escribir <emphasis>Objetos de Transferencia de Datos
(Data Transfer Objects)</emphasis> (DTO). En una arquitectura tradicional de EJB, los DTOs tienen
un prop&#x00f3;sito doble: primero, atacan el problema que los beans de entidad no son serializables.
Segundo, definen impl&#x00ed;citamente una fase de ensamblado cuando se recuperan y se forman (marshalling)
todos los datos a usar por la vista en los DTOs antes de devolver el control a la grada de
presentaci&#x00f3;n. Hibernate elimina el primer prop&#x00f3;sito. Sin embargo, a&#x00fa;n necesitas una fase
de ensamblado (piensa en tus m&#x00e9;todos de negocio como si tuviesen un contrato estricto con la grada
de presentaci&#x00f3;n sobre qu&#x00e9; datos est&#x00e1;n disponibles en los objetos separados) a menos que est&#x00e9;s
preparado para tener el contexto de persistencia (la sesi&#x00f3;n) abierto a trav&#x00e9;s del proceso
de renderizaci&#x00f3;n de la vista. &#x00a1;Esta no es una limitaci&#x00f3;n de Hibernate! Es un requerimiento
fundamental de acceso seguro a datos transaccionales.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Considera abstraer tu l&#x00f3;gica de negocio de Hibernate</term>
<listitem>
<para>
Oculta el c&#x00f3;digo de acceso a datos (Hibernate) detr&#x00e1;s de una interface. Combina los patrones
<emphasis>DAO</emphasis> y <emphasis>Sesi&#x00f3;n de Hebra Local</emphasis>. Incluso puedes tener
algunas clases hechas persistentes por JDBC escrito a mano, asociadas a Hibernate por medio
de un <literal>UserType</literal>. (Este consejo est&#x00e1; pensado para aplicaciones "suficientemente
grandes"; &#x00a1;no es apropiado para una aplicaci&#x00f3;n con cinco tablas!)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>No uses mapeos de asociaci&#x00f3;n ex&#x00f3;ticos.</term>
<listitem>
<para>
Son raros los casos de uso de asociaciones reales muchos-a-muchos. La mayor parte del tiempo
necesitas informaci&#x00f3;n adicional almacenada en una "tabla de enlace". En este caso, es mucho
mejor usar dos asociaciones uno-a-muchos a una clase de enlace intermedia. De hecho, pensamos
que la mayor&#x00ed;a de asociaciones son uno-a-muchos y muchos-a-uno, debes ser cuidadoso al usr
cualquier otro estilo de asociaci&#x00f3;n y preguntarte si es realmente necesario.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Prefiere las asociaciones bidireccionales.</term>
<listitem>
<para>
Las asociaciones unidireccionales son m&#x00e1;s dif&#x00ed;ciles de consultar. En una aplicaci&#x00f3;n grande,
casi todas las asociaciones deben ser navegables en ambas direcciones en consultas.
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,403 @@
<chapter id="components">
<title>Mapeo de Componentes</title>
<para>
La noci&#x00f3;n de un <emphasis>componente</emphasis> es reusada en muchos contextos diferentes,
para prop&#x00f3;sitos diferentes, a trav&#x00e9;s de Hibernate.
</para>
<sect1 id="components-dependentobjects">
<title>Objetos dependientes</title>
<para>
Un componente es un objeto contenido que es persistido como un tipo de valor, no una
referencia de entidad. El t&#x00e9;rmino "componente" hace referencia a la noci&#x00f3;n orientada a
objetos de composici&#x00f3;n (no a componentes a nivel de arquitectura). Por ejemplo, podr&#x00ed;as
modelar una persona como:
</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>
Ahora <literal>Name</literal> puede ser persistido como un componente de
<literal>Person</literal>. Observa que <literal>Name</literal> define m&#x00e9;todos
getter y setter para sus propiedades persistentes, pero no necesita declarar
ninguna interface ni propiedades identificadoras.
</para>
<para>
Nuestro mapeo de Hibernate se ver&#x00ed;a as&#x00ed;:
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</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>
La tabla person tendr&#x00ed;a las columnas <literal>pid</literal>,
<literal>birthday</literal>,
<literal>initial</literal>,
<literal>first</literal> y
<literal>last</literal>.
</para>
<para>
Como todos los tipos de valor, los componentes no soportan referencias compartidas.
En otras palabras, dos personas pueden tener el mismo nombre, pero los dos objetos
persona contendr&#x00ed;an dos objetos nombre independientes, s&#x00f3;lo "iguales" en valor.
La sem&#x00e1;ntica de valor nulo de un componente es <emphasis>ad hoc</emphasis>.
Cuando se recargue el objeto contenedor, Hibernate asumir&#x00e1; que si todas las columnas del
componente son nulas, el componente entero es nulo. Esto debe estar bien para la mayor&#x00ed;a
de prop&#x00f3;sitos.
</para>
<para>
Las propiedades de un componentes pueden ser de cualquier tipo de Hibernate
(colecciones, muchos-a-uno, asociaciones, otros componentes, etc). Los componentes
anidados <emphasis>no</emphasis> deben ser considerados un uso ex&#x00f3;tico. Hibernate est&#x00e1;
concebido para soportar un modelo de objetos granularizado en fino.
</para>
<para>
El elemento <literal>&lt;component&gt;</literal> permite un subelemento
<literal>&lt;parent&gt;</literal> que mapee una propiedad de la clase del componente
como una referencia de regreso a la entidad contenedora.
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</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>Colecciones de objetos dependientes</title>
<para>
Las colecciones de componentes est&#x00e1;n soportadas (por ejemplo,
un array de tipo <literal>Name</literal>). Declara tu colecci&#x00f3;n
de componentes remplazando la etiqueta <literal>&lt;element&gt;</literal>
por una etiqueta <literal>&lt;composite-element&gt;</literal>.
</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>
Nota: si defines un <literal>Set</literal> de elementos compuestos, es muy
importante implementar <literal>equals()</literal> y <literal>hashCode()</literal>
correctamente.
</para>
<para>
Los elementos compuestos pueden contener componentes pero no colecciones.
Si tu elemento compuesto contiene a su vez componentes, usa la etiqueta
<literal>&lt;nested-composite-element&gt;</literal>. Este es un caso bastante
ex&#x00f3;tico - una colecci&#x00f3;n de componentes que a su vez tienen componentes. A esta
altura debes estar pregunt&#x00e1;ndote si una asociaci&#x00f3;n uno-a-muchos es m&#x00e1;s
apropiada. Intenta remodelar el elemento compuesto como una entidad - pero
observa que aunque el modelo Java es el mismo, el modelo relacional y la
sem&#x00e1;ntica de persistencia siguen siendo ligeramente diferentes.
</para>
<para>
Por favor observa que un mapeo de elemento compuesto no soporta
propiedades nulables si est&#x00e1;s usando un <literal>&lt;set&gt;</literal>.
Hibernate tiene que usar cada columna para identificar un registro
al borrar objetos (no hay una columna clave primaria separada en la tabla del
elemento compuesto), lo que es imposible con valores nulos. Tienes que, o bien usar
s&#x00f3;lo propiedades no nulas en un elemento compuesto o elegir un
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal> o <literal>&lt;idbag&gt;</literal>.
</para>
<para>
Un caso especial de un elemento compuesto es un elemento compuesto con un
elemento anidado <literal>&lt;many-to-one&gt;</literal>. Un mapeo como este
te permite mapear columnas extra de una tabla de asociaci&#x00f3;n muchos-a-muchos
a la clase del elemento compuesto. La siguiente es una asociaci&#x00f3;n muchos-a-muchos
de <literal>Order</literal> a <literal>Item</literal> donde
<literal>purchaseDate</literal>, <literal>price</literal> y
<literal>quantity</literal> son propiedades de la asociaci&#x00f3;n:
</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>
Por supuesto, no puede haber una referencia a la compra del otro lado para la
navegaci&#x00f3;n bidireccional de la asociaci&#x00f3;n. Recuerda que los componentes son tipos de
valor no permiten referencias compartidas. Una sola <literal>Purchase</literal> puede
estar en el conjunto de una <literal>Order</literal>, pero no puede ser referenciada
por el <literal>Item</literal> al mismo tiempo.
</para>
<para>Incluso son posibles las asociaciones ternarias (o cuaternarias, etc):</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>
Los elementos compuestos pueden aparecer en consultas usando la misma
sint&#x00e1;xis que las asociaciones a otras entidades.
</para>
</sect1>
<sect1 id="components-asmapindex">
<title>Componentes como &#x00ed;ndices de Map</title>
<para>
El elemento <literal>&lt;composite-map-key&gt;</literal> te permite mapear
una clase componente como la clave de un <literal>Map</literal>. Aseg&#x00fa;rate que
sobrescribes <literal>hashCode()</literal> y <literal>equals()</literal>
correctamente en la clase componente.
</para>
</sect1>
<sect1 id="components-compositeid" revision="1">
<title>Componentes como identificadores compuestos</title>
<para>
Puedes usar un componente como un identidicador de una clase entidad. Tu clase
componente debe satisfacer ciertos requerimientos:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Debe implementar <literal>java.io.Serializable</literal>.
</para>
</listitem>
<listitem>
<para>
Debe re-implementar <literal>equals()</literal> y
<literal>hashCode()</literal>, consistentemente con la
noci&#x00f3;n de base de datos de igualdad de clave compuesta.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>Nota: en Hibernat3, el segundo requerimiento no es absolutamente un
requerimiento r&#x00ed;gido de Hibernate. Pero de todas formas, h&#x00e1;zlo.</emphasis>
</para>
<para>
No puedes usar un <literal>IdentifierGenerator</literal> para generar claves
compuestas. La aplicaci&#x00f3;n debe, en cambio, asignar sus propios identificadores.
</para>
<para>
Usa la etiqueta <literal>&lt;composite-id&gt;</literal> (con elementos
anidados <literal>&lt;key-property&gt;</literal>) en lugar de la usual
declaraci&#x00f3;n <literal>&lt;id&gt;</literal>. Por ejemplo, la clase
<literal>OrderLine</literal> tiene una clave primaria que depende de
la clave primaria (compuesta) de <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>
Ahora, cualquier clave for&#x00e1;nea que referencie la tabla de <literal>OrderLine</literal>
es tambi&#x00e9;n compuesta. Debes declarar esto en tus mapeos de otras clases. Una asociaci&#x00f3;n
a <literal>OrderLine</literal> ser&#x00ed;a mapeado as&#x00ed;:
</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>
(Nota que la etiqueta <literal>&lt;column&gt;</literal> es una alternativa al
atributo <literal>column</literal> en cualquier sitio.)
</para>
<para>
Una asociaci&#x00f3;n <literal>muchos-a-muchos</literal> a <literal>OrderLine</literal>
tambi&#x00e9;n usa la clave for&#x00e1;nea compuesta:
</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>
La colecci&#x00f3;n de <literal>OrderLine</literal>s en <literal>Order</literal> usar&#x00ed;a:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(El elemento <literal>&lt;one-to-many&gt;</literal>, como es usual, no declara columnas.)
</para>
<para>
Si <literal>OrderLine</literal> posee una colecci&#x00f3;n por s&#x00ed; misma, tiene tambi&#x00e9;n
una clave for&#x00e1;nea compuesta.
</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>Componentes din&#x00e1;micos</title>
<para>
Puedes incluso mapear una propiedad de tipo <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>
La sem&#x00e1;ntica de un mapeo <literal>&lt;dynamic-component&gt;</literal> es &#x00ed;dentica
a la de <literal>&lt;component&gt;</literal>. La ventaja de este tipo de mapeos es
la habilidad para determinar las propiedades reales del bean en tiempo de despliegue,
s&#x00f3;lo con editar el documento de mapeo. La manipulaci&#x00f3;n del documento de mapeo en tiempo
de ejecuci&#x00f3;n es tambi&#x00e9;n posible, usando un analizador DOM. Incluso mejor, puedes acceder
(y cambiar) el metamodelo de tiempo de configuraci&#x00f3;n de Hibernate por medio del objeto
<literal>Configuration</literal>.
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,233 @@
<chapter id="events">
<title>Interceptores y eventos</title>
<para>
Frecuentemente es &#x00fa;til para la aplicaci&#x00f3;n reaccionar a ciertos eventos que ocurran dentro de Hibernate.
Esto permite la implementaci&#x00f3;n de ciertos tipos de funcionalidade gen&#x00e9;rica, y extensi&#x00f3;n de la
funcionalidad de Hibernate.
</para>
<sect1 id="objectstate-interceptors" revision="1">
<title>Interceptores</title>
<para>
La interface <literal>Interceptor</literal> provee callbacks desde la sesi&#x00f3;n a la aplicaci&#x00f3;n
permitiendo a &#x00e9;sta &#x00fa;ltima inspeccionar y/o manipular las propiedades de un objeto persistente
antes que sea salvado, actualizado, borrado o cargado. Un uso posible de esto es seguir la pista
de informaci&#x00f3;n de auditor&#x00ed;a. Por ejemplo, el siguiente <literal>Interceptor</literal> establece
autom&#x00e1;ticamente el <literal>createTimestamp</literal> cuando un <literal>Auditable</literal> es
creado y actualiza la propiedad <literal>lastUpdateTimestamp</literal> cuando un
<literal>Auditable</literal> es acutalizado.
</para>
<programlisting><![CDATA[package org.hibernate.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.Interceptor;
import org.hibernate.type.Type;
public class AuditInterceptor implements Interceptor, Serializable {
private int updates;
private int creates;
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) {
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 postFlush(Iterator entities) {
System.out.println("Creations: " + creates + ", Updates: " + updates);
}
public void preFlush(Iterator entities) {
updates=0;
creates=0;
}
...
}]]></programlisting>
<para>
El interceptor podr&#x00ed;a ser especificado cuando se crea la sesi&#x00f3;n:
</para>
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
<para>
Puedes adem&#x00e1;s establecer un interceptor a un nivel global, usando la <literal>Configuration</literal>:
</para>
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
</sect1>
<sect1 id="objectstate-events" revision="2">
<title>Sistema de eventos</title>
<para>
Si tienes que reaccionar a eventos particulares en tu capa de persistencia, puedes tambi&#x00e9;n la
arquitectura de <emphasis>eventos</emphasis> de Hibernate3. El sistema de eventos puede ser usado
en adici&#x00f3;n o como un remplazo a los interceptores.
</para>
<para>
Esencialmente todos los m&#x00e9;todos de la interface <literal>Session</literal> se correlacionan
con un evento. Tienes un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
(consulta el DTD del fichero de configuraci&#x00f3;n XML o el paquete <literal>org.hibernate.event</literal>
para la lista completa de tipos de evento definidos). Cuando se hace una petici&#x00f3;n de uno de estos
m&#x00e9;todos, la <literal>Session</literal> de Hibernate genera un evento apropiado y se lo pasa
al oyente (listener) de eventos configurado para ese tipo. De f&#x00e1;brica, estos oyentes implementan
el mismo procesamiento en los que siempre resultan aquellos m&#x00e9;todos. Sin embargo, eres libre de
implementar una personalizaci&#x00f3;n de una de las interfaces oyentes (es decir, el
<literal>LoadEvent</literal> es procesado por la implementaci&#x00f3;n registrada de la interface
<literal>LoadEventListener</literal>), en cuyo caso su implementaci&#x00f3;n ser&#x00ed;a responsable
de procesar cualquier petici&#x00f3;n <literal>load()</literal> hecha a la <literal>Session</literal>.
</para>
<para>
Los oyentes deben ser considerados efectivamente singletons; quiere decir, que son compartidos
entre las peticiones, y por lo tanto no guardan ning&#x00fa;n estado en variables de instancia.
</para>
<para>
Un oyente personalizado debe implementar la interface apropiada para el evento que quiere procesar y/o
extender una de las clases base de conveniencia (o incluso los oyentes de eventos por defecto
usados por Hibernate de f&#x00e1;brica al ser &#x00e9;stos declarados non-final para este prop&#x00f3;sito). Los
oyentes personalizados pueden ser registrados program&#x00e1;ticamente a trav&#x00e9;s del objeto
<literal>Configuration</literal>, o especificados en el XML de configuraci&#x00f3;n de Hibernate
(la declaraci&#x00f3;n declarativa a trav&#x00e9;s del fichero de propiedades no est&#x00e1; soportada).
He aqu&#x00ed; un ejemplo de un oyente personalizado de eventos load:
</para>
<programlisting><![CDATA[public class MyLoadListener extends DefaultLoadEventListener {
// this is the single method defined by the LoadEventListener interface
public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
return super.onLoad(event, loadType);
}
}]]></programlisting>
<para>
Necesitas adem&#x00e1;s una entrada de configuraci&#x00f3;n dici&#x00e9;ndole a Hibernate que use el
oyente en vez del oyente por defecto:
</para>
<programlisting><![CDATA[<hibernate-configuration>
<session-factory>
...
<listener type="load" class="MyLoadListener"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
En cambio, puedes registrarlo program&#x00e1;ticamente:
</para>
<programlisting><![CDATA[Configuration cfg = new Configuration();
cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );]]></programlisting>
<para>
Los oyentes registrados declarativamente no pueden compartir instancias. Si el mismo nombre de clase es
usado en m&#x00fa;ltiples elementos <literal>&lt;listener/&gt;</literal>, cada referencia resultar&#x00e1; en una instancia
separada de esa clase. Si necesitas la capacidad de compartir instancias de oyentes entre tipos de oyente
debes usar el enfoque de registraci&#x00f3;n program&#x00e1;tica.
</para>
<para>
&#x00bf;Por qu&#x00e9; implementar una interface y definir el tipo espc&#x00ed;fico durante la configuraci&#x00f3;n?
Bueno, una implementaci&#x00f3;n de oyente podr&#x00ed;a implementar m&#x00fa;ltiples interfaces de oyente
de eventos. Teniendo el tipo definido adicionalmente durante la registraci&#x00f3;n lo hace m&#x00e1;s
f&#x00e1;cil para activar o desactivar oyentes personalizados durante la configuraci&#x00f3;n.
</para>
</sect1>
<sect1 id="objectstate-decl-security">
<title>Seguridad declarativa de Hibernate</title>
<para>
Usualmente, la seguridad declarativa en aplicaciones Hibernate es manejada en una capa de fachada
de sesi&#x00f3;n. Ahora, Hibernate3 permite que ciertas acciones sean permitidas v&#x00ed;a JACC, y autorizadas v&#x00ed;a
JAAS. Esta en una funcionalidad opcional constru&#x00ed;da encima de la arquitectura de eventos.
</para>
<para>
Primero, debes configurar los oyentes de eventos apropiados, para habilitar el uso de
autorizaci&#x00f3;n JAAS.
</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>
Seguido, a&#x00fa;n en <literal>hibernate.cfg.xml</literal>, liga los permisos a roles:
</para>
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
<para>
Los nombres de role son los roles entendidos por tu proveedor de JACC.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,654 @@
<chapter id="example-mappings">
<title>Ejemplo: Varios Mapeos</title>
<para>
Este cap&#x00ed;tulo muestra mapeos de asociaciones m&#x00e1;s complejos.
</para>
<sect1 id="example-mappings-emp">
<title>Empleador/Empleado</title>
<para>
El siguiente modelo de la relaci&#x00f3;n entre <literal>Employer</literal> y <literal>Employee</literal>
usa una clase de entidad real (<literal>Employment</literal>) para representar la asociaci&#x00f3;n.
Esto se ha hecho esto porque podr&#x00ed;a haber m&#x00e1;s de un per&#x00ed;odo de empleo para los mismos dos participantes.
Se usan componentes para modelar valores monetarios y nombres de empleado.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
He aqu&#x00ed; un documento de mapeo posible:
</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>
Y he aqu&#x00ed; el esquema de tablas generado por <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>Autor/Obra</title>
<para>
Considera el siguiente modelo de las relaciones entre <literal>Work</literal>,
<literal>Author</literal> y <literal>Person</literal>. Representamos la relaci&#x00f3;n entre <literal>Work</literal>
y <literal>Author</literal> como una asociaci&#x00f3;n muchos-a-muchos. Elegimos representar la relaci&#x00f3;n entre
<literal>Author</literal> y <literal>Person</literal> como una asociaci&#x00f3;n uno-a-uno. Otra posibilidad
hubiese sido que <literal>Author</literal> extendiera <literal>Person</literal>.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
El siguiente documento de mapeo representa estas relaciones correctamente:
</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>
Hay cuatro tablas en este mapeo. <literal>works</literal>, <literal>authors</literal> y <literal>persons</literal>
tienen los datos de obra, autor y persona respectivamente. <literal>author_work</literal> es una tabla de
asociaci&#x00f3;n enlazando autores a obras. He aqu&#x00ed; el esquema de tablas, tal como fue generado por
<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>Cliente/Orden/Producto</title>
<para>
Ahora considera un modelo de las relaciones entre <literal>Customer</literal>,
<literal>Order</literal> y <literal>LineItem</literal> y <literal>Product</literal>.
Hay una asociaci&#x00f3;n uno-a-muchos entre <literal>Customer</literal> y <literal>Order</literal>,
pero, &#x00bf;c&#x00f3;mo deber&#x00ed;amos representar <literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>?
He elegido mapear <literal>LineItem</literal> como una clase de asociaci&#x00f3;n representando la
asociaci&#x00f3;n muchos-a-muchos entre <literal>Order</literal> y <literal>Product</literal>. En Hibernate,
esto se llama un elemento compuesto.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
El documento de mapeo:
</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> y
<literal>products</literal> tienen los datos de cliente, orden, &#x00ed;tem de l&#x00ed;nea de orden y producto
respectivamente. Adem&#x00e1;s <literal>line_items</literal> act&#x00fa;a como una tabla de asociaci&#x00f3;n enlazando
&#x00f3;rdenes con productos.
</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>Mapeos miscel&#x00e1;neos de ejemplo</title>
<para>
Todos estos ejemplos est&#x00e1;n tomados de la bater&#x00ed;a de pruebas de Hibernate.
Encontrar&#x00e1;s muchos otros mapeos de ejemplo &#x00fa;tiles all&#x00ed;. Mira en la carpeta
<literal>test</literal> de la distribuci&#x00f3;n de Hibernate.
</para>
<para>POR HACER: poner palabras alrededor de este material</para>
<sect2 id="example-mappings-typed-onetone">
<title>Asociaci&#x00f3;n uno-a-uno "Tipificada"</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>Ejemplo de clave compuesta</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>Muchos-a-muchos con atributo de clave compuesta compartido</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>Discriminaci&#x00f3;n basada en contenido</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" >
<title>Asociaciones sobre claves alternativas</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.hex"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
<property name="type" not-null="true"/>
</class>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

@ -0,0 +1,362 @@
<chapter id="example-parentchild">
<title>Ejemplo: Padre/Hijo</title>
<para>
Una de las primer&#x00ed;simas cosas que los usuarios nuevos intentan hacer con Hibernate es modelar una relaci&#x00f3;n de
tipo padre / hijo. Para esto hay dos enfoques diferentes. Por varias razones, el enfoque m&#x00e1;s conveniente,
especialmente para usuarios nuevos, es modelar tanto <literal>Parent</literal> como <literal>Child</literal>
como clases de entidad con una asociaci&#x00f3;n <literal>&lt;one-to-many&gt;</literal> desde <literal>Parent</literal>
a <literal>Child</literal>. (El enfoque alternativo es declarar el <literal>Child</literal> como un
<literal>&lt;composite-element&gt;</literal>.) Ahora, resulta que la sem&#x00e1;ntica por defecto de una asociaci&#x00f3;n
uno a muchos (en Hibernate) es mucho menos cercana a la sem&#x00e1;ntica usual de una relaci&#x00f3;n padre / hijo que aquellas
de un mapeo de elementos compuestos. Explicaremos c&#x00f3;mo usar una <emphasis>asociaci&#x00f3;n uno a muchos bidireccional
con tratamiento en cascada</emphasis> para modelar una relaci&#x00f3;n padre / hijo eficiente y elegantemente.
&#x00a1;No es para nada dif&#x00ed;cil!
</para>
<sect1 id="example-parentchild-collections">
<title>Una nota sobre las colecciones</title>
<para>
Se considera que las colecciones de Hibernate son una parte l&#x00f3;gica de la entidad que las posee; nunca de
las entidades contenidas. &#x00a1;Esta es una distinci&#x00f3;n crucial! Esto tiene las siguientes consecuencias:
</para>
<itemizedlist>
<listitem>
<para>
Cuando se quita / a&#x00f1;ade un objeto desde / a una colecci&#x00f3;n, se incrementa el n&#x00fa;mero de versi&#x00f3;n del
due&#x00f1;o de la colecci&#x00f3;n.
</para>
</listitem>
<listitem>
<para>
Si un objeto que fue quitado de una colecci&#x00f3;n es una instancia de un tipo de valor (por ejemplo, un
elemento compuesto), ese objeta cesar&#x00e1; de ser persistente y su estado ser&#x00e1; completamente quitado de la
base de datos. Asimismo, a&#x00f1;adir una instancia de tipo de valor a la colecci&#x00f3;n causar&#x00e1; que su estado
sea inmediatamente persistente.
</para>
</listitem>
<listitem>
<para>
Por otro lado, si se quita una entidad de una colecci&#x00f3;n (una asociaci&#x00f3;n uno-a-muchos o muchos-a-muchos),
no ser&#x00e1; borrado, por defecto. Este comportamiento es completamente consistente. &#x00a1;Un cambio en el
estado interno de otra entidad no hace desaparecer la entidad asociada! Asimismo, a&#x00f1;adir una entidad a
una colecci&#x00f3;n no causa que la entidad se vuelva persistente, por defecto.
</para>
</listitem>
</itemizedlist>
<para>
En cambio, el comportamiento por defecto es que al a&#x00f1;adir una entidad a una colecci&#x00f3;n se crea meramente
un enlace entre las dos entidades, mientras que al quitarla se quita el enlace. Esto es muy apropiado para
todos los tipos de casos. Donde no es para nada apropiado es en el caso de una relaci&#x00f3;n padre / hijo. donde
la vida del hijo est&#x00e1; ligada al ciclo de vida del padre.
</para>
</sect1>
<sect1 id="example-parentchild-bidir">
<title>Uno-a-muchos bidirectional</title>
<para>
Sup&#x00f3;n que empezamos con una asociaci&#x00f3;n simple <literal>&lt;one-to-many&gt;</literal> desde
<literal>Parent</literal> a <literal>Child</literal>.
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Si ejecut&#x00e1;semos el siguiente c&#x00f3;digo
</para>
<programlisting><![CDATA[Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();]]></programlisting>
<para>
Hibernate publicar&#x00ed;a dos sentencias SQL:
</para>
<itemizedlist>
<listitem>
<para>un <literal>INSERT</literal> para crear el registro de <literal>c</literal></para>
</listitem>
<listitem>
<para>
un <literal>UPDATE</literal> para crear el enlace desde <literal>p</literal> a
<literal>c</literal>
</para>
</listitem>
</itemizedlist>
<para>
Esto no es s&#x00f3;lo ineficiente, sino que adem&#x00e1;s viola cualquier restricci&#x00f3;n <literal>NOT NULL</literal> en la
columna <literal>parent_id</literal>. Podemos reparar la violaci&#x00f3;n de restricci&#x00f3;n de nulabilidad
especificando <literal>not-null="true"</literal> en el mapeo de la colecci&#x00f3;n:
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Sin embargo, esta no es la soluci&#x00f3;n recomendada.
</para>
<para>
El caso subyacente de este comportamiento es que el enlace (la clave for&#x00e1;nea <literal>parent_id</literal>)
de <literal>p</literal> a <literal>c</literal> no es considerado parte del estado del objeto
<literal>Child</literal> y por lo tanto no es creada en el <literal>INSERT</literal>. De modo que la
soluci&#x00f3;n es hacer el enlace parte del mapeo del <literal>Child</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
<para>
(Necesitamos adem&#x00e1;s a&#x00f1;adir la propiedad <literal>parent</literal> a la clase <literal>Child</literal>.)
</para>
<para>
Ahora que la entidad <literal>Child</literal> est&#x00e1; gestionando el estado del enlace, le decimos a la
colecci&#x00f3;n que no actualice el enlace. Usamos el atributo <literal>inverse</literal>.
</para>
<programlisting><![CDATA[<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
El siguiente c&#x00f3;digo podr&#x00ed;a ser usado para a&#x00f1;adir un nuevo <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>
Y ahora, &#x00a1;S&#x00f3;lo se publicar&#x00ed;a un <literal>INSERT</literal> de SQL!
</para>
<para>
Para ajustar un poco m&#x00e1;s las cosas, podr&#x00ed;amos crear un m&#x00e9;todo <literal>addChild()</literal> en
<literal>Parent</literal>.
</para>
<programlisting><![CDATA[public void addChild(Child c) {
c.setParent(this);
children.add(c);
}]]></programlisting>
<para>
Ahora, el c&#x00f3;digo para a&#x00f1;adir un <literal>Child</literal> se ve as&#x00ed;
</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>Ciclo de vida en cascada</title>
<para>
La llamada expl&#x00ed;cita a <literal>save()</literal> es a&#x00fa;n molesta. Apuntaremos a esto usando tratamientos
en cascada.
</para>
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Esto simplifica el c&#x00f3;digo anterior a
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();]]></programlisting>
<para>
Similarmente, no necesitamos iterar los hijos al salvar o borrar un <literal>Parent</literal>.
Lo siguiente quita <literal>p</literal> y todos sus hijos de la base de datos.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();]]></programlisting>
<para>
Sin embargo, este c&#x00f3;digo
</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>
no quitar&#x00e1; <literal>c</literal> de la base de datos; s&#x00f3;lo quitar&#x00e1; el enlace a <literal>p</literal>
(y causar&#x00e1; una violaci&#x00f3;n a una restricci&#x00f3;n <literal>NOT NULL</literal>). Necesitas borrar el hijo
expl&#x00ed;citamente llamando a <literal>delete()</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>
Ahora, en nuestro caso, un <literal>Child</literal> no puede existir realmente sin su padre. De modo que
si quitamos un <literal>Child</literal> de la colecci&#x00f3;n, realmente queremos que sea borrado. Para esto,
debemos usar <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>
Nota: aunque el mapeo de la colecci&#x00f3;n especifique <literal>inverse="true"</literal>, el tratamiento en
cascada se procesa a&#x00fa;n al iterar los elementos de colecci&#x00f3;n. De modo que si requieres que un objeto sea
salvado, borrado o actualizado en cascada, debes a&#x00f1;adirlo a la colecci&#x00f3;n. No es suficiente con simplemente
llamar a <literal>setParent()</literal>.
</para>
</sect1>
<sect1 id="example-parentchild-update">
<title>Tratamiento en cascada y <literal>unsaved-value</literal></title>
<para>
Sup&#x00f3;n que hemos cargado un <literal>Parent</literal> en una <literal>Session</literal>, hemos hecho algunos
cambios en una acci&#x00f3;n de UI y deseamos hacer persistentes estos cambios en una nueva sesi&#x00f3;n llamando a
<literal>update()</literal>. El <literal>Parent</literal> contendr&#x00e1; una colecci&#x00f3;n de hijos y, ya que
est&#x00e1; habilitado el tratamiento en cascada, Hibernate necesita saber qu&#x00e9; hijos est&#x00e1;n reci&#x00e9;n instanciados
y cu&#x00e1;les representan filas existentes en la base de datos. Asumamos que tanto <literal>Parent</literal> como
<literal>Child</literal> tienen propiedades identificadoras generadas de tipo <literal>Long</literal>.
Hibernate usar&#x00e1; el identificador y el valor de la propiedad de versi&#x00f3;n/timestamp para determinar cu&#x00e1;les de
los hijos son nuevos. (Ver <xref linkend="objectstate-saveorupdate"/>.) <emphasis>En Hibernate3, no es
m&#x00e1;s necesario especificar un <literal>unsaved-value</literal> expl&#x00ed;citamente.</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>
Bueno, todo eso est&#x00e1; muy bien para el caso de un identificador generado, pero &#x00bf;qu&#x00e9; de los
identificadores asignados y de los identificadores compuestos? Esto es m&#x00e1;s dif&#x00ed;cil, ya que Hibernate
no puede usar la propiedad identificadora para distinguir entre un objeto reci&#x00e9;n instanciado (con un
identificador asignado por el usuario) y un objeto cargado en una sesi&#x00f3;n previa. En este caso, Hibernate
bien usar&#x00e1; la propiedad de versi&#x00f3;n o timestamp, o bien consultar&#x00e1; realmente el cach&#x00e9; de segundo nivel,
o bien, en el peor de los casos, la base de datos, para ver si existe la fila.
</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>Conclusi&#x00f3;n</title>
<para>
Hay que resumir un poco aqu&#x00ed; y podr&#x00ed;a parecer confuso a la primera vez. Sin embargo, en la pr&#x00e1;ctica,
todo funciona muy agradablemente. La mayor&#x00ed;a de las aplicaciones de Hibernate usan el patr&#x00f3;n
padre / hijo en muchos sitios.
</para>
<para>
Hemos mencionado una alternativa en el primer p&#x00e1;rrafo. Ninguno de los temas anteriores existe en el caso
de los mapeos <literal>&lt;composite-element&gt;</literal>, que tienen exactamente la sem&#x00e1;ntica de una
relaci&#x00f3;n padre / hijo. Desafortunadamente, hay dos grandes limitaciones para las clases de elementos
compuestos: los elementos compuestos no pueden poseer sus propias colecciones, y no deben ser el hijo
de cualquier otra entidad que no sea su padre &#x00fa;nico.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,429 @@
<chapter id="example-weblog">
<title>Ejemplo: Aplicaci&#x00f3;n de Weblog</title>
<sect1 id="example-weblog-classes">
<title>Clases Persistentes</title>
<para>
Las clases persistentes representan un weblog, y un &#x00ed;tem enviado a un weblog. Van a ser modelados como una
relaci&#x00f3;n padre/hijo est&#x00f1;ndar, pero usaremos un bag ordenado, en vez de un conjunto (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>Mapeos de Hibernate</title>
<para>
Los mapeos XML ahora deben ser absolutamente directos.
</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>C&#x00f3;digo Hibernate</title>
<para>
La siguiente clase demuestra algunos de los tipos de cosas que podemos haces con estas clases,
usando Hibernate.
</para>
<programlisting><![CDATA[package eg;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class BlogMain {
private SessionFactory _sessions;
public void configure() throws HibernateException {
_sessions = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class)
.buildSessionFactory();
}
public void exportTables() throws HibernateException {
Configuration cfg = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class);
new SchemaExport(cfg).create(true, true);
}
public Blog createBlog(String name) throws HibernateException {
Blog blog = new Blog();
blog.setName(name);
blog.setItems( new ArrayList() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public BlogItem createBlogItem(Blog blog, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setBlog(blog);
item.setDatetime( Calendar.getInstance() );
blog.getItems().add(item);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public BlogItem createBlogItem(Long blogid, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Blog blog = (Blog) session.load(Blog.class, blogid);
item.setBlog(blog);
blog.getItems().add(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public void updateBlogItem(BlogItem item, String text)
throws HibernateException {
item.setText(text);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public void updateBlogItem(Long itemid, String text)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
item.setText(text);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public List listAllBlogNamesAndItemCounts(int max)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"select blog.id, blog.name, count(blogItem) " +
"from Blog as blog " +
"left outer join blog.items as blogItem " +
"group by blog.name, blog.id " +
"order by max(blogItem.datetime)"
);
q.setMaxResults(max);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
public Blog getBlogAndAllItems(Long blogid)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
Blog blog = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"left outer join fetch blog.items " +
"where blog.id = :blogid"
);
q.setParameter("blogid", blogid);
blog = (Blog) q.uniqueResult();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public List listBlogsAndRecentItems() throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"inner join blog.items as blogItem " +
"where blogItem.datetime > :minDate"
);
Calendar cal = Calendar.getInstance();
cal.roll(Calendar.MONTH, false);
q.setCalendar("minDate", cal);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,130 @@
<chapter id="filters">
<title>Filtrando datos</title>
<para>
Hibernate3 provee un nuevo enfoque innovador para manejar datos con reglas de "visibilidad".
Un <emphasis>filtro de Hibernate</emphasis> es un filtro global, con nombre y parametrizado
que puede ser habilitado o deshabilitado para una sesión de Hibernate en particular.
</para>
<sect1 id="objectstate-filters">
<title>Filtros de Hibernate</title>
<para>
Hibernate3 añade la habilidad de predefinir criterios de filtros y unir esos filtros tanto a
nivel de una clase como de una colección. Un criterio de filtro es la habilidad de definir una
cláusula de restricción muy similar al atributo existente "where" disponible en el elemento
class y varios elementos de colección. Excepto en que estos filtros pueden ser parametrizados.
La aplicación puede tomar la decisión en tiempo de ejecución de qué filtros deben estar
habilitados y cuáles deben ser sus parámetros. Los filtros pueden ser usados como vistas de
base de datos, pero parametrizados dentro de la aplicación.
</para>
<para>
Para usar los filtros, éstos deben primero ser definidos y luego unidos a los elementos de mapeo
apropiados. Para definir un filtro, usa el elemento <literal>&lt;filter-def/&gt;</literal> dentro
de un elemento <literal>&lt;hibernate-mapping/&gt;</literal>:
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>]]></programlisting>
<para>
Entonces este filtro puede ser unido a una clase:
</para>
<programlisting><![CDATA[<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>]]></programlisting>
<para>
o a una colección:
</para>
<programlisting><![CDATA[<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>]]></programlisting>
<para>
o incluso a ambos (o muchos de cada uno) al mismo tiempo.
</para>
<para>
Los métodos en <literal>Session</literal> son: <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, y <literal>disableFilter(String filterName)</literal>.
Por defecto, los filtros <emphasis>no</emphasis> están habilitados para una sesión dada; deben ser
habilitados explícitamente por medio del uso del método <literal>Session.enableFilter()</literal>,
que devuelve una instancia de la interface <literal>Filter</literal>. Usando el filtro simple definido
arriba, esto se vería así:
</para>
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
<para>
Nota que los métodos en la interface org.hibernate.Filter permiten el encadenamiento de métodos
común en gran parte de Hibernate.
</para>
<para>
Un ejemplo completo, usando datos temporales con un patrón efectivo de fechas de registro:
</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>
Entonces, en orden de asegurar que siempre tendrás de vuelta registros actualmente efectivos,
simplemente habilita el filtro en la sesión previo a recuperar los datos de empleados:
</para>
<programlisting><![CDATA[Session session = ...;
session.enabledFilter("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>
En el HQL de arriba, aunque sólo hemos mencionado explícitamente una restricción de salario en
los resultados, debido al filtro habilitado la consulta sólo devolverá empleados actualmente activos
que tengan un salario mayor que un millón de dólares.
</para>
<para>
Nota: si planeas usar filtros con unión externa (outer joining) (bien a través de HQL, o bien
de recuperación de carga) sé cuidadoso en la dirección de expresión de la condición. Lo más seguro
es establecer esto para unión externa izquierda (left outer joining). En general, coloca el primer
parámetro seguido del nombre(s) de columna(s) después del operador.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,464 @@
<chapter id="inheritance">
<title>Mapeo de Herencia</title>
<sect1 id="inheritance-strategies" revision="2">
<title>Las Tres Estrategias</title>
<para>
Hibernate soporta las tres estrategias b&#x00e1;sicas de mapeo de herencia:
</para>
<itemizedlist>
<listitem>
<para>
tabla por jerarqu&#x00ed;a de clases
</para>
</listitem>
<listitem>
<para>
tabla por subclase
</para>
</listitem>
<listitem>
<para>
tabla por clase concreta
</para>
</listitem>
</itemizedlist>
<para>
En adici&#x00f3;n, Hibernate soporta un cuarto, ligeramente diferente tipo
de polimorfismo:
</para>
<itemizedlist>
<listitem>
<para>
polimorfismo impl&#x00ed;cito
</para>
</listitem>
</itemizedlist>
<para>
Es posible usar estrategias de mapeo diferentes para diferentes
ramificaciones de la misma jerarqu&#x00ed;a de herencia, y entonces usar
polimorfismo impl&#x00ed;cito para conseguir polimorfismo a trav&#x00e9;s de
toda la jerarqu&#x00ed;a. Sin embargo, Hibernate no soporta la mezcla de
mapeos <literal>&lt;subclass&gt;</literal>,
y <literal>&lt;joined-subclass&gt;</literal>
y <literal>&lt;union-subclass&gt;</literal> bajo el mismo elemento
<literal>&lt;class&gt;</literal> ra&#x00ed;z. Es posible mezclar juntas las
estrategias de tabla por jerarqu&#x00ed;a y tabla por subclase, bajo el mismo
elemento <literal>&lt;class&gt;</literal>, combinando los elementos
<literal>&lt;subclass&gt;</literal> y <literal>&lt;join&gt;</literal>
(ver debajo).
</para>
<sect2 id="inheritance-tableperclass" >
<title>Tabla por jerarqu&#x00ed;a de clases</title>
<para>
Sup&#x00f3;n que tenemos una interface <literal>Payment</literal>, con
los implementadores <literal>CreditCardPayment</literal>,
<literal>CashPayment</literal>, <literal>ChequePayment</literal>.
El mapeo de tabla por jerarqu&#x00ed;a se ver&#x00ed;a as&#x00ed;:
</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>
Se requiere exactamente una tabla. Hay una gran limitaci&#x00f3;n de esta estrategia de mapeo:
las columnas declaradas por las subclases, como <literal>CCTYPE</literal>, no pueden
tener restricciones <literal>NOT NULL</literal>.
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>Tabla por subclase</title>
<para>
Un mapeo de tabla por sublclase se ver&#x00ed;a as&#x00ed;:
</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>
Se requieren cuatro tablas. Las tres tablas de subclase tienen
asociaciones de clave primaria a la tabla de superclase (de modo
que en el modelo relacional es realmente una asociaci&#x00f3;n uno-a-uno).
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
<title>Tabla por subclase, usando un discriminador</title>
<para>
Observa que la implementaci&#x00f3;n de Hibernate de tabla por subclase
no requiere ninguna columna discriminadora. Otros mapeadores
objeto/relacional usan una implementaci&#x00f3;n diferente de tabla por
subclase que requiere una columna discriminadora de tipo en la tabla
de superclase. Este enfoque es mucho m&#x00e1;s dif&#x00ed;cil de implementar
pero discutiblemente m&#x00e1;s correcto desde un punto de vista relacional.
Si quisieras usar una columna discriminadora con la estrategia de
tabla por subclase, puedes combinar el uso de <literal>&lt;subclass&gt;</literal>
y <literal>&lt;join&gt;</literal>, como sigue:
</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>
la declaraci&#x00f3;n opcional <literal>fetch="select"</literal> dice a Hibernate
que no recupere los datos de la subclase <literal>ChequePayment</literal>
usando una uni&#x00f3;n externa (outer join) al consultar la superclase.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>Mezclando tabla por jerarqu&#x00ed;a de clases con tabla por subclase</title>
<para>
Puedes incluso mezclar las estrategias de tabla po jerarqu&#x00ed;a y tabla por
subclase usando este enfoque:
</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>
Para cualquiera de estas estrategias de mapeo, una asociaci&#x00f3;n polim&#x00f3;rfica
a la clase ra&#x00ed;z <literal>Payment</literal> es mapeada usando <literal>&lt;many-to-one&gt;</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="1">
<title>Tabla por clase concreta</title>
<para>
Podr&#x00ed;amos ir de dos maneras a la estrategia de mapeo de tabla por clase
concreta. La primera es usar <literal>&lt;union-subclass&gt;</literal>.
</para>
<programlisting><![CDATA[<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>]]></programlisting>
<para>
Est&#x00e1;n implicadas tres tablas. Cada tabla define columnas para todas las
propiedades de la clase, inccluyendo las propiedades heredadas.
</para>
<para>
La limitaci&#x00f3;n de este enfoque es que si una propiedad es mapeada en la
superclase, el nombre de columna debe ser el mismo en todas las tablas
de subclase. (Podr&#x00ed;amos relajar esto en un lanzamiento futuro de Hibernate.)
La estrategia de generador de indentidad no est&#x00e1; permitida en la herencia
de uni&#x00f3;n de subclase, de hecho la semilla de clave primaria tiene que ser
compartida a trav&#x00e9;s de todas las subclases unidas de una jerarqu&#x00ed;a.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Tabla por clase concreta, usando polimorfismo impl&#x00ed;cito</title>
<para>
Un enfoque alternativo es hacer uso de polimorfismo impl&#x00ed;cito:
</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>
Nota que en ning&#x00fa;n sitio mencionamos la interface <literal>Payment</literal>
expl&#x00ed;citamente. Nota adem&#x00e1;s que las propiedades de <literal>Payment</literal>
son mapeadas en cada una de las subclases. Si quieres evitar duplicaci&#x00f3;n,
considera usar entidades XML. (por ejemplo,
<literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
en la declaraci&#x00f3;n <literal>DOCTYPE</literal> y <literal>&amp;allproperties;</literal>
en el mapeo).
</para>
<para>
La desventaja de este enfoque es que Hibernate no genera <literal>UNION</literal>s
de SQL al realizar consultas polim&#x00f3;rficas.
</para>
<para>
Para esta estrategia de mapeo, una asociaci&#x00f3;n polim&#x00f3;rfica a <literal>Payment</literal>
es mapeada generalmente usando <literal>&lt;any&gt;</literal>.
</para>
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
<meta-value value="CREDIT" class="CreditCardPayment"/>
<meta-value value="CASH" class="CashPayment"/>
<meta-value value="CHEQUE" class="ChequePayment"/>
<column name="PAYMENT_CLASS"/>
<column name="PAYMENT_ID"/>
</any>]]></programlisting>
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>Mezclando polimorfismo impl&#x00ed;cito con otros mapeos de herencia</title>
<para>
Hay una cosa m&#x00e1;s por notar acerca de este mapeo. Ya que las subclases se mapean
cada una en su propio elemento <literal>&lt;class&gt;</literal> (y ya que
<literal>Payment</literal> es s&#x00f3;lo una interface), cada una de las subclases
podr&#x00ed;a ser parte de otra jerarqu&#x00ed;a de herencia! (Y todav&#x00ed;a puedes seguir usando
consultas polim&#x00f3;rficas contra la interface <literal>Payment</literal>.)
</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>
Una vez m&#x00e1;s, no mencionamos a <literal>Payment</literal> expl&#x00ed;citamente.
Si ejecutamos una consulta contra la interface <literal>Payment</literal>
- por ejemplo, <literal>from Payment</literal> - Hibernate devuelve
autom&#x00e1;ticamente instancias de <literal>CreditCardPayment</literal>
(y sus subclases, ya que ellas tambi&#x00e9;n implementan <literal>Payment</literal>),
<literal>CashPayment</literal> y <literal>ChequePayment</literal> pero
no instancias de <literal>NonelectronicTransaction</literal>.
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>Limitaciones</title>
<para>
Existen ciertas limitaciones al enfoque de "polimorfismo impl&#x00ed;cito" en
la estrategia de mapeo de tabla por clase concreta. Existen limitaciones
algo menos restrictivas a los mapeos <literal>&lt;union-subclass&gt;</literal>.
</para>
<para>
La siguiente tabla muestra las limitaciones de mapeos de tabla por
clase concreta, y de polmorfismo impl&#x00ed;cito, en Hibernate.
</para>
<table frame="topbot">
<title>Funcionalidades de mapeo de herencia</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>Estrategia de herencia</entry>
<entry>muchos-a-uno polim&#x00f3;rfica</entry>
<entry>uno-a-uno polim&#x00f3;rfica</entry>
<entry>uno-a-muchos polim&#x00f3;rfica</entry>
<entry>mushos-a-muchos polim&#x00f3;rfica</entry>
<entry><literal>load()/get()</literal> polim&#x00f3;rficos</entry>
<entry>Consultas polim&#x00f3;rficas</entry>
<entry>Uniones polim&#x00f3;rficas</entry>
<entry>Recuperaci&#x00f3;n por uni&#x00f3;n externa (outer join)</entry>
</row>
</thead>
<tbody>
<row>
<entry>tabla por jerarqu&#x00ed;a de clases</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>soportada</emphasis></entry>
</row>
<row>
<entry>tabla por subclase</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>soportada</emphasis></entry>
</row>
<row>
<entry>tabla por clase concreta (union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (para <literal>inverse="true"</literal> solamente)</entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>soportada</emphasis></entry>
</row>
<row>
<entry>tabla por clase concreta (polimorfismo impl&#x00ed;cito)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>no soportada</emphasis></entry>
<entry><emphasis>no soportada</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>no suportadas</emphasis></entry>
<entry><emphasis>no soportada</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,478 @@
<chapter id="persistent-classes" revision="2">
<title>Clases Persistentes</title>
<para>
Clases presistentes son clases en una aplicaci&#x00f3;n que implementan las
entidades del problema de negocio (por ejemplo, Customer y Order en una
aplicaci&#x00f3;n de comercio electr&#x00f3;nico). No todas las instancias de una
clase persistente se considera que est&#x00e9;n en el estado persistente,
una instancia puede en cambio ser transitoria o estar separada.
</para>
<para>
Hibernate funciona mejor si las clases siguen algunas simples reglas, tambi&#x00e9;n
conocidas como el modelo de programaci&#x00f3;n de Viejas Clases Java Planas
(Plain Old Java Object o POJO). Sin embargo, ninguna de estas reglas son
requerimientos r&#x00ed;gidos. En cambio, Hibernate3 asume muy poco acerca de
la naturaleza de tus objetos persistentes. Puedes expresar un modelo de dominio en
otras formas: usando &#x00e1;rboles de instancias de <literal>Map</literal>,
por ejemplo.
</para>
<sect1 id="persistent-classes-pojo">
<title>Un ejemplo simple de POJO</title>
<para>
La mayor&#x00ed;a de aplicaciones Java requieren una clase
representando felinos.
</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>
Aqu&#x00ed; hay cuatro reglas principales a seguir:
</para>
<sect2 id="persistent-classes-pojo-constructor" revision="1">
<title>Implementa un constructor sin argumentos</title>
<para>
<literal>Cat</literal> tiene un contructor sin argumentos. Todas las clases persistentes
deben tener un constructor por defecto (que puede no ser p&#x00fa;blico) de modo que Hibernate
pueda instanciarlas usando <literal>Constructor.newInstance()</literal>. Recomendamos fuertemente tener
un constructor por defecto con al menos visibilidad de <emphasis>package</emphasis> para la
generaci&#x00f3;n de proxies en tiempo de ejecuci&#x00f3;n en Hibernate.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-identifier" revision="2">
<title>Provee una propiedad identificadora (opcional)</title>
<para>
<literal>Cat</literal> tiene una propiedad llamada <literal>id</literal>. Esta
propiedad mapea a la columna clave primaria de la tabla de base de datos. La propiedad
podr&#x00ed;a llamarse cualquierCosa, y su tipo podr&#x00ed;a haber sido cualquier tipo
primitivo, cualquier tipo de "envoltura" primitivo, <literal>java.lang.String</literal>
o <literal>java.util.Date</literal>. (Si tu tabla de base de datos heredada tiene claves
compuestas, puedes incluso usar una clase definida por el usuario con propiedades de
estos tipos, ver la secci&#x00f3;n sobre identificadores compuestos luego.)
</para>
<para>
La propiedad identificadora es estrictamente opcional. Puedes olvidarla y dejar que Hibernate
siga internamente la pista de los identificadores del objeto. Sin embargo, no recomendamos esto.
</para>
<para>
De hecho, alguna funcionalidad est&#x00e1; disponible s&#x00f3;lo para clases que
declaran una propiedad identificadora:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Reasociaci&#x00f3;n transitiva de objetos separados (actualizaciones o
fusiones en cascada) - ver <xref linkend="objectstate-transitive"/>
</para>
</listitem>
<listitem>
<para>
<literal>Session.saveOrUpdate()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>Session.merge()</literal>
</para>
</listitem>
</itemizedlist>
<para>
Recomendamos que declares propiedades identificadoras nombradas-consistentemente
en clases persistentes. Mas a&#x00fa;n, recomendamos que uses un tipo nulable
(es decir, no primitivo).
</para>
</sect2>
<sect2 id="persistent-classes-pojo-final">
<title>Prefiere las clases no finales (opcional)</title>
<para>
Un aspecto central de Hibernate, <emphasis>proxies</emphasis>, depende de que
las clases persistentes sean ya no finales, o sean ya la implementaci&#x00f3;n
de una interface que declare todos los m&#x00e9;todos p&#x00fa;blicos.
</para>
<para>
Puedes persistir con Hibernate clases <literal>final</literal> que no implementen una
interface, pero no ser&#x00e1;s capaz de usar proxies para recuperaci&#x00f3;n perezosa
de asociaciones, lo que limitar&#x00e1; tus opciones para afinar el rendimiento.
</para>
<para>
Debes tambi&#x00e9;n evitar declarar m&#x00e9;todos <literal>public final</literal>
en clases non-final. Si quieres usar una clase con un m&#x00e9;todo <literal>public
final</literal>, debes deshabilitar expl&#x00ed;citamente el uso de proxies estableciendo
<literal>lazy="false"</literal>.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-accessors" revision="2">
<title>Declara m&#x00e9;todos de acceso y modificaci&#x00f3;n para los campos persistentes (opcional)</title>
<para>
<literal>Cat</literal> declara m&#x00e9;todos de acceso para todos sus campos persistente.
Muchas otras herramientas ORM persisten directamente variables de instancia. Creemos que
es mejor proveer una indirecci&#x00f3;n entre el esquema relacional y las estructuras internas de la clase.
Por defecto, Hibernate persiste propiedades del estilo JavaBeans, y reconoce nombres de m&#x00e9;todo
de la forma <literal>getFoo</literal>, <literal>isFoo</literal> y <literal>setFoo</literal>.
Puedes cambiar a acceso directo a campos para propiedades en particular, de ser necesario.
</para>
<para>
Las propiedades <emphasis>no</emphasis> necesitan ser declaradas p&#x00fa;blicas. Hibernate puede
persistir una propiedad con un par get / set <literal>protected</literal> o <literal>private</literal>.
</para>
</sect2>
</sect1>
<sect1 id="persistent-classes-inheritance">
<title>Implementando herencia</title>
<para>
Una subclase puede a su vez observar la primera y segunda regla. Hereda su
propiedad identificadora de la superclase, <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>Implementando <literal>equals()</literal> y <literal>hashCode()</literal></title>
<para>
Tienes que sobrescribir los m&#x00e9;todos <literal>equals()</literal> y <literal>hashCode()</literal>
si :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
piensas poner instancias de clases persistentes en un <literal>Set</literal>
(la forma recomendada de representar asociaciones multivaluadas)
<emphasis>y</emphasis>
</para>
</listitem>
<listitem>
<para>
piensas usar reasociaci&#x00f3;n de instancias separadas.
</para>
</listitem>
</itemizedlist>
<para>
Hibernate garantiza la equivalencia de identidad persistente (fila de base de datos) y
identidad Java s&#x00f3;lo dentro del &#x00e1;mbito de una sesi&#x00f3;n en particular.
De modo que en el momento que mezclamos instancias recuperadas en sesiones diferentes,
debemos implementar <literal>equals()</literal> y <literal>hashCode()</literal> si
deseamos tener una sem&#x00e1;ntica significativa de <literal>Set</literal>s.
</para>
<para>
La forma m&#x00e1;s obvia es implementar <literal>equals()</literal>/<literal>hashCode()</literal>
comparando el valor identificador de ambos objetos. Si el valor es el mismo, ambos deben ser
la misma fila de base de datos, por lo tanto son iguales (si ambos son agregados a un
<literal>Set</literal>, s&#x00f3;lo tendremos un elemento en el <literal>Set</literal>).
Desafortunadamente, no podemos usar este enfoque con identificadores generados! Hibernate s&#x00f3;lo
asignar&#x00e1; valores identificadores a objetos que son persistentes, una instancia reci&#x00e9;n
creada no tendr&#x00e1; ning&#x00fa;n valor identificador! Adem&#x00e1;s, si una instancia no est&#x00e1;
salvada y est&#x00e1; actualmente en un <literal>Set</literal>, salvarla asignar&#x00e1; un
valor identificador al objeto. Si <literal>equals()</literal> and <literal>hashCode()</literal>
est&#x00e1;n basados en el valor identificador, el c&#x00f3;digo hash podr&#x00ed;a cambiar,
rompiendo el contrato de <literal>Set</literal>. Ver el sitio web de Hibernate para una
discusi&#x00f3;n completa de este problema. Observa que esto no es una incidencia de Hibernate,
sino la sem&#x00e1;ntica normal de Java de identidad de objeto e igualdad.
</para>
<para>
Recomendamos implementar <literal>equals()</literal> y <literal>hashCode()</literal>
usando <emphasis>igualdad de clave de negocio (Business key equality)</emphasis>.
Igualdad de clave de negocio significa que el m&#x00e9;todo <literal>equals()</literal>
compara s&#x00f3;lo las propiedades que forman la clave de negocio, una clave que podr&#x00ed;a
identificar nuestra instancia en el mundo real (una clave candidata
<emphasis>natural</emphasis>):
</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>
Nota que una clave de negocio no tiene que ser tan s&#x00f3;lida como
una clave primaria candidata de base de datos (ver
<xref linkend="transactions-basics-identity"/>). Las propiedades inmutables o
&#x00fa;nicas son usualmente buenas candidatas para una clave de negocio.
</para>
</sect1>
<sect1 id="persistent-classes-dynamicmodels">
<title>Modelos din&#x00e1;micos</title>
<para>
<emphasis>Ten en cuenta que las siguientes funcionalidades est&#x00e1;n
consideradas actualmente experimentales y pueden cambiar en el futuro
cercano.</emphasis>
</para>
<para>
Las entidades persistentes no necesariamente tienen que estar representadas
como clases POJO o como objetos JavaBean en tiempo de ejecuci&#x00f3;n. Hibernate
soporta adem&#x00e1;s modelos din&#x00e1;micos (usando <literal>Map</literal>s de
<literal>Map</literal>s en tiempo de ejecuci&#x00f3;n) y la representaci&#x00f3;n
de entidades como &#x00e1;rboles de DOM4J. Con este enfoque no escribes clases
persistentes, s&#x00f3;lo ficheros de mapeo.
</para>
<para>
Por defecto, Hibernate funciona en modo POJO normal. Puedes establecer una
representaci&#x00f3;n de entidad por defecto para una <literal>SessionFactory</literal>
en particular usando la opci&#x00f3;n de configuraci&#x00f3;n
<literal>default_entity_mode</literal>
(ver <xref linkend="configuration-optional-properties"/>).
</para>
<para>
Los siguientes ejemplos demuestran la representaci&#x00f3;n usando
<literal>Map</literal>s. Primero, en el fichero de mapeo,
tiene que declararse un <literal>entity-name</literal> en vez de
(o como agregado a) un nombre de clase:
</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>
Ten en cuenta que aunque las asociaciones se declaran usando nombres
de clase objetivo, el tipo objetivo de una asociaci&#x00f3;n puede
ser adem&#x00e1;s una entidad din&#x00e1;mica en vez de un POJO.
</para>
<para>
Despu&#x00e9;s de establecer el modo de entidad por defecto a
<literal>dynamic-map</literal> para la <literal>SessionFactory</literal>,
podemos trabajar en tiempo de ejecuci&#x00f3;n con <literal>Map</literal>s
de <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>
Las ventajas de un mapeo din&#x00e1;mico es r&#x00e1;pido tiempo de ciclo
de prototipado sin la necesidad de implementaci&#x00f3;n de clases de entidad.
Sin embargo, pierdes chequeo de tipos en tiempo de compilaci&#x00f3;n y
muy probablemente tratar&#x00e1;s con muchas excepciones en tiempo de ejecuci&#x00f3;n.
Gracias al mapeo de Hibernate, el esquema de base de datos puede estar facilmente
sano y normalizado, permitiendo agregar una implementaci&#x00f3;n apropiada del
modelo de dominio m&#x00e1;s tarde.
</para>
<para>
Los modos de representaci&#x00f3;n de entidad pueden ser establecidos
por <literal>Session</literal>:
</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>
Por favor, ten en cuenta que la llamada a <literal>getSession()</literal>
usando un <literal>EntityMode</literal> est&#x00e1; en la API de
<literal>Session</literal>, no en la de <literal>SessionFactory</literal>.
De esta forma, la nueva <literal>Session</literal> comparte la conexi&#x00f3;n
JDBC, transacci&#x00f3;n y otra informaci&#x00f3;n de contexto. Esto significa
que no tienes que llamar a <literal>flush()</literal> ni a <literal>close()</literal>
en la <literal>Session</literal> secundaria, y tembi&#x00e9;n dejar el manejo
de la transacci&#x00f3;n y de la conexi&#x00f3;n a la unidad de trabajo primaria.
</para>
<para>
Puede encontrarse m&#x00e1;s informaci&#x00f3;n sobre las capacidades de
representaci&#x00f3;n XML en <xref linkend="xml"/>.
</para>
</sect1>
<para>
PORHACER: Documentar el framework de extensiones del usuario en los paquetes
de propiedad y proxies.
</para>
</chapter>

View File

@ -0,0 +1,431 @@
<chapter id="querycriteria">
<title>Consultas por Criterios</title>
<para>
Acompa&#x00f1;a a Hibernate una API de consultas por criterios intuitiva y extensible.
</para>
<sect1 id="querycriteria-creating">
<title>Creando una instancia de <literal>Criteria</literal></title>
<para>
La interface <literal>org.hibernate.Criteria</literal> representa una consulta contra
una clase persistente en particular. La <literal>Session</literal> es una f&#x00e1;brica de instancias
de <literal>Criteria</literal>.
</para>
<programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-narrowing">
<title>Estrechando el conjunto resultado</title>
<para>
Un criterio individual de consulta es una instancia de la interface
<literal>org.hibernate.criterion.Criterion</literal>. La clase
<literal>org.hibernate.criterion.Restrictions</literal> define m&#x00e9;todos de f&#x00e1;brica para obtener ciertos tipos
prefabricados de <literal>Criterion</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();]]></programlisting>
<para>
Las restricciones pueden ser agrupadas l&#x00f3;gicamente.
</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>
Hay un gran rango de tipos de criterio prefabricados (subclases de <literal>Restrictions</literal>),
pero uno que es especialmente útil te deja especificar SQL directamente.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
.list();]]></programlisting>
<para>
El sitio <literal>{alias}</literal> ser&#x00e1; remplazado por el alias de fila de la entidad consultada.
</para>
<para>
Un enfoque alternativo para obtener un criterio es tomarlo de una instancia de
<literal>Property</literal>. Puedes crear una <literal>Property</literal> llamando a
<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>Ordenando los resultados</title>
<para>
Puedes ordenar los resultados usando <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">
<title>Asociaciones</title>
<para>
Puedes especificar f&#x00e1;cilmente restricciones sobre las entidades relacionadas al navegar asociaciones
usando <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>
nota que el segundo <literal>createCriteria()</literal> devuelve una nueva instancia de
<literal>Criteria</literal>, que hace referencia a los elementos de la colecci&#x00f3;n
<literal>kittens</literal>.
</para>
<para>
La siguiente forma alternativa es útil en ciertas circunstancias.
</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> no crea una nueva instancia de
<literal>Criteria</literal>.)
</para>
<para>
&#x00a1;Observa que las colecciones de gatitos tenidas por las instancias de <literal>Cat</literal> devueltas
por las dos consultas previas <emphasis>no</emphasis> est&#x00e1;n prefiltradas por los criterios! Si deseas
recuperar s&#x00f3;lo los gatitos que emparejen los criterios, debes usar <literal>returnMaps()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.returnMaps()
.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>Recuperaci&#x00f3;n din&#x00e1;mica de asociaciones</title>
<para>
Puedes especificar la sem&#x00e1;ntica de recuperaci&#x00f3;n de asociaciones en tiempo de ejecuci&#x00f3;n usando
<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>
Esta consulta recuperar&#x00e1; tanto <literal>mate</literal> como <literal>kittens</literal> por
uni&#x00f3;n exterior (outer join). Ver <xref linkend="performance-fetching"/> para m&#x00e1;s informaci&#x00f3;n.
</para>
</sect1>
<sect1 id="querycriteria-examples">
<title>Consultas por ejemplos</title>
<para>
La clase <literal>org.hibernate.criterion.Example</literal> te permite construir un criterio de consulta
a partir de una instancia dada.
</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>
Las propiedades de versi&#x00f3;n, los identificadores y las asociaciones son ignorados. Por defecto,
las propiedades valuadas a nulo son exclu&#x00ed;das.
</para>
<para>
Puedes ajustar c&#x00f3;mo se aplica el <literal>Example</literal>.
</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>
Puedes incluso usar ejemplos para colocar criterios sobre objetos asociados.
</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>Proyecciones, agregaci&#x00f3;n y agrupamiento</title>
<para>
La clase <literal>org.hibernate.criterion.Projections</literal> es una f&#x00e1;brica de instancias de
<literal>Projection</literal>. Aplicamos una proyecci&#x00f3;n a una consulta llamando a
<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>
No es necesario ningún "group by" expl&#x00ed;cito en una consulta por criterios.
Ciertos tipos de proyecciones son definidos para ser <emphasis>proyecciones agrupadas</emphasis>,
que adem&#x00e1;s aparecen en la cl&#x00e1;usula SQL <literal>group by</literal>.
</para>
<para>
Puede opcionalmente asignarse un alias a una proyecci&#x00f3;n, de modo que el valor proyectado pueda
ser referido en restricciones u ordenamientos. Aqu&#x00ed; hay dos formas diferentes de hacer esto:
</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>
Los m&#x00e9;todos <literal>alias()</literal> y <literal>as()</literal> simplemente envuelven una instancia
de proyecci&#x00f3;n en otra instancia de <literal>Projection</literal> con alias. Como un atajo, puedes asignar
un alias cuando agregas la proyecci&#x00f3;n a una lista de proyecciones:
</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>
Puedes tambi&#x00e9;n usar <literal>Property.forName()</literal> para expresar proyecciones:
</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>Consultas y subconsultas separadas</title>
<para>
La clase <literal>DetachedCriteria</literal> te deja crear una consulta fuera del &#x00e1;mbito de una sesi&#x00f3;n,
y entonces ejecutarla luego usando alguna <literal>Session</literal> arbitraria.
</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>
Tambi&#x00e9;n una <literal>DetachedCriteria</literal> puede usarse para expresar una subconsulta.
Las instancias de Criterion implicando subconsultas pueden obtenerse v&#x00ed;a <literal>Subqueries</literal> o
<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>
Incluso son posibles las subconsultas correlacionadas:
</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>Consultas por identificador natural</title>
<para>
Para la mayor&#x00ed;a de consultas, incluyendo las consultas por criterios, el cach&#x00e9; de consulta no es
muy eficiente, debido a que la invalidaci&#x00f3;n del cach&#x00e9; de consulta ocurre demasiado frecuentemente.
Sin embargo, hay un tipo especial de consulta donde podemos optimizar el algoritmo de invalidaci&#x00f3;n
de cach&#x00e9;: búsquedas por una clave natural constante. En algunas aplicaciones, este tipo de consulta,
ocurre frecuentemente. La API de criterios brinda especial provisi&#x00f3;n para este caso de uso.
</para>
<para>
Primero, debes mapear la clave natural de tu entidad usando
<literal>&lt;natural-id&gt;</literal>, y habilitar el uso del cach&#x00e9; de segundo nivel.
</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>
Nota que esta funcionalidad no est&#x00e1; pensada para uso con entidades con claves naturales
<emphasis>mutable</emphasis>.
</para>
<para>
Seguido, habilita el cach&#x00e9; de consulta de Hibernate.
</para>
<para>
Ahora, <literal>Restrictions.naturalId()</literal> nos permite hacer uso de el algoritmo de cach&#x00e9;
m&#x00e1;s eficiente.
</para>
<programlisting><![CDATA[session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")
.set("org", "hb")
).setCacheable(true)
.uniqueResult();]]></programlisting>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,477 @@
<chapter id="querysql" revision="2">
<title>SQL Nativo</title>
<para>
Puedes tambi&#x00e9;n expresar consultas en el dialecto SQL nativo de tu base de datos. Esto es &#x00fa;til si quieres
utilizar aspectos espec&#x00ed;ficos de base de datos tal como consejos (hints) de consulta o la palabra clave
<literal>CONNECT</literal> en Oracle. Provee adem&#x00e1;s una clara ruta de migraci&#x00f3;n desde una aplicaci&#x00f3;n
basada en SQL/JDBC directo a Hibernate.
</para>
<para>
Hibernate3 te permite especificar SQL escrito a mano (incluyendo procedimientos almacenados) para todas
las operaciones de creaci&#x00f3;n, actualizaci&#x00f3;n, borrado y carga.
</para>
<sect1 id="querysql-creating">
<title>Creando una <literal>Query</literal> de SQL nativo</title>
<para>
Las consultas SQL se controlan por medio de la interface <literal>SQLQuery</literal>, que se obtiene
llamando a <literal>Session.createSQLQuery()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class)
.setMaxResults(50)
.list();]]></programlisting>
<para>
Esta consulta especificada:
</para>
<itemizedlist>
<listitem>
<para>
la cadena de consulta SQL, con un lugar para que Hibernate inyecte los alias de columnas
</para>
</listitem>
<listitem>
<para>
la entidad devuelta por la consulta, y sus alias de tablas SQL
</para>
</listitem>
</itemizedlist>
<para>
El m&#x00e9;todo <literal>addEntity()</literal> asocia alias de tablas SQL con clases de entidad,
y determina la forma del conjunto resultado de la consulta.
</para>
<para>
El m&#x00e9;todo <literal>addJoin()</literal> puede ser usado para cargar asociaciones a otras entidades y
colecciones.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.addEntity("cat", Cat.class)
.addJoin("kitten", "cat.kittens")
.list();]]></programlisting>
<para>
Una consulta SQL nativa podr&#x00ed;a devolver un valor escalar simple o una combinaci&#x00f3;n de escalares y entidades.
</para>
<programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();]]></programlisting>
</sect1>
<sect1 id="querysql-aliasreferences">
<title>Alias y referencias de propiedad</title>
<para>
La notaci&#x00f3;n <literal>{cat.*}</literal> usada arriba es un atajo para "todas las propiedades".
Alternativamente, puedes listar las columnas expl&#x00ed;citamente, pero incluso en este caso dejamos
que Hibernate inyecte los alias de columnas SQL para cada propiedad. El lugar para un alias de columna
es s&#x00f3;lo el nombre de propiedad cualificado por el alias de la tabla. En el siguiente ejemplo,
recuperamos <literal>Cat</literal>s de una tabla diferente (<literal>cat_log</literal>) a una
declarada en los metadatos de mapeo. Nota que podr&#x00ed;amos incluso usar los alias de propiedad en la
cl&#x00e1;usula where si quisieramos.
</para>
<para>
La sint&#x00e1;xis <literal>{}</literal> <emphasis>no</emphasis> es requerida para consultas con nombre.
Ver <xref linkend="querysql-namedqueries"/>
</para>
<programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " +
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
"from cat_log cat where {cat.mate} = :catId"
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.setLong("catId", catId)
.list();]]></programlisting>
<para>
<emphasis>Nota:</emphasis> si listas cada propiedad expl&#x00ed;citamente, &#x00a1;debes incluir todas las
propiedades de la clase <emphasis>y sus subclases</emphasis>!
</para>
</sect1>
<sect1 id="querysql-namedqueries" revision="2">
<title>Consultas SQL con nombre</title>
<para>
Las consultas SQL con nombre pueden definirse en el documento de mapeo y llamadas exactamente
en la misma forma en que a una consulta HQL con nombre. En este caso, <emphasis>no</emphasis>
necesitamos llamar a <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>
Los elementos <literal>&lt;return-join&gt;</literal> y <literal>&lt;load-collection&gt;</literal>
se usan para unir asociaciones y definir consultas que inicialicen colecciones, respectivamente.
</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},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<para>
Una consulta SQL con nombre puede devolver un valor escalar. Debes especificar el alias de columna y
tipo Hibernate usando el elementp <literal>&lt;return-scalar&gt;</literal>:
</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>
<sect2 id="propertyresults">
<title>Usando return-property para especificar expl&#x00ed;citamente nombres de columna/alias</title>
<para>
Con <literal>&lt;return-property&gt;</literal> puedes decirle expl&#x00ed;citamente a Hibernate qu&#x00e9;
alias de columna usar, en vez de usar la sint&#x00e1;xis <literal>{}</literal> para dejar que Hibernate
inyecte sus propios alias.
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
]]></programlisting>
<para>
<literal>&lt;return-property&gt;</literal> tambi&#x00e9;n trabaja con m&#x00fa;ltiples columnas. Esto resuelve una
limitaci&#x00f3;n de la sint&#x00e1;xis <literal>{}</literal>, la cual no puede permitir un control fino de propiedades
multi-columna.
</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>
Nota que en este ejemplo hemos usado <literal>&lt;return-property&gt;</literal> en combinaci&#x00f3;n con
la sint&#x00e1;xis <literal>{}</literal> para inyecci&#x00f3;n, permitiendo a los usuarios elejir c&#x00f3;mo quieren
referirse a las columnas y propiedades.
</para>
<para>
Si tu mapeo tiene un discriminador debes usar <literal>&lt;return-discriminator&gt;</literal>
para especificar la columna discriminadora.
</para>
</sect2>
<sect2 id="sp_query">
<title>Usando procedimientos almacenados para consultar</title>
<para>
Hibernate3 introduce soporte para consultas v&#x00ed;a procedimientos almacenados. Los procedimientos
almacenados deben devolver un conjunto resultado como el primer par&#x00e1;metro de salida para ser
capaces de funcionar con Hibernate. Un ejemplo de uno procedimiento almacenado en Oracle 9
o superior es as&#x00ed;:
</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>
Para usar esta consulta en Hibernate necesitas mapearla por medio de una consulta con nombre.
</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>
Nota que los procedimientos almacenados s&#x00f3;lo devuelven escalares y entidades.
No est&#x00e1;n soportados <literal>&lt;return-join&gt;</literal> y <literal>&lt;load-collection&gt;</literal>.
</para>
<sect3 id="querysql-limits-storedprocedures">
<title>Reglas/limitaciones para usar procedimientos almacenados</title>
<para>
Para usar procedimientos almacenados con Hibernate los procedimientos tienen que seguir algunas reglas.
Si no siguen esas reglas no son usables por Hibernate. Si a&#x00fa;n quisieras usar estos procedimientos
tendr&#x00ed;as que ejecutarlos por medio de <literal>session.connection()</literal>. Las reglas son
diferentes para cada base de datos, ya que los vendedores de base de datos tienen diferentes
sem&#x00e1;nticas/sint&#x00e1;xis de procedimientos almacenados.
</para>
<para>
Las consultas de procedimientos almacenados no pueden ser paginadas con
<literal>setFirstResult()/setMaxResults()</literal>.
</para>
<para>
Para Oracle se aplican las siguientes reglas:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
El procedimiento debe devolver un conjunto resultado. Esto se hace devolviendo un
<literal>SYS_REFCURSOR</literal> en Oracle 9 o 10. En Oracle necesitas definir un
tipo <literal>REF CURSOR</literal>.
</para>
</listitem>
<listitem>
<para>
La forma recomendada es <literal>{ ? = call procName(&lt;parameters&gt;) }</literal> o
<literal>{ ? = call procName }</literal> (esto es m&#x00e1;s una regla de Oracle que una regla de Hibernate).
</para>
</listitem>
</itemizedlist>
<para>
Para Sybase o MS SQL server se aplican las siguientes reglas:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
El procedimiento debe devolver un conjunto resultado. Nota que ya que estos servidores pueden
y devolver&#x00e1;n m&#x00fa;ltiples conjuntos resultados y cuentas de actualizaci&#x00f3;n, Hibernate iterar&#x00e1;
los resultados y tomar&#x00e1; el primer resultado que sea un conjunto resultado como su valor
a devolver. Todo lo dem&#x00e1;s ser&#x00e1; descartado.
</para>
</listitem>
<listitem>
<para>
Si habilitas <literal>SET NOCOUNT ON</literal> en tu procedimiento ser&#x00e1; probablemente m&#x00e1;s
eficiente, pero esto no es un requerimiento.
</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</sect1>
<sect1 id="querysql-cud">
<title>SQL personalizado para crear, actualizar y borrar</title>
<para>
Hibernate3 puede usar sentencias SQL personalizadas para las operaciones de
crear, actualizar y borrar. Los persistidores de clases y colecciones en Hibernate
ya contienen un conjunto de cadenas generadas en tiempo de configuraci&#x00f3;n (insertsql,
deletesql, updatesql, etc.). Las etiquetas de mapeo <literal>&lt;sql-insert&gt;</literal>,
<literal>&lt;sql-delete&gt;</literal>, y <literal>&lt;sql-update&gt;</literal> sobrescriben
estas cadenas:
</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>
El SQL se ejecuta directamente en tu base de datos, de modo que eres libre de usar cualquier
dialecto que quieras. Esto reducir&#x00e1;, por supuesto, la portabilidad de tu mapeo si usas SQL
espec&#x00ed;fico de la base de datos.
</para>
<para>
Los procedimientos almacenados son soportados si est&#x00e1; establecido el atributo
<literal>callable</literal>:
</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>
El orden de los par&#x00e1;metros posicionales son actualmente vitales, ya que deben estar en la
misma secuencia en que las espera Hibernate.
</para>
<para>
Puedes ver el orden esperado habilitando el registro de depuraci&#x00f3;n para el nivel
<literal>org.hibernate.persister.entity</literal>. Con este nivel habilitado, Hibernate
imprimir&#x00e1; el SQL est&#x00e1;tico que se usa para crear, actualizar, borrar, etc. las entidades.
(Para ver la secuencia esperada, recuerda no incluir tu SQL personalizado en los ficheros
de mapeo ya que sobrescribir&#x00e1;n el sql est&#x00e1;tico generado por Hibernate.)
</para>
<para>
Los procedimientos almacenados son, en la mayor&#x00ed;a de los casos (l&#x00e9;ase, mejor hacerlo que no hacerlo),
obligados a devolver el n&#x00fa;mero de filas insertadas/actualizadas/borradas, ya que Hibernate tiene algunas
comprobaciones en tiempo de ejecuci&#x00f3;n del &#x00e9;xito de la sentencia. Hibernate siempre registra el primer
par&#x00e1;metro de la sentencia como un par&#x00e1;metro de salida num&#x00e9;rico para las operaciones CUD:
</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>SQL personalizado para carga</title>
<para>
Puedes tambi&#x00e9;n declarar tu propias consultas SQL (o HQL) para cargar entidades:
</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>
Esto es s&#x00f3;lo una declaraci&#x00f3;n de consulta con nombrem como se ha discutido anteriormente.
Puedes hacer referencia a esta consulta con nombre en un mapeo de clase:
</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>
Esto incluso funciona con procedimientos almacenados.
</para>
<para>
Puedes incluso definit una consulta para la carga de colecciones:
</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>
Podr&#x00ed;as incluso definir un cargador de entidades que cargue una colecci&#x00f3;n por
recuperaci&#x00f3;n por uni&#x00f3;n (join fetching):
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,666 @@
<chapter id="quickstart">
<title>Comienzo r&#x00e1;pido con Tomcat</title>
<sect1 id="quickstart-intro" revision="2">
<title>Empezando con Hibernate</title>
<para>
Este tutorial explica una instalaci&#x00f3;n de Hibernate con el
contenedor de servlets Apache Tomcat (hemos usado la versi&#x00f3;n 4.1,
las diferencias con la 5.0 deben ser m&#x00ed;nimas) para una aplicaci&#x00f3;n
basada en web. Hibernate trabaja bien en un entorno manejado con
todos los servidores de aplicaciones J2EE importantes, o incluso en aplicaciones
Java independientes. El sistema de base de datos es s&#x00f3;lo una cuesti&#x00f3;n
de cambiar la configuraci&#x00f3;n del dialecto SQL de Hibernate y las
propiedades de conexi&#x00f3;n.
</para>
<para>
Primero, tenemos que copiar todas las bibliotecas requeridas a la instalaci&#x00f3;n
de Tomcat. Usamos un contexto web separado (<literal>webapps/quickstart</literal>)
para este tutorial, de modo que tenemos que considerar tanto la ruta de b&#x00fa;squeda
de bibliotecas global (<literal>TOMCAT/common/lib</literal>) como tambi&#x00e9;n
el cargador de clases a nivel de contexto en <literal>webapps/quickstart/WEB-INF/lib</literal>
(para ficheros JAR) y <literal>webapps/quickstart/WEB-INF/classes</literal>.
Nos referiremos a ambos niveles de cargador de clases como el classpath global y el classpath
de contexto, respectivamente.
</para>
<para>
Ahora, copia las bibliotecas a los dos classpaths:
</para>
<orderedlist>
<listitem>
<para>
Copia el driver JDBC para la base de datos al classpath global. Esto se
requiere para el software de pool de conexiones DBCP que se distribuye
con Tomcat. Hibernate usa conexiones JDBC para ejecutar SQL sobre la base de
datos, de modo que, o bien tienes que proveer conexiones JDBC en pool,
o bien configurar Hibernate para que use uno de los pools soportados
directamente (C3P0, Proxool). Para este tutorial, copia la biblioteca
<literal>pg74jdbc3.jar</literal> (para PostgreSQL 7.4 y JDK 1.4) al
classpath del cargador global. Si quisieras usar una base de datos diferente,
simplemente copia su apropiado driver JDBC.
</para>
</listitem>
<listitem>
<para>
Nunca copies nada m&#x00e1;s dentro de la ruta del cargador de clases global
en Tomcat, o tendr&#x00e1;s problemas con varias herramientas, incluyendo
Log4J, commons-logging y otras. Siempre usa el classpath de contexto para
cada aplicaci&#x00f3;n web, esto es, copia las bibliotecas a
<literal>WEB-INF/lib</literal> y tus propias clases y ficheros de
configuraci&#x00f3;n/propiedades a <literal>WEB-INF/classes</literal>.
Ambos directorios est&#x00e1;n a nivel del classpath de contexto por defecto.
</para>
</listitem>
<listitem>
<para>
Hibernate est&#x00e1; empaquetado como una biblioteca JAR. El fichero
<literal>hibernate3.jar</literal> debe ser copiado en el classpath de contexto
junto a las otras clases de la aplicaci&#x00f3;n. Hibernate requiere algunas
bibliotecas de terceros en tiempo de ejecuci&#x00f3;n; &#x00e9;stas vienen
inclu&#x00ed;das con la distribuci&#x00f3;n de Hibernate en el directorio
<literal>lib/</literal>. Ver <xref linkend="3rdpartylibs"/>. Copia las
bibliotecas de terceros requeridas al classpath de contexto.
</para>
</listitem>
</orderedlist>
<table frame="topbot" id="3rdpartylibs">
<title>
Bibliotecas de terceros de Hibernate
</title>
<tgroup cols="2" rowsep="1" colsep="1">
<colspec colname="c1" colwidth="1*"/>
<colspec colname="c2" colwidth="2*"/>
<thead>
<row>
<entry align="center">
Biblioteca
</entry>
<entry align="center">
Descripci&#x00f3;n
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
antlr (requerida)
</entry>
<entry>
Hibernate usa ANTLR para producir analizadores de consultas,
esta biblioteca tambi&#x00e9;n se necesita en tiempo de ejecuci&#x00f3;n.
</entry>
</row>
<row>
<entry>
dom4j (requerida)
</entry>
<entry>
Hibernate usa dom4j para analizar ficheros de configuraci&#x00f3;n
XML y ficheros de metadatos de mapeo XML.
</entry>
</row>
<row>
<entry>
CGLIB, asm (requerida)
</entry>
<entry>
Hibernate usa la biblioteca de generaci&#x00f3;n de c&#x00f3;digo
para aumentar las clases en tiempo de ejecuci&#x00f3;n
(en combinaci&#x00f3;n con reflecci&#x00f3;n Java).
</entry>
</row>
<row>
<entry>
Commons Collections, Commons Logging (requeridas)
</entry>
<entry>
Hibernate usa varias bibliotecas de utilidad del proyecto
Jakarta Commons de Apache.
</entry>
</row>
<row>
<entry>
EHCache (requerida)
</entry>
<entry>
Hibernate puede usar varios provedores de cach&#x00e9; para
el cach&#x00e9; de segundo nivel. EHCache es el provedor de
cach&#x00e9; por defecto si no se cambia en la configuraci&#x00f3;n.
</entry>
</row>
<row>
<entry>
Log4j (opcional)
</entry>
<entry>
Hibernate usa la API de Commons Logging, que a su vez puede
usar Log4J como el mecanismo de logging subyacente. Si la
biblioteca Log4J est&#x00e1; disponible en el directorio de
bibliotecas del contexto, Commons Logging usar&#x00e1; Log4J
y la configuraci&#x00f3;n <literal>log4j.properties</literal>
en el classpath de contexto. Un fichero de propiedades de ejemplo
para Log4J se incluye con la distribuci&#x00f3;n de Hibernate.
As&#x00ed; que copia log4j.jar y el fichero de configuraci&#x00f3;n
(de <literal>src/</literal>) a tu classpath de contexto si quieres
ver que ocurre tras esc&#x00e9;nas.
</entry>
</row>
<row>
<entry>
¿Requerida o no?
</entry>
<entry>
Echa una mirada al fichero <literal>lib/README.txt</literal> en la
distribuci&#x00f3;n de Hibernate. Esta es una lista actualizada
de bibliotecas de terceros distribu&#x00ed;das con Hibernate.
Encontrar&#x00e1;s listadas ah&#x00ed; todas las bibliotecas
requeridas y opcionales (Observa que "buildtame required" significa
aqu&#x00ed; para la construcci&#x00f3;n de Hibernate, no de tu
aplicaci&#x00f3;n).
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Ahora instalamos el pooling y modo compartido de conexiones de base de datos
tanto en Tomcat como Hibernate. Esto significa que Tomcat proveer&#x00e1;
conexiones JDBC en pool (usando su funcionalidad prefabricada de pooling DBCP).
Hibernate pide esas conexiones a trav&#x00e9;s de JNDI. Alternativamente,
puedes dejar que Hibernate maneje el pool de conexiones. Tomcat liga su pool
de conexiones a JNDI; agregamos una declaraci&#x00f3;n de recurso al fichero
de configuraci&#x00f3;n principal de Tomcat, <literal>TOMCAT/conf/server.xml</literal>:
</para>
<programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
<Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/quickstart">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- DBCP database connection settings -->
<parameter>
<name>url</name>
<value>jdbc:postgresql://localhost/quickstart</value>
</parameter>
<parameter>
<name>driverClassName</name><value>org.postgresql.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>quickstart</value>
</parameter>
<parameter>
<name>password</name>
<value>secret</value>
</parameter>
<!-- DBCP connection pooling options -->
<parameter>
<name>maxWait</name>
<value>3000</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>100</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>10</value>
</parameter>
</ResourceParams>
</Context>]]></programlisting>
<para>
El contexto que configuramos en este ejemplo se llama <literal>quickstart</literal>,
su base es el directorio <literal>TOMCAT/webapp/quickstart</literal>. Para acceder
a cualquier servlet, llama a la ruta <literal>http://localhost:8080/quickstart</literal>
en tu navegador (por supuesto, agregando el nombre del servlet como se mapee en tu
<literal>web.xml</literal>). Puedes tambi&#x00e9;n ir m&#x00e1;s all&#x00e1; y crear
ahora un servlet simple que tenga un m&#x00e9;todo <literal>process()</literal>
vac&#x00ed;o.
</para>
<para>
Tomcat provee ahora conexiones a trav&#x00e9;s de JNDI en
<literal>java:comp/env/jdbc/quickstart</literal>. Si tienes problemas obteniendo
el pool de conexiones en ejecuci&#x00f3;n, refi&#x00e9;rete a la documentaci&#x00f3;n
de Tomcat. Si obtienes mensajes de excepci&#x00f3;n del driver JDBC, intenta instalar
primero el pool de conexiones JDBC sin Hibernate. Hay disponibles en la Web
tutoriales de Tomcat y JDBC.
</para>
<para>
Tu pr&#x00f3;ximo paso es configurar Hibernate. Hibernate tiene que saber c&#x00f3;mo
debe obtener conexiones JDBC. Usamos la configuraci&#x00f3;n de Hibernate basada en XML.
El otro enfoque, usando un ficheros de propiedad, es casi equivalente pero pierde unas
pocas funcionalidades que s&#x00ed; permite la sintaxis XML. El fichero de configuraci&#x00f3;n
XML se ubica en el classpath de contexto (<literal>WEB-INF/classes</literal>), como
<literal>hibernate.cfg.xml</literal>:
</para>
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
<property name="show_sql">false</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Mapping files -->
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
Desactivamos el registro (logging) de comandos SQL y decimos a Hibernate
qu&#x00e9; dialecto SQL de base de datos se usa y d&#x00f3;nde obtener
conexiones JDBC (declarando la direcci&#x00f3;n JNDI del pool ligado a
Tomcat). El dialecto es una configuraci&#x00f3;n requerida, las bases de
datos difieren en su interpretaci&#x00f3;n del "est&#x00e1;ndar" de SQL.
Hibernate cuidar&#x00e1; de las diferencias y viene con dialectos inclu&#x00ed;dos
para todas las principales bases de datos comerciales y de c&#x00f3;digo
abierto.
</para>
<para>
Una <literal>SessionFactory</literal> es el concepto de Hibernate
de un almac&#x00e9;n de datos solo. Pueden usarse m&#x00fa;ltiples
bases de datos creando m&#x00fa;ltiples ficheros de configuraci&#x00f3;n
XML y creando m&#x00fa;ltiples objetos <literal>Configuration</literal>
y <literal>SessionFactory</literal> en tu aplicaci&#x00f3;n.
</para>
<para>
El &#x00fa;ltimo elemento del <literal>hibernate.cfg.xml</literal>
declara <literal>Cat.hbm.xml</literal> como el nombre de un fichero
de mapeo XML para la clase persistente <literal>Cat</literal>. Este
fichero contiene los metadatos para el mapeo de la clase POJO
<literal>Cat</literal> a una tabla (o tablas) de base de datos.
Volveremos a este fichero pronto. Escribamos primero la clase POJO
y luego declaremos los metadatos de mapeo para ella.
</para>
</sect1>
<sect1 id="quickstart-persistentclass" revision="1">
<title>Primera clase persistente</title>
<para>
Hibernate trabaja mejor con el modelo de programaci&#x00f3;n de los
Viejos Objetos Planos de Java (POJOs, a veces llamados Ordinarios Objetos Planos de Java)
para clases persistentes. Un POJO es como un JavaBean, con las propiedades
de la clase accesible v&#x00ed;a m&#x00e9;todos getter y setter,
encapsulando la representaci&#x00f3;n interna de la interfaz publicamente
visible (Hibernate puede tambi&#x00e9;n acceder a los campos directamente, si se
necesita):
</para>
<programlisting><![CDATA[package org.hibernate.examples.quickstart;
public class Cat {
private String id;
private String name;
private char sex;
private float weight;
public Cat() {
}
public String getId() {
return id;
}
private void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
}]]></programlisting>
<para>
Hibernate no est&#x00e1; restringido en su uso de tipos de propiedad, todos
los tipos y tipos primitivos del JDK de Java (como <literal>String</literal>,
<literal>char</literal> y <literal>Date</literal>) pueden ser mapeados, incluyendo
clases del framework de colecciones de Java. Puedes mapearlos como valores,
colecciones de valores, o asociaciones a otras entidades. El <literal>id</literal>
es una propiedad especial que representa el identificador de base de datos (clave
primaria) de la clase. Es altamente recomendado para entidades como un
<literal>Cat</literal>. Hibernate puede usar identificadores s&#x00f3;lo
internamente, pero perder&#x00ed;amos algo de la flexibilidad en nuestra
arquitectura de aplicaci&#x00f3;n.
</para>
<para>
No tiene que implementarse ninguna interface especial para las clases persistentes
ni tienes que subclasear de una clase persistente ra&#x00ed;z en especial. Hibernate
tampoco requiere ning&#x00fa;n procesamiento en tiempo de construcci&#x00f3;n,
como manipulaci&#x00f3;n del byte-code. Se basa solamente en reflecci&#x00f3;n de Java
y aumentaci&#x00f3;n de clases en tiempo de ejecuci&#x00f3;n (a trav&#x00e9;s de CGLIB).
De modo que, sin ninguna dependencia de la clase POJO en Hibernate, podemos mapearla
a una tabla de base de datos.
</para>
</sect1>
<sect1 id="quickstart-mapping" revision="1">
<title>Mapeando el gato</title>
<para>
El fichero de mapeo <literal>Cat.hbm.xml</literal> contiene los
metadatos requeridos para el mapeo objeto/relacional. Los metadatos
incluyen la declaraci&#x00f3;n de clases persistentes y el mapeo de
propiedades (a columnas y relaciones de claves for&#x00e1;neas a otras
entidades) a tablas de base de datos.
</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>
<class name="org.hibernate.examples.quickstart.Cat" table="CAT">
<!-- A 32 hex character is our surrogate key. It's automatically
generated by Hibernate with the UUID pattern. -->
<id name="id" type="string" unsaved-value="null" >
<column name="CAT_ID" sql-type="char(32)" not-null="true"/>
<generator class="uuid.hex"/>
</id>
<!-- A cat has to have a name, but it shouldn' be too long. -->
<property name="name">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="sex"/>
<property name="weight"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Cada clase persistente debe tener un atributo identificador (realmente,
s&#x00f3;lo las clases que representen entidades, no las clases dependientes
de tipo-valor, que son mapeadas como componentes de una entidad). Esta propiedad
es usada para distinguir los objetos persistentes: Dos gatos son iguales si
<literal>catA.getId().equals(catB.getId())</literal> es verdadero. Este concepto
se llama <emphasis>identidad de base de datos (database identity)</emphasis>.
Hibernate viene empaquetado con varios generadores de identificador para diferentes
escenarios (incluyendo generadores nativos para secuencias de base de datos, tablas
de identificadores alto/bajo, e identificadores asignados por aplicaci&#x00f3;n).
Usamos el generador UUID (recomendado s&#x00f3;lo para pruebas, pues deben
preferirse las claves enteras delegadas generadas por la base de datos) y
tambi&#x00e9;n especificamos la columna <literal>CAT_ID</literal> de la tabla
<literal>CAT</literal> para el valor identificador generado por Hibernate
(como una clave primaria de la tabla).
</para>
<para>
Todas las dem&#x00e1;s propiedades de <literal>Cat</literal> son mapeadas a la
misma tabla. En el caso de la propiedad <literal>name</literal>, la hemos mapeado
con una declaraci&#x00f3;n expl&#x00ed;cita de columna de base de datos. Esto es
especialmente &#x00fa;til cuando el esquema de base de datos es generado
autom&#x00e1;ticamente (como sentencias DDL de SQL) desde la declaraci&#x00f3;n
de mapeo con la herramienta <emphasis>SchemaExport</emphasis> de Hibernate.
Todas las dem&#x00e1;s propiedades son mapeadas usando la configuraci&#x00f3;n
por defecto de Hibernate, que es lo que necesitas la mayor&#x00ed;a del tiempo.
La tabla <literal>CAT</literal> en la base de datos se ve as&#x00ed; como:
</para>
<programlisting><![CDATA[ Columna | Tipo | Modificadores
--------+-----------------------+-----------
cat_id | character(32) | not null
name | character varying(16) | not null
sex | character(1) |
weight | real |
Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
<para>
Ahora debes crear esta tabla manualmente en tu base de datos, y luego leer el
<xref linkend="toolsetguide"/> si quieres automatizar este paso con la
herramienta <literal>hbm2ddl</literal>. Esta herramienta puede crear un
DDL SQL completo, incluyendo definici&#x00f3;n de tablas, restricciones
personalizadas de tipo de columnas, restricciones de unicidad e &#x00ed;ndices.
</para>
</sect1>
<sect1 id="quickstart-playingwithcats" revision="2">
<title>Jugando con gatos</title>
<para>
Ahora estamos listos para comenzar la <literal>Session</literal> de Hibernate.
Es el <emphasis>manejador de persistencia</emphasis> que usamos para almacenar
y traer <literal>Cat</literal>s hacia y desde la base de datos. Pero primero,
tenemos que obtener una <literal>Session</literal> (unidad de trabajo de Hibernate)
de la <literal>SessionFactory</literal>:
</para>
<programlisting><![CDATA[SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();]]></programlisting>
<para>
La llamada a <literal>configure()</literal> carga el fichero de
configuraci&#x00f3;n <literal>hibernate.cfg.xml</literal> e
inicializa la instancia de <literal>Configuration</literal>.
Puedes establecer otras propiedades (e incluso cambiar los metadatos de mapeo)
accediendo a la <literal>Configuration</literal> <emphasis>antes</emphasis>
que construyas la <literal>SessionFactory</literal> (que es inmutable).
¿D&#x00f3;nde creamos la <literal>SessionFactory</literal> y c&#x00f3;mo
accedemos a ella en nuestra aplicaci&#x00f3;n?
</para>
<para>
Una <literal>SessionFactory</literal> usualmente se construye una vez,
por ejemplo, al arrancar con un servlet <emphasis>load-on-startup</emphasis>.
Esto significa tambi&#x00e9;n que no debes mantenerla en una variable de instancia
en tus servlets, sino en alguna otro sitio. Adem&#x00e1;s, necesitamos alg&#x00fa;n
tipo de <emphasis>Singleton</emphasis>, de modo que podamos acceder a la
<literal>SessionFactory</literal> f&#x00e1;cilmente en el c&#x00f3;digo de
aplicaci&#x00f3;n. El siguiente enfoque mostrado resuelve ambos problemas:
configuraci&#x00f3;n de arranque y f&#x00e1;cil acceso a una
<literal>SessionFactory</literal>.
</para>
<para>
Implementamos una clase de ayuda <literal>HibernateUtil</literal>:
</para>
<programlisting><![CDATA[import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() {
Session s = (Session) session.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}]]></programlisting>
<para>
Esta clase no s&#x00f3;lo cuida de la <literal>SessionFactory</literal>
con su inicializador static, sino que adem&#x00e1;s tiene una variable
<literal>ThreadLocal</literal> que tiene la <literal>Session</literal>
para la hebra actual. Aseg&#x00fa;rate de entender el concepto Java de una
variable local a una hebra antes de intentar usar esta ayuda. Una clase
<literal>HibernateUtil</literal> m&#x00e1;s compleja y potente puede
encontrarse en <literal>CaveatEmptor</literal>, http://caveatemptor.hibernate.org/
</para>
<para>
Una <literal>SessionFactory</literal> es segura entre hebras, muchas hebras pueden
acceder a ella concurrentemente y pedirle <literal>Session</literal>s. Una
<literal>Session</literal> no es un objeto seguro entre hebras que representa
una sola unidad-de-trabajo con la base de datos. Las <literal>Session</literal>s
se abren desde una <literal>SessionFactory</literal> y son cerradas cuando
todo el trabajo est&#x00e1; completo. Un ejemplo en el m&#x00e9;todo
<literal>process()</literal> de tu servlet podr&#x00ed;a parecerse a esto
(sin manejo de excepciones):
</para>
<programlisting><![CDATA[Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
tx.commit();
HibernateUtil.closeSession();]]></programlisting>
<para>
En una <literal>Session</literal>, cada operaci&#x00f3;n de base de datos
ocurre dentro de una transacci&#x00f3;n que a&#x00ed;sla las operaciones
de base de datos (incluso operaciones de s&#x00f3;lo lectura).
Usamos la API de <literal>Transaction</literal> de Hibernate para
abstraer de la estrategia de transacciones subyacente (en nuestro caso,
transacciones JDBC). Esto permite que nuestro c&#x00f3;digo sea desplegado
con transacciones manejadas por contenedor (usando JTA) sin cambio alguno.
</para>
<para>
Observa que puedes llamar <literal>HibernateUtil.currentSession();</literal>
tantas veces como quieras, siempre obtendr&#x00e1;s la <literal>Session</literal>
actual de esta hebra. Tienes que asegurarte que la <literal>Session</literal>
sea cerrada despu&#x00e9;s que se complete tu unidad-de-trabajo, ya sea en
c&#x00f3;digo de tu servlet o en un filtro de servlet antes que la respuesta HTTP
sea enviada. El bonito efecto colateral de la segunda opci&#x00f3;n es la
f&#x00e1;cil inicializaci&#x00f3;n perezosa: la <literal>Session</literal> todav&#x00ed;a
est&#x00e1; abierta cuando se dibuja la vista, de modo que Hibernate puede cargar
objetos no inicializados mientras navegas tu actual grafo de objetos.
</para>
<para>
Hibernate tiene varios m&#x00e9;todos que pueden ser usados para traer
objetos desde la base de datos. La forma m&#x00e1;s flexible es usando
el Lenguaje de Consulta de Hibernate (Hibernate Query Language o HQL),
que es una extensi&#x00f3;n orientada a objetos de SQL f&#x00e1;cil de
aprender:
</para>
<programlisting><![CDATA[Transaction tx = session.beginTransaction();
Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
out.println("Female Cat: " + cat.getName() );
}
tx.commit();]]></programlisting>
<para>
Hibernate tambi&#x00e9;n ofrece una API <emphasis>consulta por criterios</emphasis>
orientada a objetos que puede ser usada para formular consultas de tipo seguro.
Por supuesto, Hibernate usa <literal>PreparedStatement</literal>s y ligado de
par&#x00e1;metros para toda la comunicaci&#x00f3;n SQL con la base de datos.
Tambi&#x00e9;n puedes usar la funcionalidad de consulta SQL directa de Hibernate
u obtener una conexi&#x00f3;n plana de JDBC de una <literal>Session</literal>
en casos raros.
</para>
</sect1>
<sect1 id="quickstart-summary" revision="1">
<title>Finalmente</title>
<para>
Rasgu&#x00f1;amos solamente la superficie de Hibernate en este peque&#x00f1;o
tutorial. Por favor, observa que no incluimos ning&#x00fa;n c&#x00f3;digo
espec&#x00ed;fico de servlet en nuestros ejemplos. Tienes que crear un servlet
por t&#x00ed; mismo e insertar el c&#x00f3;digo de Hibernate como lo veas
ubicado.
</para>
<para>
Ten en mente que Hibernate, como capa de acceso a datos, est&#x00e1; firmemente
integrado dentro de tu aplicaci&#x00f3;n. Usualmente, todas las otras capas dependen
del mecanismo de persistencia. Aseg&#x00fa;rate de entender las implicaciones
de este dise&#x00f1;o.
</para>
<para>
Para un ejemplo de aplicaci&#x00f3;n m&#x00e1;s compleja, ver
http://caveatemptor.hibernate.org/ y echa una mirada a los
otros tutoriales con links en http://www.hibernate.org/Documentation
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,459 @@
<chapter id="toolsetguide" revision="2">
<title>Gu&#x00ed;a del Conjunto de Herramientas</title>
<para>
La ingenier&#x00ed;a de ida y vuelta con Hibernate es posible usando un conjunto de plugins de Eclipse,
herramientas de l&#x00ed;nea de comandos, as&#x00ed; como tareas de Ant.
</para>
<para>
Las <emphasis>Herramientas de Hibernate</emphasis> actualmente incluyen plugins para la IDE de
Eclipse as&#x00ed; como tareas de Ant para la ingenier&#x00ed;a inversa de bases de datos existentes:
</para>
<itemizedlist>
<listitem><para>
<emphasis>Editor de Mapeo:</emphasis> Un editor de ficheros de mapeo XML, que soporta autocompleci&#x00f3;n
y resaltado de sint&#x00e1;xis. Soporta tambi&#x00e9;n autocompleci&#x00f3;n sem&#x00e1;ntica de nombres de clases y nombres de
campos/propiedades, haci&#x00e9;ndolo mucho m&#x00e1;s vers&#x00e1;til que un editor de XML normal.
</para></listitem>
<listitem><para>
<emphasis>Consola:</emphasis> La consola es una nueva vista en Eclipse. Adem&#x00e1;s de la vista de
&#x00e1;rbol de tus configuraciones de consola, tienes tambi&#x00e9;n una vista interactiva de tus clases
persistentes y sus relaciones. La console te permite ejecutar consultas HQL contra tu base de datos y
navegar el resultado directamente en Eclipse.
</para></listitem>
<listitem><para>
<emphasis>Asistentes de Desarrollo:</emphasis> Se proveen muchos asistentes con las herramientas
de Eclipse. Puedes usar un asistente para generar r&#x00e1;pidamente ficheros de configuraci&#x00f3;n de Hibernate
(cfg.xml), o incluso puedes haceruna ingenier&#x00ed;a inversa completa de un esquema de base de datos existente
en ficheros de c&#x00f3;digo de POJO y ficheros de mapeo de Hibernate. El asistente de ingenier&#x00ed;a inversa soporta
plantillas personalizables.
</para></listitem>
<listitem><para>
<emphasis>Tareas de Ant:</emphasis>
</para></listitem>
</itemizedlist>
<para>
Por favor refi&#x00e9;rete al paquete <emphasis>Herramientas de Hibernate</emphasis> y su documentaci&#x00f3;n para
m&#x00e1;s informaci&#x00f3;n.
</para>
<para>
Sin embargo, el paquete principal de Hibernate viene incluyendo una herramienta integrada
(puede ser usada incluso "dentro" de Hibernate on-the-fly): <emphasis>SchemaExport</emphasis>
tambi&#x00e9;n conocido como <literal>hbm2ddl</literal>.
</para>
<sect1 id="toolsetguide-s1" revision="2">
<title>Generaci&#x00f3;n autom&#x00e1;tica de esquemas</title>
<para>
Una utilidad de Hibernate puede generar DDL desde tus ficheros de mapeo. El esquema generado incluye
restricciones de integridad referencial (claves primarias y for&#x00e1;neas) para las tablas de entidades y
colecciones. Las tablas y secuencias tambi&#x00e9;n son creadas para los generadores de identificadores mapeados.
</para>
<para>
<emphasis>Debes</emphasis> especificar un <literal>Dialecto</literal> SQL v&#x00ed;a la propiedad
<literal>hibernate.dialect</literal> al usar esta herramienta, ya que el DDL es altamente espec&#x00ed;fico del
vendedor.
</para>
<para>
First, customize your mapping files to improve the generated schema.
</para>
<sect2 id="toolsetguide-s1-2" revision="1">
<title>Personalizando el esquema</title>
<para>
Muchos elementos de mapeo de Hibernate definen un atributo opcional llamado <literal>length</literal>.
Con este atributo puedes establecer el tama&#x00f1;o de una columna. (O, para tipos de datos
num&#x00e9;ricos/decimales, la precisi&#x00f3;n.)
</para>
<para>
Algunas etiquetas tambi&#x00e9;n aceptan un atributo <literal>not-null</literal> (para generar una restricci&#x00f3;n
<literal>NOT NULL</literal> en columnas de tablas) y y un atributo <literal>unique</literal> (para generar
restricciones <literal>UNIQUE</literal> en columnas de tablas).
</para>
<para>
Algunas etiquetas aceptan un atributo <literal>index</literal> para especificar el nombre de un &#x00ed;ndice
para esa columna. Se puede usar un atributo <literal>unique-key</literal> para agrupar columnas en una
restricci&#x00f3;n de clave de una sola unidad. Actualmente, el valor especificado del atributo
<literal>unique-key</literal> <emphasis>no</emphasis> es usado para nombrar la restricci&#x00f3;n, s&#x00f3;lo para
agrupar las columnas en el fichero de mapeo.
</para>
<para>
Ejemplos:
</para>
<programlisting><![CDATA[<property name="foo" type="string" length="64" not-null="true"/>
<many-to-one name="bar" foreign-key="fk_foo_bar" not-null="true"/>
<element column="serial_number" type="long" not-null="true" unique="true"/>]]></programlisting>
<para>
Alternativamente, estos elementos aceptan tamb&#x00ed;en un elemento hijo <literal>&lt;column&gt;</literal>.
Esto es particularmente &#x00fa;til para tipos multicolumnas:
</para>
<programlisting><![CDATA[<property name="foo" type="string">
<column name="foo" length="64" not-null="true" sql-type="text"/>
</property>]]></programlisting>
<programlisting><![CDATA[<property name="bar" type="my.customtypes.MultiColumnType"/>
<column name="fee" not-null="true" index="bar_idx"/>
<column name="fi" not-null="true" index="bar_idx"/>
<column name="fo" not-null="true" index="bar_idx"/>
</property>]]></programlisting>
<para>
El atributo <literal>sql-type</literal> permite al usuario sobrescribir el mapeo por defecto de
tipo Hibernate a tipo de datos SQL.
</para>
<para>
El atributo <literal>check</literal> te permite especificar una comprobaci&#x00f3;n de restricci&#x00f3;n.
</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>Resumen</title>
<tgroup cols="3">
<colspec colwidth="1*"/>
<colspec colwidth="1*"/>
<colspec colwidth="2.5*"/>
<thead>
<row>
<entry>Atributo</entry>
<entry>Valores</entry>
<entry>Interpretaci&#x00f3;n</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>length</literal></entry>
<entry>number</entry>
<entry>largo de columna/precisi&#x00f3;n decimal</entry>
</row>
<row>
<entry><literal>not-null</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>especifica que la columna debe ser no nulable</entry>
</row>
<row>
<entry><literal>unique</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>especifica que la columna debe tener una restricci&#x00f3;n de unicidad</entry>
</row>
<row>
<entry><literal>index</literal></entry>
<entry><literal>index_name</literal></entry>
<entry>especifica el nombre de un &#x00ed;ndice (multicolumna)</entry>
</row>
<row>
<entry><literal>unique-key</literal></entry>
<entry><literal>unique_key_name</literal></entry>
<entry>especifica el nombre de una restricci&#x00f3;n de unicidad multicolumna</entry>
</row>
<row>
<entry><literal>foreign-key</literal></entry>
<entry><literal>foreign_key_name</literal></entry>
<entry>
especifica el nombre de la restricci&#x00f3;n de clave for&#x00e1;nea generada por una
asociaci&#x00f3;n, &#x00fa;salo en los elementos de mapeo &lt;one-to-one&gt;, &lt;many-to-one&gt;,
&lt;key&gt;, y &lt;many-to-many&gt;. Nota que los lados
<literal>inverse="true"</literal> no ser&#x00e1;n considerados por
<literal>SchemaExport</literal>.
</entry>
</row>
<row>
<entry><literal>sql-type</literal></entry>
<entry><literal>column_type</literal></entry>
<entry>
sobrescribe el tipo de columna por defecto (s&#x00f3;lo atributo del elemento
<literal>&lt;column&gt;</literal>)
</entry>
</row>
<row>
<entry><literal>check</literal></entry>
<entry>expresi&#x00f3;n SQL</entry>
<entry>
crea una restricci&#x00f3;n de comprobaci&#x00f3;n SQL en columna o tabla
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
El elemento <literal>&lt;comment&gt;</literal> te permite especificar un comentario para el esquema
generado.
</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>
Esto resulta en una sentencia <literal>comment on table</literal> o <literal>comment on column</literal>
en el DDL generado (donde est&#x00e9; soportado).
</para>
</sect2>
<sect2 id="toolsetguide-s1-3">
<title>Ejecutando la herramienta</title>
<para>
La herramienta <literal>SchemaExport</literal> escribe un gui&#x00f3;n DDL a la salida est&#x00e1;ndar y/o
ejecuta las sentencias DDL.
</para>
<para>
<literal>java -cp </literal><emphasis>classpaths_de_hibernate</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>opciones ficheros_de_mapeo</emphasis>
</para>
<table frame="topbot">
<title>Opciones de L&#x00ed;nea de Comandos de <literal>SchemaExport</literal></title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Opci&#x00f3;n</entry>
<entry>Descripci&#x00f3;n</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>no enviar a salida est&#x00e1;ndar el gui&#x00f3;n</entry>
</row>
<row>
<entry><literal>--drop</literal></entry>
<entry>s&#x00f3;lo desechar las tablas</entry>
</row>
<row>
<entry><literal>--text</literal></entry>
<entry>no exportar a la base de datos</entry>
</row>
<row>
<entry><literal>--output=my_schema.ddl</literal></entry>
<entry>enviar la salida del gui&#x00f3;n ddl a un fichero</entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>lee la configuraci&#x00f3;n de Hibernate de un fichero XML</entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>lee las propiedades de base de datos de un fichero</entry>
</row>
<row>
<entry><literal>--format</literal></entry>
<entry>formatea agradablemente el SQL generado en el gui&#x00f3;n</entry>
</row>
<row>
<entry><literal>--delimiter=x</literal></entry>
<entry>establece un delimitador de fin de l&#x00ed;nea para el gui&#x00f3;n</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Puedes incluso encajar <literal>SchemaExport</literal> en tu aplicaci&#x00f3;n:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaExport(cfg).create(false, true);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-4">
<title>Propiedades</title>
<para>
Las propiedades de base de datos pueden especificarse
</para>
<itemizedlist spacing="compact">
<listitem>
<para>como propiedades de sistema con <literal>-D</literal><emphasis>&lt;property&gt;</emphasis></para>
</listitem>
<listitem>
<para>en <literal>hibernate.properties</literal></para>
</listitem>
<listitem>
<para>en un fichero de propiedades mencionado con <literal>--properties</literal></para>
</listitem>
</itemizedlist>
<para>
Las propiedades necesarias son:
</para>
<table frame="topbot">
<title>Propiedades de Conexi&#x00f3;n de SchemaExport</title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Nombre de Propiedad</entry>
<entry>Descripci&#x00f3;n</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>hibernate.connection.driver_class</literal></entry>
<entry>clase del driver jdbc</entry>
</row>
<row>
<entry><literal>hibernate.connection.url</literal></entry>
<entry>url de jdbc</entry>
</row>
<row>
<entry><literal>hibernate.connection.username</literal></entry>
<entry>usuario de base de datos</entry>
</row>
<row>
<entry><literal>hibernate.connection.password</literal></entry>
<entry>contrase&#x00f1;a de usuario</entry>
</row>
<row>
<entry><literal>hibernate.dialect</literal></entry>
<entry>dialecto</entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2 id="toolsetguide-s1-5">
<title>Usando Ant</title>
<para>
Puedes llamar a <literal>SchemaExport</literal> desde tu gui&#x00f3;n de construcci&#x00f3;n de Ant:
</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">
<title>Actualizaciones incrementales de esquema</title>
<para>
La herramienta <literal>SchemaUpdate</literal> actualizar&#x00e1; un esquema existente con cambios
"incrementales". Nota que <literal>SchemaUpdate</literal> depende fuertemente de la API de metadatos
de JDBC, de modo que no funcionar&#x00e1; con todos los drivers JDBC.
</para>
<para>
<literal>java -cp </literal><emphasis>classpaths_de_hibernate</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>opciones ficheros_de_mapeo</emphasis>
</para>
<table frame="topbot">
<title>Opciones de L&#x00ed;nea de Comandos de <literal>SchemaUpdate</literal></title>
<tgroup cols="2">
<colspec colwidth="1.5*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Opci&#x00f3;n</entry>
<entry>Descripci&#x00f3;n</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>no enviar a salida est&#x00e1;ndar el gui&#x00f3;n</entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>lee las propiedades de base de datos de un fichero</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Puedes encajar <literal>SchemaUpdate</literal> en tu aplicaci&#x00f3;n:
</para>
<programlisting><![CDATA[Configuration cfg = ....;
new SchemaUpdate(cfg).execute(false);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-7">
<title>Usando Ant para actualizaciones incrementales de esquema</title>
<para>
Puedes llamar a <literal>SchemaUpdate</literal> desde el gui&#x00f3;n de Ant:
</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>
</sect1>
</chapter>

View File

@ -0,0 +1,925 @@
<chapter id="transactions" revision="1">
<title>Transacciones y Concurrencia</title>
<para>
El punto m&#x00e1;s importante sobre Hibernate y el control de concurrencia es que muy f&#x00e1;cil
de comprender. Hibernate usa directamente conexiones JDBC y recursos JTA sin agregar
ning&#x00fa;n comportamiento de bloqueo adicional. Recomendamos altamente que gastes algo de
tiempo con la especificaci&#x00f3;n de JDBC, ANSI, y el aislamiento de transacciones de tu sistema
de gesti&#x00f3;n de base de datos. Hibernate s&#x00f3;lo a&#x00f1;ade versionado autom&#x00e1;tico pero no bloquea
objetos en memoria ni cambia el nivel de aislamiento de tus transacciones de base de datos.
B&#x00e1;sicamente, usa Hibernate como usar&#x00ed;as JDBC directo (o JTA/CMT) con tus recursos de base de
datos.
</para>
<para>
Sin embargo, adem&#x00e1;s del versionado autom&#x00e1;tico, Hibernate ofrece una API (menor) para
bloqueo pesimista de filas, usando la sint&#x00e1;xis <literal>SELECT FOR UPDATE</literal>.
Esta API se discute m&#x00e1;s adelante en este cap&#x00ed;tulo:
</para>
<para>
Comenzamos la discusi&#x00f3;n del control de concurrencia en Hibernate con la granularidad
de <literal>Configuration</literal>, <literal>SessionFactory</literal>, y
<literal>Session</literal>, as&#x00ed; como la base de datos y las transacciones de aplicaci&#x00f3;n
largas.
</para>
<sect1 id="transactions-basics">
<title>&#x00c1;mbitos de sesi&#x00f3;n y de transacci&#x00f3;n</title>
<para>
Una <literal>SessionFactory</literal> es un objeto seguro entre hebras caro-de-crear
pensado para ser compartido por todas las hebras de la aplicaci&#x00f3;n. Es creado una sola vez,
usualmente en el arranque de la aplicaci&#x00f3;n, a partir de una instancia de <literal>Configuration</literal>.
</para>
<para>
Una <literal>Session</literal> es un objeto barato, inseguro entre hebras que debe
ser usado una sola vez, para un solo proceso de negocio, una sola unidad de trabajo,
y luego descartado. Una <literal>Session</literal> no obtendr&#x00e1; una <literal>Connection</literal>
JDBC (o un <literal>Datasource</literal>) a menos que sea necesario, de modo que puedas
abrir y cerrar seguramente una <literal>Session</literal> incluso si no est&#x00e1;s seguro
que se necesitar&#x00e1; acceso a los datos para servir una petici&#x00f3;n en particular. (Esto se
vuelve importante en cuanto est&#x00e9;s implementando alguno de los siguientes patrones usando
intercepci&#x00f3;n de peticiones).
</para>
<para>
Para completar este cuadro tienes que pensar tambi&#x00e9;n en las transacciones de base de
datos. Una transacci&#x00f3;n de base de datos tiene que ser tan corta como sea posible, para
reducir la contenci&#x00f3;n de bloqueos en la base de datos. Las transacciones largas de base de
datos prevendr&#x00e1;n a tu aplicaci&#x00f3;n de escalar a una carga altamente concurrente.
</para>
<para>
&#x00bf;Qu&#x00e9; es el &#x00e1;mbito de una unidad de trabajo? &#x00bf;Puede una sola <literal>Session</literal> de Hibernate
extenderse a trav&#x00e9;s de varias transacciones de base de datos o es &#x00e9;sta una relaci&#x00f3;n uno-a-uno
de &#x00e1;mbitos? &#x00bf;Cu&#x00e1;ndo debes abrir y cerrar una <literal>Session</literal> y c&#x00f3;mo demarcas los
l&#x00ed;mites de la transacci&#x00f3;n de base de datos?
</para>
<sect2 id="transactions-basics-uow">
<title>Unidad de trabajo</title>
<para>
Primero, no uses el antipatr&#x00f3;n <emphasis>sesi&#x00f3;n-por-operaci&#x00f3;n</emphasis>, esto es,
&#x00a1;no abras y cierres una <literal>Session</literal> para cada simple llamada a la base
de datos en una sola hebra! Por supuesto, lo mismo es verdad para transacciones de base de
datos. Las llamadas a base de datos en una aplicaci&#x00f3;n se hacen usando una secuencia
prevista, que est&#x00e1;n agrupadas dentro de unidades de trabajo at&#x00f3;micas. (Nota que esto
tambi&#x00e9;n significa que el auto-commit despu&#x00e9;s de cada una de las sentencias SQL es in&#x00fa;til
en una aplicaci&#x00f3;n, este modo est&#x00e1; pensado para trabajo ad-hoc de consola SQL.
Hibernate deshabilita, o espera que el servidor de aplicaciones lo haga, el modo
auto-commit inmediatamente.)
</para>
<para>
El patr&#x00f3;n m&#x00e1;s com&#x00fa;n en una aplicaci&#x00f3;n mutiusuario cliente/servidor es
<emphasis>sesi&#x00f3;n-por-petici&#x00f3;n</emphasis>. En este modelo, una petici&#x00f3;n del cliente
es enviada al servidor (en donde se ejecuta la capa de persistencia de Hibernate),
se abre una nueva <literal>Session</literal> de Hibernate, y todas las operaciones
de base de datos se ejecutan en esta unidad de trabajo. Una vez completado el trabajo
(y se ha preparado la respuesta para el cliente) la sesi&#x00f3;n es limpiada y cerrada.
Podr&#x00ed;as usar una sola transacci&#x00f3;n de base de datos para servir a petici&#x00f3;n del cliente,
comenz&#x00e1;ndola y comprometi&#x00e9;ndola cuando abres y cierras la <literal>Session</literal>.
La relaci&#x00f3;n entre las dos es uno-a-uno y este modelo es a la medida perfecta de muchas
aplicaciones.
</para>
<para>
El desaf&#x00ed;o yace en la implementaci&#x00f3;n: no s&#x00f3;lo tienen que comenzarse y terminarse correctamente
la <literal>Session</literal> y la transacci&#x00f3;n, sino que adem&#x00e1;s tienen que estar accesibles
para las operaciones de acceso a datos. La demarcaci&#x00f3;n de una unidad de trabajo se implementa
idealmente usando un interceptor que se ejecuta cuando una petici&#x00f3;n llama al servidor y anter que
la respuesta sea enviada (es decir, un <literal>ServletFilter</literal>). Recomendamos ligar la
<literal>Session</literal> a la hebra que atiende la petici&#x00f3;n, usando una variable
<literal>ThreadLocal</literal>. Esto permite un f&#x00e1;cil acceso (como acceder a una variable static)
en t&#x00f3;do el c&#x00f3;digo que se ejecuta en esta hebra. Dependiendo del mecanismo de demarcaci&#x00f3;n de
transacciones de base de datos que elijas, podr&#x00ed;as mantener tambi&#x00e9;n el contexto de la transacci&#x00f3;n
en una variable <literal>ThreadLocal</literal>. Los patrones de implementaci&#x00f3;n para esto son
conocidos como <emphasis>Sesi&#x00f3;n Local de Hebra (ThreadLocal Session)</emphasis> y
<emphasis>Sesi&#x00f3;n Abierta en Vista (Open Session in View)</emphasis>. Puedes extender f&#x00e1;cilmente
la clase de ayuda <literal>HibernateUtil</literal> mostrada anteriormente para encontrar
una forma de implementar un interceptor e instalarlo en tu entorno. Ver el sitio web de Hibernate
para consejos y ejemplos.
</para>
</sect2>
<sect2 id="transactions-basics-apptx">
<title>Transacciones de aplicaci&#x00f3;n</title>
<para>
El patr&#x00f3;n sesi&#x00f3;n-por-petici&#x00f3;n no es el &#x00fa;nico concepto &#x00fa;til que puedes usar para dise&#x00f1;ar unidades
de trabajo. Muchos procesos de negocio requiere una serie completa de interacciones con el
usuario intercaladas con accesos a base de datos. En aplicaciones web y de empresa no es aceptable
que una transacci&#x00f3;n de base de datos se extienda a trav&#x00e9;s de la interacci&#x00f3;n de un usuario.
Considera el siguiente ejemplo:
</para>
<itemizedlist>
<listitem>
<para>
Se abre la primera pantalla de un di&#x00e1;logo, los datos vistos por el usuario han sido
cargados en una <literal>Session</literal> y transacci&#x00f3;n de base de datos particular.
El usuario es libre de modificar los objetos.
</para>
</listitem>
<listitem>
<para>
El usuario hace click en "Salvar" despu&#x00e9;s de 5 minutos y espera que sus modificaciones
sean hechas persistentes. Tambi&#x00e9;n espera que &#x00e9;l sea la &#x00fa;nica persona editando esta
informaci&#x00f3;n y que no puede ocurrir ninguna modificaci&#x00f3;n en conflicto.
</para>
</listitem>
</itemizedlist>
<para>
Llamamos a esto unidad de trabajo, desde el punto de vista del usuario, una larga
<emphasis>transacci&#x00f3;n de aplicaci&#x00f3;n</emphasis> ejecut&#x00e1;ndose. Hay muchas formas en
que puedes implementar esto en tu aplicaci&#x00f3;n.
</para>
<para>
Una primera implementaci&#x00f3;n ingenua podr&#x00ed;a mantener abierta la <literal>Session</literal>
y la transacci&#x00f3;n de base de datos durante el tiempo de pensar del usuario, con bloqueos
tomados en la base de datos para prevenir la modificaci&#x00f3;n concurrente, y para garantizar
aislamiento y atomicidad. Esto es, por supuesto, un antipatr&#x00f3;n, ya que la contenci&#x00f3;n de
bloqueo no permitir&#x00ed;a a la aplicaci&#x00f3;n escalar con el n&#x00fa;mero de usuarios concurrentes.
</para>
<para>
Claramente, tenemos que usar muchas transacciones de base de datos para implementar la transacci&#x00f3;n
de aplicaci&#x00f3;n. En este caso, mantener el aislamiento de los procesos de negocio se vuelve una
responsabilidad parcial de la capa de aplicaci&#x00f3;n. Una sola transacci&#x00f3;n de aplicaci&#x00f3;n usualmente
abarca varias transacciones de base de datos. Ser&#x00e1; at&#x00f3;mica si s&#x00f3;lo una de estas transacciones de
base de datos (la &#x00fa;ltima) almacena los datos actualizados, todas las otras simplemente leen datos
(por ejemplo, en un di&#x00e1;logo estilo-asistente abarcando muchos ciclos petici&#x00f3;n/respuesta).
Esto es m&#x00e1;s f&#x00e1;cil de implementar de lo que suena, especialmente si usas las funcionalidades de
Hibernate:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Versionado Autom&#x00e1;tico</emphasis> - Hibernate puede llevar un control autom&#x00e1;tico de
concurrencia optimista por ti, puede detectar autom&#x00e1;ticamente si una modificaci&#x00f3;n concurrente
ha ocurrido durante el tiempo de pensar del usuario.
</para>
</listitem>
<listitem>
<para>
<emphasis>Objetos Separados</emphasis> - Si decides usar el ya discutido patr&#x00f3;n
de <emphasis>sesi&#x00f3;n-por-petici&#x00f3;n</emphasis>, todas las instancias cargadas estar&#x00e1;n
en estado separado durante el tiempo de pensar del usuario. Hibernate te permite
volver a unir los objetos y hacer persistentes las modificaciones. El patr&#x00f3;n se
llama <emphasis>sesi&#x00f3;n-por-petici&#x00f3;n-con-objetos-separados</emphasis>. Se usa
versionado autom&#x00e1;tico para aislar las modificaciones concurrentes.
</para>
</listitem>
<listitem>
<para>
<emphasis>Sesi&#x00f3;n Larga</emphasis> - La <literal>Session</literal> de Hibernate puede ser
desconectada de la conexi&#x00f3;n JDBC subyacente despu&#x00e9;s que se haya sido comprometida la
transacci&#x00f3;n de base de datos, y reconectada cuando ocurra una nueva petici&#x00f3;n del cliente.
Este patr&#x00f3;n es conocido como <emphasis>sesi&#x00f3;n-por-transacci&#x00f3;n-de-aplicaci&#x00f3;n</emphasis>
y hace la re-uni&#x00f3;n innecesaria. Para aislar las modificaciones concurrentes se usa el
versionado autom&#x00e1;tico.
</para>
</listitem>
</itemizedlist>
<para>
Tanto <emphasis>sesi&#x00f3;n-por-petici&#x00f3;n-con-objetos-separados</emphasis> como
<emphasis>sesi&#x00f3;n-por-transacci&#x00f3;n-de-aplicaci&#x00f3;n</emphasis>, ambas tienen
ventajas y desventajas, las discutimos m&#x00e1;s adelante en este cap&#x00ed;tulo en el contexto
del control optimista de concurrencia.
</para>
</sect2>
<sect2 id="transactions-basics-identity">
<title>Considerando la identidad del objeto</title>
<para>
Una aplicaci&#x00f3;n puede acceder concurrentemente a el mismo estado persistente en dos
<literal>Session</literal>s diferentes. Sin embargo, una instancia de una clase
persistente nunca se comparte entre dos instancias de <literal>Session</literal>.
Por lo tanto existen dos nociones diferentes de identidad:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>Identidad de Base de Datos</term>
<listitem>
<para>
<literal>foo.getId().equals( bar.getId() )</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Identidad JVM</term>
<listitem>
<para>
<literal>foo==bar</literal>
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Entonces para objetos unidos a una <literal>Session</literal> <emphasis>en particular</emphasis>
(es decir en el &#x00e1;mbito de una <literal>Session</literal>) las dos nociones son equivalentes, y
la identidad JVM para la identidad de base de datos est&#x00e1; garantizada por Hibernate. Sin embargo,
mientras la aplicaci&#x00f3;n acceda concurrentemente al "mismo" (identidad persistente) objeto de negocio
en dos sesiones diferentes, las dos instancias ser&#x00e1;n realmente "diferentes" (identidad JVM).
Los conflictos se resuelven (con versionado autom&#x00e1;tico) en tiempo de limpieza (flush) usando un
enfoque optimista.
</para>
<para>
Este enfoque deja que Hibernate y la base de datos se preocupen sobre la concurrencia. Adem&#x00e1;s
provee la mejor escalabilidad, ya que garantizando la identidad un unidades de trabajo monohebra
no se necesitan bloqueos caros u otros medios de sincronizaci&#x00f3;n. La aplicaci&#x00f3;n nunca necesita
sincronizar sobre ning&#x00fa;n objeto de negocio, siempre que se apegue a una sola hebra por
<literal>Session</literal>. Dentro de una <literal>Session</literal> la aplicaci&#x00f3;n puede usar
con seguridad <literal>==</literal> para comparar objetos.
</para>
<para>
Sin embargo, una aplicaci&#x00f3;n que usa <literal>==</literal> fuera de una <literal>Session</literal>,
podr&#x00ed;a ver resultados inesperados. Esto podr&#x00ed;a ocurrir incluso en sitios algo inesperados,
por ejemplo, si pones dos instancias separadas dentro del mismo <literal>Set</literal>.
Ambas podr&#x00ed;an tener la misma identidad de base de datos (es decir, representar la misma fila),
pero la identidad JVM, por definici&#x00f3;n, no est&#x00e1; garantizada para las instancias en estado separado.
El desarrollador tiene que sobrescribir los m&#x00e9;todos <literal>equals()</literal> y
<literal>hashCode()</literal> en las clases persistentes e implementar su propia noci&#x00f3;n de igualdad
de objetos. Hay una advertencia: Nunca uses el identificador de base de datos para implementar
la igualdad, usa una clave de negocio, una combinaci&#x00f3;n de atributos &#x00fa;nicos, usualmente inmutables.
El identificador de base de datos cambiar&#x00e1; si un objeto transitorio es hecho persistente.
Si la instancia transitoria (usualmente junta a instancias separadas) es mantenida en un
<literal>Set</literal>, cambiar el c&#x00f3;digo hash rompe el contrato del <literal>Set</literal>.
Los atributos para las claves de negocio no tienen que ser tan estables como las claves primarias
de base de datos, s&#x00f3;lo tienes que garantizar estabilidad en tanto los objetos est&#x00e9;n en el mismo
<literal>Set</literal>. Mira el sitio web de Hibernate para una discusi&#x00f3;n m&#x00e1;s cuidadosa de este
tema. Nota tambi&#x00e9;n que &#x00e9;ste no es un tema de Hibernate, sino simplemente c&#x00f3;mo la identidad y la igualdad
de los objetos Java tiene que ser implementada.
</para>
</sect2>
<sect2 id="transactions-basics-issues">
<title>Temas comunes</title>
<para>
Nunca uses los antipatrones <emphasis>sesi&#x00f3;n-por-sesi&#x00f3;n-de-usuario</emphasis> o
<emphasis>sesi&#x00f3;n-por-aplicaci&#x00f3;n</emphasis> (por supuesto, hay raras excepciones a esta
regla). Nota que algunis de los siguientes temas podr&#x00ed;an tambi&#x00e9;n aparecer con los patrones
recomendados. Aseg&#x00fa;rate que entiendes las implicaciones antes de tomar una decisi&#x00f3;n de
dise&#x00f1;o:
</para>
<itemizedlist>
<listitem>
<para>
Una <literal>Session</literal> no es segura entre hebras. Las cosas que se suponen
que funcionan concurrentemente, como peticiones HTTP, beans de sesi&#x00f3;n, o workers de
Swing, provocar&#x00e1;n condiciones de competencia si una instancia de <literal>Session</literal>
fuese compartida. Si guardas tu <literal>Session</literal> de Hibernate en tu
<literal>HttpSession</literal> (discutido m&#x00e1;s adelante), debes considerar sincronizar
el acceso a tu sesi&#x00f3;n HTTP. De otro modo, un usuario que hace click lo suficientemente
r&#x00e1;pido puede llegar a usar la misma <literal>Session</literal> en dos hebras ejecut&#x00e1;ndose
concurrentemente.
</para>
</listitem>
<listitem>
<para>
Una excepci&#x00f3;n lanzada por Hibernate significa que tienes que deshacer (rollback) tu
transacci&#x00f3;n de base de datos y cerrar la <literal>Session</literal> inmediatamente
(discutido en m&#x00e1;s detalle luego). Si tu <literal>Session</literal> est&#x00e1; ligada a la
aplicaci&#x00f3;n, tienes que parar la aplicaci&#x00f3;n. Deshacer (rollback) la transacci&#x00f3;n de base
de datos no pone a tus objetos de vuelta al estado en que estaban al comienzo de la
transacci&#x00f3;n. Esto significa que el estado de la base de datos y los objetos de negocio
quedan fuera de sincron&#x00ed;a. Usualmente esto no es un problema, pues las excepciones no
son recuperables y tienes que volver a comenzar despu&#x00e9;s del rollback de todos modos.
</para>
</listitem>
<listitem>
<para>
La <literal>Session</literal> pone en cach&#x00e9; todo objeto que est&#x00e9; en estado persistente
(vigilado y chequeado por estado sucio por Hibernate). Esto significa que crece sin
fin hasta que obtienes una OutOfMemoryException, si la mantienes abierta por un largo
tiempo o simplemente cargas demasiados datos. Una soluci&#x00f3;n para esto es llamar a
<literal>clear()</literal> y <literal>evict()</literal> para gestionar el cach&#x00e9; de la
<literal>Session</literal>, pero probalemente debas considerar un procedimiento almacenado
si necesitas operaciones de datos masivas. Se muestran algunas soluciones en
<xref linkend="batch"/>. Mantener una <literal>Session</literal> abierta por la duraci&#x00f3;n
de una sesi&#x00f3;n de usuario significa tambi&#x00e9;n una alta probabilidad de datos a&#x00f1;ejos.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-demarcation">
<title>Demarcaci&#x00f3;n de la transacci&#x00f3;n de base de datos</title>
<para>
Los l&#x00ed;mites de las transacciones de base de datos (o sistema) son siempre necesarios. Ninguna comunicaci&#x00f3;n
con la base de datos puede darse fuera de una transacci&#x00f3;n de base de datos (esto parece confundir muchos
desarrolladores acostumbrados al modo auto-commit). Siempre usa l&#x00ed;mites de transacci&#x00f3;n claros, incluso
para las operaciones de s&#x00f3;lo lectura. Dependiendo del nivel de aislamiento y las capacidades de base de
datos, esto podr&#x00ed;a o no ser requerido, pero no hay un merma si siempre demarcas expl&#x00ed;citamente
las transacciones.
</para>
<para>
Una aplicaci&#x00f3;n Hibernate puede ejecutarse en entornos no manejados (es decir, como independiente,
Web simple, o aplicaciones Swing) y entornos manejados J2EE. En un entorno no manejado, Hibernate es
usualmente responsable de su propio pool de conexiones de base de datos. El desarrollador de aplicaciones
tiene que establecer manualmente los l&#x00ed;mites de transacci&#x00f3;n, en otras palabras, hacer begin, commit, o
rollback las transacciones de base de datos por s&#x00ed; mismo. Un entorno manejado usualmente provee transacciones
gestionadas por contenedor, con el ensamble de transacci&#x00f3;n definido declarativamente en descriptores de
despliegue de beans de sesi&#x00f3;n EJB, por ejemplo. La demarcaci&#x00f3;n program&#x00e1;tica de transacciones no es m&#x00e1;s
necesario, incluso limpiar (flush) la <literal>Session</literal> es hecho autom&#x00e1;ticamente.
</para>
<para>
Sin embargo, frecuentemente es deseable mantener portable tu capa de persistencia. Hibernate ofrece
una API de envoltura llamada <literal>Transaction</literal> que se traduce al sistema de transacciones
nativo de tu entorno de despliegue. Esta API es realmente opcional, pero recomendamos fuertemente su uso
salvo que est&#x00e9;s en un bean de sesi&#x00f3;n CMT.
</para>
<para>
Usualmente, finalizar una <literal>Session</literal> implica cuatro fases distintas:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
limpiar (flush) la sesi&#x00f3;n
</para>
</listitem>
<listitem>
<para>
comprometer la transacci&#x00f3;n
</para>
</listitem>
<listitem>
<para>
cerrar la sesi&#x00f3;n
</para>
</listitem>
<listitem>
<para>
manejar excepciones
</para>
</listitem>
</itemizedlist>
<para>
Limpiar la sesi&#x00f3;n ha sido discutido anteriormente, tendremos ahora una mirada m&#x00e1;s de cerca
a la demarcaci&#x00f3;n de transacciones y manejo de excepciones en sendos entornos manejado y no manejados.
</para>
<sect2 id="transactions-demarcation-nonmanaged">
<title>Entorno no manejado</title>
<para>
Si una capa de persistencia Hibernate se ejecuta en un entorno no manejado, las conexiones
de base de datos son manejadas usualmente por el mecanismo de pooling de Hibernate. El idioma
manejo de sesi&#x00f3;n/transacci&#x00f3;n se ve as&#x00ed;:
</para>
<programlisting><![CDATA[//Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}]]></programlisting>
<para>
No tienes que limpiar con <literal>flush()</literal> la <literal>Session</literal> expl&#x00ed;citamente -
la llamada a <literal>commit()</literal> autom&#x00e1;ticamente dispara la sincronizaci&#x00f3;n.
</para>
<para>
Una llamada a <literal>close()</literal> marca el fin de una sesi&#x00f3;n. La principal implicaci&#x00f3;n
de <literal>close()</literal> es que la conexi&#x00f3;n JDBC ser&#x00e1; abandonada por la sesi&#x00f3;n.
</para>
<para>
Este c&#x00f3;digo Java es portable y se ejecuta tanto en entornos no manejados como en entornos JTA.
</para>
<para>
Muy probablemente nunca veas este idioma en c&#x00f3;digo de negocio en una aplicaci&#x00f3;n normal;
las excepciones fatales (sistema) deben siempre ser capturadas en la "cima". En otras palabras,
el c&#x00f3;digo que ejecuta las llamadas de Hibernate (en la capa de persistencia) y el c&#x00f3;digo que
maneja <literal>RuntimeException</literal> (y usualmente s&#x00f3;lo puede limpiar y salir) est&#x00e1;n en
capas diferentes. Esto puede ser un desaf&#x00ed;o de dise&#x00f1;arlo t&#x00fa; mismo y debes usar los servicios
de contenedor J2EE/EJB en cuanto estuviesen disponibles. El manejo de excepciones se dicute
m&#x00e1;s adelante en este cap&#x00ed;tulo.
</para>
<para>
Nota que debes seleccionar <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
(que es el por defecto).
</para>
</sect2>
<sect2 id="transactions-demarcation-jta">
<title>Usando JTA</title>
<para>
Si tu capa de persistencia se ejecuta en un servidor de aplicaciones (por ejemplo, detr&#x00e1;s
de beans de sesi&#x00f3;n EJB), cada conexi&#x00f3;n de datasource obtenida por Hibernate ser&#x00e1; parte
autom&#x00e1;ticamente de la transacci&#x00f3;n JTA global. Hibernate ofrece dos estrategias para esta
integraci&#x00f3;n.
</para>
<para>
Si usas transacciones gestionadas-por-bean (BMT) Hibernate le dir&#x00e1; al servidor de aplicaciones
que comience y finalice una transacci&#x00f3;n BMT si usas la API de <literal>Transaction</literal>.
De modo que, el c&#x00f3;digo de gesti&#x00f3;n de la transacci&#x00f3;n es id&#x00e9;ntico al de un entorno no manejado.
</para>
<programlisting><![CDATA[// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}]]></programlisting>
<para>
Con CMT, la demarcaci&#x00f3;n de la transacci&#x00f3;n se hace en descriptores de despliegue de beans de sesi&#x00f3;n,
no program&#x00e1;ticamente. Si no quieres limpiar (flush) y cerrar manualmente la <literal>Session</literal>
por ti mismo, solamente establece <literal>hibernate.transaction.flush_before_completion</literal> a
<literal>true</literal>, <literal>hibernate.connection.release_mode</literal> a
<literal>after_statement</literal> o <literal>auto</literal> y
<literal>hibernate.transaction.auto_close_session</literal> a <literal>true</literal>. Hibernate
limpiar&#x00e1; y cerrar&#x00e1; entonces autom&#x00e1;ticamente la <literal>Session</literal> para ti. Lo &#x00fa;nico que resta
es deshacer (rollback) la transacci&#x00f3;n cuando ocurra una excepci&#x00f3;n. Afortunadamente, en un bean CMT,
incluso esto ocurre autom&#x00e1;ticamente, ya que una <literal>RuntimeException</literal> no manejada
disparada por un m&#x00e9;todo de un bean de sesi&#x00f3;n le dice al contenedor que ponga a deshacer la transacci&#x00f3;n
global. <emphasis>Esto significa que, en CMT, no necesitas usar en absoluto la API de
<literal>Transaction</literal> de Hibernate.</emphasis>
</para>
<para>
Nota que debes elegir <literal>org.hibernate.transaction.JTATransactionFactory</literal> en un
bean de sesi&#x00f3;n BMT, y <literal>org.hibernate.transaction.CMTTransactionFactory</literal> en un
bean de sesi&#x00f3;n CMT, cuando configures la f&#x00e1;brica de transacciones de Hibernate. Recuerda adem&#x00e1;s
establecer <literal>org.hibernate.transaction.manager_lookup_class</literal>.
</para>
<para>
Si trabajas en un entorno CMT, y usas limpieza (flushing) y cierre autom&#x00e1;ticos de la sesi&#x00f3;n,
podr&#x00ed;as querer tambi&#x00e9;n usar la misma sesi&#x00f3;n en diferentes partes de tu c&#x00f3;digo. T&#x00ed;picamente,
en un entorno no manejado, usar&#x00ed;as una variable <literal>ThreadLocal</literal> para tener la sesi&#x00f3;n,
pero una sola petici&#x00f3;n de EJB puede ejecutarse en diferentes hebras (por ejemplo, un bean de sesi&#x00f3;n
llamando a otro bean de sesi&#x00f3;n). Si no quieres molestarte en pasar tu <literal>Session</literal>
por alrededor, la <literal>SessionFactory</literal> provee el m&#x00e9;todo
<literal>getCurrentSession()</literal>, que devuelve una sesi&#x00f3;n que est&#x00e1; pegada al contexto de
transacci&#x00f3;n JTA. &#x00a1;Esta es la forma m&#x00e1;s f&#x00e1;cil de integrar Hibernate en una aplicaci&#x00f3;n!
La sesi&#x00f3;n "actual" siempre tiene habilitados limpieza, cierre y liberaci&#x00f3;n de conexi&#x00f3;n autom&#x00e1;ticos
(sin importar la configuraci&#x00f3;n de las propiedades anteriores). Nuestra idioma de gesti&#x00f3;n de
sesi&#x00f3;n/transacci&#x00f3;n se reduce a:
</para>
<programlisting><![CDATA[// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
]]></programlisting>
<para>
En otras palabras, todo lo que tienes que hacer en un entorno manejado, es llamar a
<literal>SessionFactory.getCurrentSession()</literal>, hacer tu trabajo de acceso a datos,
y dejar el resto al contenedor. Los l&#x00ed;mites de transacci&#x00f3;n se establecen declarativamente
en los descriptores de despliegue de tu bean de sesi&#x00f3;n. El ciclo de vida de la sesi&#x00f3;n es
manejado completamente por Hibernate.
</para>
<para>
Existe una advertencia al uso del modo de liberaci&#x00f3;n de conexi&#x00f3;n <literal>after_statement</literal>.
Debido a una limitaci&#x00f3;n tonta de la especificaci&#x00f3;n de JTA, no es posible para Hibernate
limpiar autom&#x00e1;ticamente ning&#x00fa;n <literal>ScrollableResults</literal> no cerrado ni
instancias de <literal>Iterator</literal> devueltas por <literal>scroll()</literal> o
<literal>iterate()</literal>. <emphasis>Debes</emphasis> liberar el cursor de base de datos
subyacente llamando a <literal>ScrollableResults.close()</literal> o
<literal>Hibernate.close(Iterator)</literal> expl&#x00ed;citamente desde un bloque <literal>finally</literal>.
(Por supuesto, la mayor&#x00ed;a de las aplicaciones pueden evitarlo f&#x00e1;cilmente no usando en absoluto ning&#x00fa;n
<literal>scroll()</literal> o <literal>iterate()</literal> desde el c&#x00f3;digo CMT.)
</para>
</sect2>
<sect2 id="transactions-demarcation-exceptions">
<title>Manejo de excepciones</title>
<para>
Si la <literal>Session</literal> lanza una excepci&#x00f3;n (incluyendo cualquier
<literal>SQLException</literal>), debes inmediatamente deshacer (rollback) la
transacci&#x00f3;n de base de datos, llamar a <literal>Session.close()</literal> y
descartar la instancia de <literal>Session</literal>. Ciertos m&#x00e9;todos de
<literal>Session</literal> <emphasis>no</emphasis> dejar&#x00e1;n la sesi&#x00f3;n en un
estado consistente. Ninguna excepci&#x00f3;n lanzada por Hibernate puede ser tratada
como recuperable. Aseg&#x00fa;rate que la <literal>Session</literal> sea cerrada llamando
a <literal>close()</literal> en un bloque <literal>finally</literal>.
</para>
<para>
La <literal>HibernateException</literal>, que envuelve la mayor&#x00ed;a de los errores que
pueden ocurrir en la capa de persistencia de Hibernate, en una excepci&#x00f3;n no chequeada
(no lo era en versiones anteriores de Hibernate). En nuestra opini&#x00f3;n, no debemos forzar
al desarrollador de aplicaciones a capturar una excepci&#x00f3;n irrecuperable en una capa baja.
En la mayor&#x00ed;a de los sistemas, las excepciones no chequeadas y fatales son manejadas
en uno de los primeros cuadros de la pila de llamadas a m&#x00e9;todos (es decir, en las capas
m&#x00e1;s altas) y se presenta un mensaje de error al usuario de la aplicaci&#x00f3;n (o se toma alguna
otra acci&#x00f3;n apropiada). Nota que Hibernate podr&#x00ed;a tambi&#x00e9;n lanzar otras excepciones no chequeadas
que no sean una <literal>HibernateException</literal>. Una vez m&#x00e1;s, no son recuperables y debe
tomarse una acci&#x00f3;n apropiada.
</para>
<para>
Hibernate envuelve <literal>SQLException</literal>s lanzadas mientras se interact&#x00fa;a con la base
de datos en una <literal>JDBCException</literal>. De hecho, Hibernate intentar&#x00e1; convertir la excepci&#x00f3;n
en una subclase de <literal>JDBCException</literal> m&#x00e1;s significativa. La <literal>SQLException</literal>
est&#x00e1; siempre disponible v&#x00ed;a <literal>JDBCException.getCause()</literal>. Hibernate convierte la
<literal>SQLException</literal> en una subclase de <literal>JDBCException</literal> apropiada usando
el <literal>SQLExceptionConverter</literal> adjunto a la <literal>SessionFactory</literal>. Por defecto,
el <literal>SQLExceptionConverter</literal> est&#x00e1; definido para el dialecto configurado; sin embargo,
es tambi&#x00e9;n posible enchufar una implementaci&#x00f3;n personalizada (ver los javadocs de la clase
<literal>SQLExceptionConverterFactory</literal> para los detalles). Los subtipos est&#x00e1;ndar de
<literal>JDBCException</literal> son:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>JDBCConnectionException</literal> - indica un error con la comunicaci&#x00f3;n JDBC subyacente.
</para>
</listitem>
<listitem>
<para>
<literal>SQLGrammarException</literal> - indica un problema de gram&#x00e1;tica o sint&#x00e1;xis con el
SQL publicado.
</para>
</listitem>
<listitem>
<para>
<literal>ConstraintViolationException</literal> - indica alguna forma de violaci&#x00f3;n de restricci&#x00f3;n
de integridad.
</para>
</listitem>
<listitem>
<para>
<literal>LockAcquisitionException</literal> - indica un error adquiriendo un nivel de bloqueo
necesario para realizar una operaci&#x00f3;n solicitada.
</para>
</listitem>
<listitem>
<para>
<literal>GenericJDBCException</literal> - una excepci&#x00f3;n gen&#x00e9;rica que no cay&#x00f3; en ninguna de las
otras categor&#x00ed;as.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-optimistic">
<title>Control optimista de concurrencia</title>
<para>
El &#x00fa;nico enfoque que es consistente con alta concurrencia y alta escalabilidad es el control
optimista de concurrencia con versionamiento. El chuequeo de versi&#x00f3;n usa n&#x00fa;meros de versi&#x00f3;n,
o timestamps, para detectar actualizaciones en conflicto (y para prevenir actualizaciones perdidas).
Hibernate provee para tres enfoques posibles de escribir c&#x00f3;digo de aplicaci&#x00f3;n que use concurrencia
optimista. Los casos de uso que hemos mostrado est&#x00e1;n en el contexto de transacciones de aplicaci&#x00f3;n
largas pero el chequeo de versiones tiene adem&#x00e1;s el beneficio de prevenir actualizaciones perdidas
en transacciones de base de datos solas.
</para>
<sect2 id="transactions-optimistic-manual">
<title>Chequeo de versiones de aplicaci&#x00f3;n</title>
<para>
En una implementaci&#x00f3;n sin mucha ayuda de Hibernate, cada interacci&#x00f3;n con la base de datos ocurre en una
nueva <literal>Session</literal> y el desarrollador es responsable de recargar todas las intancias
persistentes desde la base de datos antes de manipularlas. Este enfoque fuerza a la aplicaci&#x00f3;n a
realizar su propio chequeo de versiones para asegurar el aislamiento de transacciones de base de datos.
Es el enfoque m&#x00e1;s similar a los EJBs de entidad.
</para>
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
session = factory.openSession();
Transaction t = session.beginTransaction();
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
session.close();]]></programlisting>
<para>
La propiedad <literal>version</literal> se mapea usando <literal>&lt;version&gt;</literal>,
e Hibernate la incrementar&#x00e1; autom&#x00e1;ticamente durante la limpieza si la entidad est&#x00e1; sucia.
</para>
<para>
Por supuesto, si est&#x00e1;s operando un entorno de baja-concurrencia-de-datos y no requieres
chequeo de versiones, puedes usar este enfoque y simplemente saltar el chequeo de versiones.
En ese caso, <emphasis>el &#x00fa;ltimo compromiso (commit) gana</emphasis> ser&#x00e1; la estrategia por
defecto para tus transacciones de aplicaci&#x00f3;n largas. Ten en mente que esto podr&#x00ed;a confundir
a los usuarios de la aplicaci&#x00f3;n, pues podr&#x00ed;an experimentar actualizaciones perdidas sin
mensajes de error ni chance de fusionar los cambios conflictivos.
</para>
<para>
Claramente, el chequeo manual de versiones es factible solamente en circunstancias muy triviales,
y no es pr&#x00e1;ctico para la mayor&#x00ed;a de aplicaciones. Frecuentemente, no s&#x00f3;lo intancias solas, sino grafos
completos de objetos modificados tienen que ser chequeados. Hibernate ofrece chequeo de versiones
autom&#x00e1;tico con el paradigma de dise&#x00f1;o de <literal>Session</literal> larga o de instancias separadas.
</para>
</sect2>
<sect2 id="transactions-optimistic-longsession">
<title>Sesi&#x00f3;n larga y versionado autom&#x00e1;tico</title>
<para>
Una sola instancia de <literal>Session</literal> y sus instancias persistentes
son usadas para toda la transacci&#x00f3;n de aplicaci&#x00f3;n. Hibernate chequea las versiones
de instancia en el momento de limpieza (flush), lanzando una excepci&#x00f3;n si se detecta
una modificaci&#x00f3;n concurrente. Concierne al desarrollador capturar y manejar esta excepci&#x00f3;n
(las opciones comunes son la oportunidad del usuario de fusionar los cambios, o recomenzar el
proceso de negocio sin datos a&#x00f1;ejos).
</para>
<para>
La <literal>Session</literal> se desconecta de cualquier conexi&#x00f3;n JDBC subyacente
al esperar por una interacci&#x00f3;n del usuario. Este enfoque es el m&#x00e1;s eficiente en t&#x00e9;rminos
de acceso a base de datos. La aplicaci&#x00f3;n no necesita tratar por s&#x00ed; misma con el chequeo de
versiones, ni re-uniendo instancias separadas, ni tiene que recargar instancias en cada
transacci&#x00f3;n de base de datos.
</para>
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
session.reconnect(); // Obtain a new JDBC connection
Transaction t = session.beginTransaction();
foo.setProperty("bar");
t.commit(); // End database transaction, flushing the change and checking the version
session.disconnect(); // Return JDBC connection ]]></programlisting>
<para>
El objeto <literal>foo</literal> todav&#x00ed;a conoce en qu&#x00e9; <literal>Session</literal> fue cargado.
<literal>Session.reconnect()</literal> obtiene una nueva conexi&#x00f3;n (o puedes proveer una) y
reasume la sesi&#x00f3;n. El m&#x00e9;todo <literal>Session.disconnect()</literal> desconectar&#x00e1; la sesi&#x00f3;n
de la conexi&#x00f3;n JDBC y la devolver&#x00e1; la conexi&#x00f3;n al pool (a menos que hayas provisto la conexi&#x00f3;n).
Despu&#x00e9;s de la reconexi&#x00f3;n, para forzar un chequeo de versi&#x00f3;n en datos que no est&#x00e9;s actualizando,
puedes llamar a <literal>Session.lock()</literal> con <literal>LockMode.READ</literal> sobre
cualquier objeto que pudiese haber sido actualizado por otra transacci&#x00f3;n. No necesitas bloquear
ning&#x00fa;n dato que <emphasis>s&#x00ed; est&#x00e9;s</emphasis> actualizando.
</para>
<para>
Si las llamadas expl&#x00ed;citas a <literal>disconnect()</literal> y <literal>reconnect()</literal>
son muy onerosas, puedes usar en cambio <literal>hibernate.connection.release_mode</literal>.
</para>
<para>
Este patr&#x00f3;n es problem&#x00e1;tico si la <literal>Session</literal> es demasiado grande para ser almacenada
durante el tiempo de pensar del usuario, por ejemplo, una <literal>HttpSession</literal> debe
mantenerse tan peque&#x00f1;a como sea posible. Ya que la <literal>Session</literal> es tambi&#x00e9;n el cach&#x00e9;
(obligatorio) de primer nivel y contiene todos los objetos cargados, podemos probablemente cargar
esta estrategia s&#x00f3;lo para unos pocos ciclos petici&#x00f3;n/respuesta. Esto est&#x00e1; de hecho recomendado, ya que
la <literal>Session</literal> tendr&#x00e1; pronto tambi&#x00e9;n datos a&#x00f1;ejos.
</para>
<para>
Nota tambi&#x00e9;n que debes mantener la <literal>Session</literal> desconectada pr&#x00f3;xima a la capa
de persistencia. En otras palabras, usa una sesi&#x00f3;n de EJB con estado para tener la
<literal>Session</literal> y no transferirla a la capa web para almacenarla en la
<literal>HttpSession</literal> (ni incluso serializarla a una capa separada).
</para>
</sect2>
<sect2 id="transactions-optimistic-detached">
<title>Objetos separados y versionado autom&#x00e1;tico</title>
<para>
Cada interacci&#x00f3;n con el almac&#x00e9;n persistente ocurre en una nueva <literal>Session</literal>.
Sin embargo, las mismas instancias persistentes son reusadas para cada interacci&#x00f3;n con la base de
datos. La aplicaci&#x00f3;n manipula el estado de las instancias separadas originalmente cargadas en otra
<literal>Session</literal> y luego las readjunta usando <literal>Session.update()</literal>,
<literal>Session.saveOrUpdate()</literal>, o <literal>Session.merge()</literal>.
</para>
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
foo.setProperty("bar");
session = factory.openSession();
Transaction t = session.beginTransaction();
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
t.commit();
session.close();]]></programlisting>
<para>
De nuevo, Hibernate chequear&#x00e1; las versiones de instancia durante la limpieza (flush),
lanzando una excepci&#x00f3;n si ocurrieron actualizaciones en conflicto.
</para>
<para>
Puedes tambi&#x00e9;n llamar a <literal>lock()</literal> en vez de <literal>update()</literal>
y usar <literal>LockMode.READ</literal> (realizando un chequeo de versi&#x00f3;n, puenteando
todos los cach&#x00e9;s) si est&#x00e1;s seguro que el objeto no ha sido modificado.
</para>
</sect2>
<sect2 id="transactions-optimistic-customizing">
<title>Personalizando el versionado autom&#x00e1;tico</title>
<para>
Puedes deshabilitar el incremento de versi&#x00f3;n autom&#x00e1;tico de Hibernate para propiedades en particular
y colecciones estableciendo el atributo de mapeo <literal>optimistic-lock</literal> a
<literal>false</literal>. Hibernate entonces no incrementar&#x00e1; ya m&#x00e1;s las versiones si la propiedad est&#x00e1;
sucia.
</para>
<para>
Los esquemas de base de datos heredados son frecuentemente est&#x00e1;ticos y no pueden ser modificados.
U otras aplicaciones podr&#x00ed;an tambi&#x00e9;n acceder la misma base de datos y no saber c&#x00f3;mo manejar los n&#x00fa;meros
de versi&#x00f3;n ni incluso timestamps. En ambos casos, el versionado no puede confiarse a una columna en
particular en una tabla. Para forzar un chequeo de versiones sin un mapeo de propiedad de versi&#x00f3;n o
timestamp, con una comparaci&#x00f3;n del estado de todos los campos en una fila, activa
<literal>optimistic-lock="all"</literal> en el mapeo de <literal>&lt;class&gt;</literal>.
Nota que esto conceptualmente funciona solamente si Hibernate puede comparar el estado viejo y nuevo,
es decir, si usas una sola <literal>Session</literal> larga y no
sesi&#x00f3;n-por-petici&#x00f3;n-con-instancias-separadas.
</para>
<para>
A veces las modificaciones concurrentes pueden permitirse, en cuanto los cambios que hayan sido
hechos no se traslapen. Si estableces <literal>optimistic-lock="dirty"</literal> al mapear la
<literal>&lt;class&gt;</literal>, Hibernate s&#x00f3;lo comparar&#x00e1; los campos sucios durante la limpieza.
</para>
<para>
En ambos casos, con columnas de versi&#x00f3;n/timestamp dedicadas o con comparaci&#x00f3;n de campos
completa/sucios, Hibernate usa una sola sentencia <literal>UPDATE</literal>
(con una cl&#x00e1;usula <literal>WHERE</literal> apropiada) por entidad para ejecutar el chequeo
de versiones y actualizar la informaci&#x00f3;n. Si usas persistencia transitiva para la re-uni&#x00f3;n
en cascada de entidades asociadas, Hibernate podr&#x00ed;a ejecutar actualizaciones innecesarias.
Esto usualmente no es un problema, pero podr&#x00ed;an ejecutarse disparadores (triggers)
<emphasis>on update</emphasis> en la base de datos incluso cuando no se haya hecho ning&#x00fa;n cambio
a las instancias separadas. Puedes personalizar este comportamiento estableciendo
<literal>select-before-update="true"</literal> en el mapeo de <literal>&lt;class&gt;</literal>,
forzando a Hibernate a <literal>SELECT</literal> la instancia para asegurar que las actualizaciones
realmente ocurran, antes de actualizar la fila.
</para>
</sect2>
</sect1>
<sect1 id="transactions-locking">
<title>Bloqueo pesimista</title>
<para>
No se pretende que los usuarios gasten mucho tiempo preocup&#x00e1;ndose de las estrategias de bloqueo.
Usualmente es suficiente con especificar un nivel de aislamiento para las conexiones JDBC y entonces
simplemente dejar que la base de datos haga todo el trabajo. Sin embargo, los usuarios avanzados pueden
a veces obtener bloqueos exclusivos pesimistas, o reobtener bloqueos al comienzo de una nueva
transacci&#x00f3;n.
</para>
<para>
&#x00a1;Hibernate siempre usar&#x00e1; el mecanismo de bloqueo de la base de datos, nunca bloqueo
de objetos en memoria!
</para>
<para>
La clase <literal>LockMode</literal> define los diferentes niveles de bloqueo que pueden ser adquiridos
por Hibernate. Un bloqueo se obtiene por los siguientes mecanismos:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>LockMode.WRITE</literal> se adquiere autom&#x00e1;ticamente cuando Hibernate actualiza o
inserta una fila.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE</literal> puede ser adquirido bajo petici&#x00f3;n expl&#x00ed;cita del usuario
usando <literal>SELECT ... FOR UPDATE</literal> en base de datos que soporten esa sint&#x00e1;xis.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE_NOWAIT</literal> puede ser adquirido bajo petici&#x00f3;n expl&#x00ed;cita del usuario
usando un <literal>SELECT ... FOR UPDATE NOWAIT</literal> bajo Oracle.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.READ</literal> es adquirido autom&#x00e1;ticamente cuando Hibernate lee datos
bajo los niveles de aislamiento Repeatable Read o Serializable. Puede ser readquirido por
pedido expl&#x00ed;cito del usuario.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.NONE</literal> representa la ausencia de un bloqueo. Todos los objetos se pasan
a este modo de bloqueo al final de una <literal>Transaction</literal>. Los objetos asociados con una
sesi&#x00f3;n v&#x00ed;a una llamada a <literal>update()</literal> o <literal>saveOrUpdate()</literal> tambi&#x00e9;n
comienzan en este modo de bloqueo.
</para>
</listitem>
</itemizedlist>
<para>
La "petici&#x00f3;n expl&#x00ed;cita del usuario" se expresa en una de las siguientes formas:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Una llamada a <literal>Session.load()</literal>, especificando un <literal>LockMode</literal>.
</para>
</listitem>
<listitem>
<para>
Una llamada a <literal>Session.lock()</literal>.
</para>
</listitem>
<listitem>
<para>
Una llamada a <literal>Query.setLockMode()</literal>.
</para>
</listitem>
</itemizedlist>
<para>
Si se llama a <literal>Session.load()</literal> con <literal>UPGRADE</literal> o
<literal>UPGRADE_NOWAIT</literal>, y el objeto pedido no ha sido a&#x00fa;n cargado por la sesi&#x00f3;n, el objeto es
cargado usando <literal>SELECT ... FOR UPDATE</literal>. Si se llama a <literal>load()</literal> para
un objeto que ya est&#x00e9; cargado con un bloqueo menos restrictivo que el pedido, Hibernate llama a
<literal>lock()</literal> para ese objeto.
</para>
<para>
<literal>Session.lock()</literal> realiza un chequeo de n&#x00fa;mero de versi&#x00f3;n si el modo de bloqueo especificado
es <literal>READ</literal>, <literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>. (En el caso de
<literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>, se usa
<literal>SELECT ... FOR UPDATE</literal>.)
</para>
<para>
Si la base de datos no soporta el modo de bloqueo solicitado, Hibernate usar&#x00e1; un modo alternativo
apropiado (en vez de lanzar una excepci&#x00f3;n). Esto asegura que las aplicaciones ser&#x00e1;n portables.
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,282 @@
<chapter id="xml">
<title>Mapeo XML</title>
<para><emphasis>
Nota que esta es una funcionalidad experimental en Hibernate 3.0 y está
bajo un desarrollo extremadamente activo.
</emphasis></para>
<sect1 id="xml-intro" revision="1">
<title>Trabajando con datos XML</title>
<para>
Hibernate te permite trabajar con datos XML persistentes en casi la misma forma
que trabajas con POJOs persistentes. Un árbol XML analizado (parsed) puede ser
pensado como sólo otra forma de representar los datos relacionales a nivel de objetos,
en vez de POJOs.
</para>
<para>
Hibernate soporta dom4j como API para manipular árboles XML. Puedes escribir
consultas que traigan árboles dom4j de la base de datos y tener cualquier modificación
que hagas al árbol sincronizada automáticamente a la base de datos. Puedes incluso tomar
un documento XML, analizarlo usando dom4j, y escribirlo a la base de datos con cualquiera
de las operaciones básicas de Hibernate: <literal>persist(), saveOrUpdate(), merge(),
delete(), replicate()</literal> (la fusión no está aún soportada).
</para>
<para>
Esta funcionalidad tiene muchas aplicaciones incluyendo la importación/exportación de datos,
externalización de datos de entidad vía JMS o SOAP y reportes basados en XSLT.
</para>
<para>
Un solo mapeo puede ser usado para mapear simultáneamente las propiedades de una clase y los nodos de un
documento XML a la base de datos, o, si no hay ninguna clase a mapear, puede ser usado para mapear sólo
el XML.
</para>
<sect2 id="xml-intro-mapping">
<title>Especificando los mapeos de XML y de clase juntos</title>
<para>
He aquí un ejemplo de mapear un POJO y XML simultáneamente:
</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>Especificando sólo un mapeo XML</title>
<para>
He aquí un ejemplo donde no hay ninguna clase POJO:
</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>
Este mapeo te permite acceder a los datos como un árbol dom4j, o como un grafo de pares nombre/valor de
propiedad (<literal>Map</literal>s de Java). Los nombres de propiedades son construcciones puramente
lógicas a las que se puede hacer referencia en consultas HQL.
</para>
</sect2>
</sect1>
<sect1 id="xml-mapping" revision="1">
<title>Mapeo de metadatos XML</title>
<para>
Muchos elementos de mapeo de Hibernate aceptan el atributo <literal>node</literal>. Esto te permite espcificar
el nombre de un atributo o elemento XML que contenga los datos de la propiedad o entidad. El formato del
atributo <literal>node</literal> debe ser uno de los siguientes:
</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>"element-name"</literal> - mapea al elemento XML mencionado</para>
</listitem>
<listitem>
<para><literal>"@attribute-name"</literal> - mapea al atributo XML mencionado</para>
</listitem>
<listitem>
<para><literal>"."</literal> - mapea al elemento padre</para>
</listitem>
<listitem>
<para>
<literal>"element-name/@attribute-name"</literal> -
mapea al atributo mencionado del elemento mencionado
</para>
</listitem>
</itemizedlist>
<para>
Para las colecciones y asociaciones monovaluadas, existe un atributo adicional <literal>embed-xml</literal>.
Si <literal>embed-xml="true"</literal>, que es el valor por defecto, el árbol XML para la entidad
asociada (o colección de tipo de valor) será embebida directamente en el árbol XML para la entidad que
posee la asociación. En otro caso, si <literal>embed-xml="false"</literal>, sólo el valor identificador
referenciado aparecerá en el XML para asociaciones de punto único y para las colecciones simplemente
no aparecerá en absoluto.
</para>
<para>
¡Debes ser cuidadoso de no dejar <literal>embed-xml="true"</literal> para demasiadas asociaciones,
ya que XML no trata bien la circularidad!
</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>
en este caso, hemos decidido embeber la colección de ids de cuenta, pero no los datos reales de cuenta.
La siguiente consulta HQL:
</para>
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
<para>
devolvería conjuntos de datos como estos:
</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>
Si estableces <literal>embed-xml="true"</literal> en el mapeo <literal>&lt;one-to-many&gt;</literal>, los datos
podrían verse así:
</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>Manipulando datos XML</title>
<para>
Vamos a releer y actualizar documentos XML en la aplicación. Hacemos esto obteniendo una sesión dom4j:
</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>
Es extremadamente útil combinar esta funcionalidad con la operación <literal>replicate()</literal>
de Hibernate para implementar la importación/exportación de datos basada en XML.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,97 @@
A {
color: #003399;
}
A:active {
color: #003399;
}
A:visited {
color: #888888;
}
P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
color: #000000;
}
TD, TH, SPAN {
color: #000000;
}
BLOCKQUOTE {
margin-right: 0px;
}
H1, H2, H3, H4, H5, H6 {
color: #000000;
font-weight:500;
margin-top:10px;
padding-top:15px;
}
TABLE {
border-collapse: collapse;
border-spacing:0;
border: 1px thin black;
empty-cells: hide;
}
TD {
padding: 4pt;
}
H1 { font-size: 150%; }
H2 { font-size: 140%; }
H3 { font-size: 110%; font-weight: bold; }
H4 { font-size: 110%; font-weight: bold;}
H5 { font-size: 100%; font-style: italic; }
H6 { font-size: 100%; font-style: italic; }
TT {
font-size: 90%;
font-family: "Courier New", Courier, monospace;
color: #000000;
}
PRE {
font-size: 100%;
padding: 5px;
border-style: solid;
border-width: 1px;
border-color: #CCCCCC;
background-color: #F4F4F4;
}
UL, OL, LI {
list-style: disc;
}
HR {
width: 100%;
height: 1px;
background-color: #CCCCCC;
border-width: 0px;
padding: 0px;
color: #CCCCCC;
}
.variablelist {
padding-top: 10;
padding-bottom:10;
margin:0;
}
.itemizedlist, UL {
padding-top: 0;
padding-bottom:0;
margin:0;
}
.term {
font-weight:bold;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,429 @@
<?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>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,334 @@
<?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>

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -0,0 +1,250 @@
<?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>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-manual</artifactId>
<version>3.3.0.beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hibernate-manual-${translation}</artifactId>
<packaging>pom</packaging>
<name>Hibernate Manual (${translation})</name>
<properties>
<translation>fr-FR</translation>
</properties>
</project>

View File

@ -0,0 +1,188 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
"../support/docbook-dtd/docbookx.dtd"
[
<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
<!ENTITY architecture SYSTEM "modules/architecture.xml">
<!ENTITY configuration SYSTEM "modules/configuration.xml">
<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
<!ENTITY session-api SYSTEM "modules/session_api.xml">
<!ENTITY transactions SYSTEM "modules/transactions.xml">
<!ENTITY events SYSTEM "modules/events.xml">
<!ENTITY batch SYSTEM "modules/batch.xml">
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
<!ENTITY filters SYSTEM "modules/filters.xml">
<!ENTITY xml SYSTEM "modules/xml.xml">
<!ENTITY performance SYSTEM "modules/performance.xml">
<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
]>
<book lang="fr">
<bookinfo>
<title>HIBERNATE - Persistance relationnelle en Java standard</title>
<subtitle>Documentation de référence d'Hibernate</subtitle>
<releaseinfo>3.3.0.beta1</releaseinfo>
</bookinfo>
<toc/>
<preface id="preface" revision="2">
<title>Préface</title>
<para>
Traducteur(s): Vincent Ricard, Sebastien Cesbron, Michael Courcy, Vincent Giguère, Baptiste Mathus, Emmanuel Bernard, Anthony Patricio
</para>
<para>
Travailler dans les deux univers que sont l'orienté objet et la base de données
relationnelle peut être lourd et consommateur en temps dans le monde de
l'entreprise d'aujourd'hui. Hibernate est un outil de mapping objet/relationnel
pour le monde Java. Le terme mapping objet/relationnel (ORM) décrit la technique
consistant à faire le lien entre la représentation objet des données
et sa représentation relationnelle basée sur un schéma SQL.
</para>
<para>
Non seulement, Hibernate s'occupe du transfert des classes Java dans les tables
de la base de données (et des types de données Java dans les types de données SQL),
mais il permet de requêter les données et propose des moyens de les récupérer.
Il peut donc réduire de manière significative le temps de développement qui
aurait été autrement perdu dans une manipulation manuelle des données via SQL
et JDBC.
</para>
<para>
Le but d'Hibernate est de libérer le développeur de 95 pourcent des tâches de
programmation liées à la persistance des données communes. Hibernate n'est
probablement pas la meilleure solution pour les applications centrées sur les
données qui n'utilisent que les procédures stockées pour implémenter la logique
métier dans la base de données, il est le plus utile dans les modèles métier orientés
objets dont la logique métier est implémentée dans la couche Java dite intermédiaire.
Cependant, Hibernate vous aidera à supprimer ou à encapsuler le code SQL
spécifique à votre base de données et vous aidera sur la tâche commune qu'est
la transformation des données d'une représentation tabulaire à une
représentation sous forme de graphe d'objets.
</para>
<para>
Si vous êtes nouveau dans Hibernate et le mapping Objet/Relationnel voire même en Java,
suivez ces quelques étapes :
</para>
<orderedlist>
<listitem>
<para>
Lisez <xref linkend="tutorial"/> pour un didacticiel plus long avec plus d'instructions étape par étape.
</para>
</listitem>
<listitem>
<para>
Lisez <xref linkend="architecture"/> pour comprendre les environnements dans lesquels
Hibernate peut être utilisé.
</para>
</listitem>
<listitem>
<para>
Regardez le répertoire <literal>eg</literal> de la distribution Hibernate, il contient
une application simple et autonome. Copiez votre pilote JDBC dans le répertoire
<literal>lib/</literal> et éditez <literal>src/hibernate.properties</literal>, en
positionnant correctement les valeurs pour votre base de données. A partir d'une
invite de commande dans le répertoire de la distribution, tapez <literal>ant eg</literal>
(cela utilise Ant), ou sous Windows tapez <literal>build eg</literal>.
</para>
</listitem>
<listitem>
<para>
Faîtes de cette documentation de référence votre principale source d'information.
Pensez à lire <emphasis>Hibernate in Action</emphasis>
(http://www.manning.com/bauer) si vous avez besoin de plus d'aide avec le design
d'applications ou si vous préférez un tutoriel pas à pas. Visitez aussi
http://caveatemptor.hibernate.org et téléchargez l'application exemple
pour Hibernate in Action.
</para>
</listitem>
<listitem>
<para>
Les questions les plus fréquemment posées (FAQs) trouvent leur réponse sur le
site web Hibernate.
</para>
</listitem>
<listitem>
<para>
Des démos, exemples et tutoriaux de tierces personnes sont référencés sur
le site web Hibernate.
</para>
</listitem>
<listitem>
<para>
La zone communautaire (Community Area) du site web Hibernate est une
bonne source d'information sur les design patterns et sur différentes
solutions d'intégration d'Hibernate (Tomcat, JBoss, Spring Framework, Struts,
EJB, etc).
</para>
</listitem>
</orderedlist>
<para>
Si vous avez des questions, utilisez le forum utilisateurs du site web Hibernate.
Nous utilisons également l'outil de gestion des incidents JIRA pour tout ce qui
est rapports de bogue et demandes d'évolution. Si vous êtes intéressé par le
développement d'Hibernate, joignez-vous à la liste de diffusion de développement.
</para>
<para>
Le développement commercial, le support de production et les formations à Hibernate
sont proposés par JBoss Inc (voir http://www.hibernate.org/SupportTraining/). Hibernate
est un projet Open Source professionnel et un composant critique de la suite de produits
JBoss Enterprise Middleware System (JEMS).
</para>
</preface>
&tutorial;
&architecture;
&configuration;
&persistent-classes;
&basic-mapping;
&collection-mapping;
&association-mapping;
&component-mapping;
&inheritance-mapping;
&session-api;
&transactions;
&events;
&batch;
&query-hql;
&query-criteria;
&query-sql;
&filters;
&xml;
&performance;
&toolset-guide;
&example-parentchild;
&example-weblog;
&example-mappings;
&best-practices;
</book>

View File

@ -0,0 +1,351 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="architecture">
<title>Architecture</title>
<sect1 id="architecture-overview" revision="1">
<title>Généralités</title>
<para>
Voici une vue (très) haut niveau de l'architecture d'Hibernate :
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/overview.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
Ce diagramme montre Hibernate utilisant une base de données et des données
de configuration pour fournir un service de persistance (et des objets
persistants) à l'application.
</para>
<para>
Nous aimerions décrire une vue plus détaillée de l'architecture. Malheureusement,
Hibernate est flexible et supporte différentes approches. Nous allons en
montrer les deux extrêmes. L'architecture légère laisse l'application fournir
ses propres connexions JDBC et gérer ses propres transactions. Cette approche
utilise le minimum des APIs Hibernate :
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/lite.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
L'architecture la plus complète abstrait l'application des APIs JDBC/JTA
sous-jacentes et laisse Hibernate s'occuper des détails.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
Voici quelques définitions des objets des diagrammes :
<variablelist spacing="compact">
<varlistentry>
<term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
<listitem>
<para>
Un cache threadsafe (immuable) des mappings vers une (et une seule) base
de données. Une factory (fabrique) de <literal>Session</literal> et un client
de <literal>ConnectionProvider</literal>. Peut contenir un cache optionnel de
données (de second niveau) qui est réutilisable entre les différentes transactions
que cela soit au sein du même processus (JVLM) ou par plusieurs n½uds d'un cluster.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Session (<literal>org.hibernate.Session</literal>)</term>
<listitem>
<para>
Un objet mono-threadé, à durée de vie courte, qui représente une conversation
entre l'application et l'entrepôt de persistance. Encapsule une connexion JDBC.
Factory (fabrique) des objets <literal>Transaction</literal>. Contient un cache
(de premier niveau) des objets persistants, ce cache est obligatoire. Il est
utilisé lors de la navigation dans le graphe d'objets ou lors de la récupération
d'objets par leur identifiant.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Objets et Collections persistants</term>
<listitem>
<para>
Objets mono-threadés à vie courte contenant l'état de persistance
et la fonction métier. Ceux-ci sont en général les objets de type JavaBean
(ou POJOs) ; la seule particularité est qu'ils sont associés avec une (et
une seule) <literal>Session</literal>. Dès que la <literal>Session</literal>
est fermée, ils seront détachés et libres d'être utilisés par n'importe laquelle
des couches de l'application (ie. de et vers la présentation en tant que Data
Transfer Objects - DTO : objet de transfert de données).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Objets et collections transients</term>
<listitem>
<para>
Instances de classes persistantes qui ne sont actuellement pas associées à
une <literal>Session</literal>. Elles ont pu être instanciées par l'application
et ne pas avoir (encore) été persistées ou elle ont pu être instanciées par
une <literal>Session</literal> fermée.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
<listitem>
<para>
(Optionnel) Un objet mono-threadé à vie courte utilisé par l'application
pour définir une unité de travail atomique. Abstrait l'application des
transactions sous-jacentes qu'elles soient JDBC, JTA ou CORBA. Une
<literal>Session</literal> peut fournir plusieurs <literal>Transaction</literal>s
dans certains cas. Toutefois, la délimitation des transactions, via l'API d'Hibernate
ou par la <literal>Transaction</literal> sous-jacente, n'est jamais optionnelle!
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
<listitem>
<para>
(Optionnel) Une fabrique de (pool de) connexions JDBC. Abstrait l'application
de la <literal>Datasource</literal> ou du <literal>DriverManager</literal> sous-jacent.
Non exposé à l'application, mais peut être étendu/implémenté par le développeur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
<listitem>
<para>
(Optionnel) Une fabrique d'instances de <literal>Transaction</literal>. Non
exposé à l'application, mais peut être étendu/implémenté par le développeur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>Interfaces d'extension</emphasis></term>
<listitem>
<para>
Hibernate fournit de nombreuses interfaces d'extensions optionnelles que
vous pouvez implémenter pour personnaliser le comportement de votre couche de persistance.
Reportez vous à la documentation de l'API pour plus de détails.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Dans une architecture légère, l'application n'aura pas à utiliser les APIs
<literal>Transaction</literal>/<literal>TransactionFactory</literal>
et/ou n'utilisera pas les APIs <literal>ConnectionProvider</literal>
pour utiliser JTA ou JDBC.
</para>
</sect1>
<sect1 id="architecture-states" revision="1">
<title>Etats des instances</title>
<para>
Une instance d'une classe persistante peut être dans l'un des trois états suivants,
définis par rapport à un <emphasis>contexte de persistance</emphasis>.
L'objet <literal>Session</literal> d'hibernate correspond à ce concept de
contexte de persistance :
</para>
<variablelist spacing="compact">
<varlistentry>
<term>passager (transient)</term>
<listitem>
<para>
L'instance n'est pas et n'a jamais été associée à un contexte
de persistance. Elle ne possède pas d'identité persistante (valeur de clé primaire)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>persistant</term>
<listitem>
<para>
L'instance est associée au contexte de persistance.
Elle possède une identité persistante (valeur de clé primaire)
et, peut-être, un enregistrement correspondant dans la base.
Pour un contexte de persistance particulier, Hibernate
<emphasis>garantit</emphasis> que l'identité persistante
est équivalente à l'identité Java (emplacement mémoire de l'objet)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>détaché</term>
<listitem>
<para>
L'instance a été associée au contexte de persistance mais ce
contexte a été fermé, ou l'instance a été sérialisée vers un
autre processus. Elle possède une identité persistante et
peut-être un enregistrement correspondant dans la base.
Pour des instances détachées, Hibernate ne donne aucune
garantie sur la relation entre l'identité persistante et
l'identité Java.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 id="architecture-jmx" revision="1">
<title>Intégration JMX</title>
<para>
JMX est le standard J2EE de gestion des composants Java.
Hibernate peut être géré via un service JMX standard. Nous fournissons une implémentation
d'un MBean dans la distribution : <literal>org.hibernate.jmx.HibernateService</literal>.
</para>
<para>
Pour avoir un exemple sur la manière de déployer Hibernate en tant que service JMX dans le
serveur d'application JBoss Application Server, référez vous au guide utilisateur JBoss (JBoss User Guide).
Si vous déployez Hibernate via JMX sur JBoss AS, vous aurez également les bénéfices suivants :
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Gestion de la session :</emphasis> Le cycle de vie de la <literal>Session</literal>
Hibernate peut être automatiquement limitée à la portée d'une transaction JTA.
Cela signifie que vous n'avez plus besoin d'ouvrir et de fermer la <literal>Session</literal>
manuellement, cela devient le travail de l'intercepteur EJB de JBoss. Vous n'avez
pas non plus à vous occuper des démarcations des transactions dans votre code (sauf
si vous voulez écrire une couche de persistance qui soit portable, dans ce cas vous
pouvez utiliser l'API optionnelle <literal>Transaction</literal> d'Hibernate).
Vous appelez l'<literal>HibernateContext</literal> pour accéder à la <literal>Session</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Déploiement HAR :</emphasis> Habituellement vous déployez le service JMX
Hibernate en utilisant le descripteur de déploiement de JBoss (dans un fichier EAR et/ou un SAR),
il supporte toutes les options de configuration usuelles d'une <literal>SessionFactory</literal>
Hibernate. Cependant, vous devez toujours nommer tous vos fichiers de mapping dans le
descripteur de déploiement. Si vous décidez d'utiliser le déploiement optionnel sous forme
de HAR, JBoss détectera automatiquement tous vos fichiers de mapping dans votre fichier HAR.
</para>
</listitem>
</itemizedlist>
<para>
Consultez le guide d'utilisation de JBoss AS pour plus d'informations sur ces options.
</para>
<para>
Les statistiques pendant l'exécution d'Hibernate (au runtime) sont une
autre fonctionnalité disponible en tant que service JMX. Voyez pour cela
<xref linkend="configuration-optional-statistics"/>.
</para>
</sect1>
<sect1 id="architecture-jca" revision="1">
<title>Support JCA</title>
<para>
Hibernate peut aussi être configuré en tant que connecteur JCA. Référez-vous au site
web pour de plus amples détails. Il est important de noter que le support JCA d'Hibernate
est encore considéré comme expérimental.
</para>
</sect1>
<sect1 id="architecture-current-session" revision="1">
<title>Sessions Contextuelles</title>
<para>
Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où
une session est liée à la portée d'un contexte particulier. Cependant, les applications ne définissent
pas toutes la notion de contexte de la même manière, et différents contextes définissent différentes
portées à la notion de "courant". Les applications à base d'Hibernate, versions précédentes à la 3.0
utilisaient généralement un principe maison de sessions contextuelles basées sur le <literal>ThreadLocal</literal>,
ainsi que sur des classes utilitaires comme <literal>HibernateUtil</literal>, ou utilisaient des
framework tiers (comme Spring ou Pico) qui fournissaient des sessions contextuelles basées sur
l'utilisation de proxy/interception.
</para>
<para>
A partir de la version 3.0.1, Hibernate a ajouté la méthode <literal>SessionFactory.getCurrentSession()</literal>.
Initialement, cela demandait l'usage de transactions <literal>JTA</literal>, où la
transaction <literal>JTA</literal> définissait la portée et le contexte de la session courante.
L'équipe Hibernate pense que, étant donnée la maturité des implémentations de <literal>JTA TransactionManager</literal> ,
la plupart (sinon toutes) des applications devraient utiliser la gestion des transactions par <literal>JTA</literal>
qu'elles soient ou non déployées dans un conteneur <literal>J2EE</literal>. Par conséquent,
vous devriez toujours contextualiser vos sessions, si vous en avez besoin, via la méthode basée sur JTA.
</para>
<para>
Cependant, depuis la version 3.1, la logique derrière
<literal>SessionFactory.getCurrentSession()</literal> est désormais branchable.
A cette fin, une nouvelle interface d'extension (<literal>org.hibernate.context.CurrentSessionContext</literal>)
et un nouveau paramètre de configuration (<literal>hibernate.current_session_context_class</literal>)
ont été ajoutés pour permettre de configurer d'autres moyens de définir la portée et le contexte des
sessions courantes.
</para>
<para>
Allez voir les Javadocs de l'interface <literal>org.hibernate.context.CurrentSessionContext</literal>
pour une description détaillée de son contrat. Elle définit une seule méthode,
<literal>currentSession()</literal>, depuis laquelle l'implémentation est responsable
de traquer la session courante du contexte. Hibernate fournit deux implémentation
de cette interface.
</para>
<itemizedlist>
<listitem>
<para>
<literal>org.hibernate.context.JTASessionContext</literal> - les sessions courantes sont
associées à une transaction <literal>JTA</literal>. La logique est la même que
l'ancienne approche basée sur JTA. Voir les javadocs pour les détails.
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.context.ThreadLocalSessionContext</literal> - les sessions
courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.
</para>
</listitem>
</itemizedlist>
<para>
Les deux implémentations fournissent un modèle de programmation de type "une session - une transaction
à la base de données", aussi connu sous le nom de <emphasis>session-per-request</emphasis>.
Le début et la fin d'une session Hibernate sont définis par la durée d'une transaction de base de données.
Si vous utilisez une démarcation programmatique de la transaction (par exemple sous J2SE ou JTA/UserTransaction/BMT),
nous vous conseillons d'utiliser l'API Hibernate <literal>Transaction</literal> pour masquer le système
de transaction utilisé. Si vous exécutez sous un conteneur EJB qui supporte CMT, vous n'avez besoin d'aucune
opérations de démarcations de session ou transaction dans votre code puisque tout
est géré de manière déclarative. Référez vous à <xref linkend="transactions"/> pour plus d'informations
et des exemples de code.
</para>
<para>
Le paramètre de configuration <literal>hibernate.current_session_context_class</literal>
définit quelle implémentation de <literal>org.hibernate.context.CurrentSessionContext</literal>
doit être utilisée. Notez que pour assurer la compatibilité avec les versions précédentes, si
ce paramètre n'est pas défini mais qu'un <literal>org.hibernate.transaction.TransactionManagerLookup</literal>
est configuré, Hibernate utilisera le <literal>org.hibernate.context.JTASessionContext</literal>.
La valeur de ce paramètre devrait juste nommer la classe d'implémentation à utiliser,
pour les deux implémentations fournies, il y a cependant deux alias correspondant: "jta" et "thread".
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,623 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="associations">
<title>Mapper les associations</title>
<sect1 id="assoc-intro" revision="1">
<title>Introduction</title>
<para>
Correctement mapper les associations est souvent la tâche la plus difficile.
Dans cette section nous traiterons les cas classiques les uns après les autres.
Nous commencerons d'abbord par les mappings unidirectionnels, puis nous aborderons
la question des mappings bidirectionnels. Nous illustrerons tous nos exemples
avec les classes <literal>Person</literal> et <literal>Address</literal>.
</para>
<para>
Nous utiliserons deux critères pour classer les associations : le premier
sera de savoir si l'association est bâti sur une table supplémentaire d'association
et le deuxieme sera basé sur la multiplicité de cette association.
</para>
<para>
Autoriser une clé étrangère nulle est considéré comme un mauvais choix dans
la construction d'un modèle de données. Nous supposerons donc que dans tous
les exemples qui vont suivre on aura interdit la valeur nulle pour les clés
étrangères. Attention, ceci ne veut pas dire que Hibernate ne supporte pas
les clés étrangères pouvant prendre des valeurs nulles, les exemples qui suivent
continueront de fonctionner si vous décidiez ne plus imposer la contrainte
de non-nullité sur les clés étrangères.
</para>
</sect1>
<sect1 id="assoc-unidirectional" revision="1">
<title>Association unidirectionnelle</title>
<sect2 id="assoc-unidirectional-m21" >
<title>plusieurs à un</title>
<para>
Une <emphasis>association plusieurs-à-un (many-to-one) unidirectionnelle </emphasis>
est le type que l'on rencontre le plus souvent dans les associations unidirectionnelles.
</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>un à un</title>
<para>
une <emphasis>association un-à-un (one-to-one) sur une clé étrangère</emphasis>
est presque identique. La seule différence est sur la contrainte d'unicité que
l'on impose à cette colonne.
</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>
Une <emphasis>association un-à-un (one-to-one) unidirectionnelle sur une clé primaire</emphasis>
utilise un générateur d'identifiant particulier. (Remarquez que nous avons inversé le sens de cette
association dans cet exemple.)
</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>un à plusieurs</title>
<para>
Une <emphasis>association un-à-plusieurs (one-to-many) unidirectionnelle sur une
clé étrangère</emphasis> est vraiment inhabituelle, et n'est pas vraiment recommandée.
</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>
Nous pensons qu'il est préférable d'utiliser une table de jointure pour ce type d'association.
</para>
</sect2>
</sect1>
<sect1 id="assoc-unidirectional-join" revision="1">
<title>Associations unidirectionnelles avec tables de jointure</title>
<sect2 id="assoc-unidirectional-join-12m">
<title>un à plusieurs</title>
<para>
Une <emphasis>association unidirectionnelle un-à-plusieurs (one-to-many) avec
une table de jointure</emphasis> est un bien meilleur choix.
Remarquez qu'en spécifiant <literal>unique="true"</literal>,
on a changé la multiplicité plusieurs-à-plusieurs (many-to-many) pour
un-à-plusieurs (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>plusieurs à un</title>
<para>
Une <emphasis>assiociation plusieurs-à-un (many-to-one) unidirectionnelle sur
une table de jointure</emphasis> est très fréquente quand l'association est optionnelle.
</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>un à un</title>
<para>
Une <emphasis>association unidirectionnelle un-à-un (one-to-one) sur une table
de jointure</emphasis> est extrèmement rare mais envisageable.
</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>plusieurs à plusieurs</title>
<para>
Finallement, nous avons <emphasis>l'association unidirectionnelle plusieurs-à-plusieurs (many-to-many)</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>Associations bidirectionnelles</title>
<sect2 id="assoc-bidirectional-m21" revision="2">
<title>un à plusieurs / plusieurs à un</title>
<para>
Une <emphasis>association bidirectionnelle plusieurs à un (many-to-one)</emphasis>
est le type d'association que l'on rencontre le plus souvent. (c'est la façon standard de créer
des relations parents/enfants.)
</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>
Si vous utilisez une <literal>List</literal> (ou toute autre collection indexée) vous devez
paramétrer la colonne <literal>key</literal> de la clé étrangère à <literal>not null</literal>,
et laisser Hibernate gérer l'association depuis l'extrémité collection pour maintenir l'index
de chaque élément (rendant l'autre extrémité virtuellement inverse en paramétrant
<literal>update="false"</literal> et <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>
</sect2>
<sect2 id="assoc-bidirectional-121">
<title>Un à un</title>
<para>
Une <emphasis>association bidirectionnelle un à un (one-to-one) sur une clé étrangère</emphasis>
est aussi très fréquente.
</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>
Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une clé primaire</emphasis>
utilise un générateur particulier d'id.
</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>Associations bidirectionnelles avec table de jointure</title>
<sect2 id="assoc-bidirectional-join-12m">
<title>un à plusieurs / plusieurs à un</title>
<para>
Une <emphasis>association bidirectionnelle un-à-plusieurs (one-to-many) sur une table de jointure </emphasis>.
Remarquez que <literal>inverse="true"</literal> peut s'appliquer sur les deux extrémités de l'
association, sur la collection, ou sur la jointure.
</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>Un à un</title>
<para>
Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une table de jointure</emphasis>
est extrèmement rare mais envisageable.
</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>plusieurs à plusieurs</title>
<para>
Finallement nous avons <emphasis>l'association bidirectionnelle plusieurs à plusieurs</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>Des mappings plus complexes</title>
<para>
Des associations encore plus complexes sont <emphasis>extrêmement</emphasis> rares.
Hibernate permet de gérer des situations plus complexes en utilisant des
parties SQL dans les fichiers de mapping. Par exemple, si une table
avec l'historiques des informations d'un compte définit les colonnes
<literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
et <literal>effectiveStartDate</literal>, mappées de telle sorte:
</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>
alors nous pouvons mapper une association à l'instance <emphasis>courante</emphasis>
(celle avec une <literal>effectiveEndDate</literal>) nulle en utilisant:
</para>
<programlisting><![CDATA[<many-to-one name="currentAccountInfo"
property-ref="currentAccountKey"
class="AccountInfo">
<column name="accountNumber"/>
<formula>'1'</formula>
</many-to-one>]]></programlisting>
<para>
Dans un exemple plus complexe, imaginez qu'une association entre
<literal>Employee</literal> et <literal>Organization</literal> est gérée
dans une table <literal>Employment</literal> pleines de données historiques.
Dans ce cas, une association vers l'employeur <emphasis>le plus récent</emphasis>
(celui avec la <literal>startDate</literal> la plus récente) pourrait être mappée comme cela:
</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>
Vous pouvez être créatif grace à ces possibilités, mais il est généralement plus pratique
d'utiliser des requêtes HQL ou criteria dans ce genre de situation.
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="batch">
<title>Traitement par paquet</title>
<para>
Une approche naïve pour insérer 100 000 lignes dans la base de données en utilisant
Hibernate pourrait ressembler à ça :
</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>
Ceci devrait s'écrouler avec une <literal>OutOfMemoryException</literal> quelque
part aux alentours de la 50 000ème ligne. C'est parce qu'Hibernate cache toutes
les instances de <literal>Customer</literal> nouvellement insérées dans le cache
de second niveau.
</para>
<para>
Dans ce chapitre nous montrerons comment éviter ce problème. D'abord, cependant,
si vous faites des traitements par batch, il est absolument critique que vous
activiez l'utilisation ds paquet JDBC (NdT : JDBC batching), si vous avez l'intention
d'obtenir des performances raisonnables. Configurez la taille du paquet JDBC avec un
nombre raisonnable (disons, 10-50) :
</para>
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
<para>
Vous pourriez aussi vouloir faire cette sorte de travail dans un traitement où
l'interaction avec le cache de second niveau est complètement désactivé :
</para>
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
<sect1 id="batch-inserts">
<title>Insertions en paquet</title>
<para>
Lorsque vous rendez des nouveaux objets persistants, vous devez régulièrement appeler
<literal>flush()</literal> et puis <literal>clear()</literal> sur la session,
pour contrôler la taille du cache de premier niveau.
</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, même taille que la taille du paquet JDBC
//flush un paquet d'insertions et libère la mémoire :
session.flush();
session.clear();
}
}
tx.commit();
session.close();]]></programlisting>
</sect1>
<sect1 id="batch-update" >
<title>Paquet de mises à jour</title>
<para>
Pour récupérer et mettre à jour des données les mêmes idées s'appliquent. En plus,
vous avez besoin d'utiliser <literal>scroll()</literal> pour tirer partie des
curseurs côté serveur pour les requêtes qui retournent beaucoup de lignes de données.
</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 un paquet de mises à jour et libère la mémoire :
session.flush();
session.clear();
}
}
tx.commit();
session.close();]]></programlisting>
</sect1>
<sect1 id="batch-statelesssession">
<title>L'interface StatelessSession</title>
<para>
Alternativement, Hibernate fournit une API orientée commande qui peut être
utilisée avec des flux de données pour et en provenance de la base de données
sous la forme d'objets détachés. Une <literal>StatelessSession</literal> n'a pas
de contexte de persistance associé et ne fournit pas beaucoup de sémantique de
durée de vie de haut niveau. En particulier, une session sans état n'implémente
pas de cache de premier niveau et n'interagit pas non plus avec un cache de
seconde niveau ou un cache de requêtes. Elle n'implémente pas les transactions
ou la vérification sale automatique (NdT : automatic dirty checking). Les
opérations réalisées avec une session sans état ne sont jamais répercutées
en cascade sur les instances associées. Les collections sont ignorées par une
session sans état. Les opérations exécutées via une session sans état outrepasse
le modèle d'événements d'Hibernate et les intercepteurs. Les sessions sans état sont
vulnérables aux effets de modification des données, ceci est dû au manque de cache
de premier niveau. Une session sans état est une abstraction bas niveau, plus
proche de la couche JDBC sous-jacente.
</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>
Notez que dans le code de l'exemple, les intances de <literal>Customer</literal>
retournées par la requête sont immédiatement détachées. Elles ne sont jamais
associées à un contexte de persistance.
</para>
<para>
Les opérations <literal>insert()</literal>, <literal>update()</literal> et
<literal>delete()</literal> définies par l'interface <literal>StatelessSession</literal>
sont considérées comme des opérations d'accès direct aux lignes de la base de données,
ce qui résulte en une exécution immédiate du SQL <literal>INSERT</literal>, <literal>UPDATE</literal>
ou <literal>DELETE</literal> respectif. De là, elles ont des sémantiques tres différentes des
opérations <literal>save()</literal>, <literal>saveOrUpdate()</literal>
et <literal>delete()</literal> définies par l'interface <literal>Session</literal>.
</para>
</sect1>
<sect1 id="batch-direct" revision="2">
<title>Opérations de style DML</title>
<para>
Comme déjà discuté avant, le mapping objet/relationnel automatique et transparent
est intéressé par la gestion de l'état de l'objet. Ceci implique que l'état de l'objet
est disponible en mémoire, d'où manipuler (en utilisant des expressions du langage de
manipulation de données - <literal>Data Manipulation Language</literal> (DML) - SQL)
les données directement dans la base n'affectera pas l'état en mémoire. Pourtant, Hibernate
fournit des méthodes pour l'exécution d'expression DML de style SQL lesquelles sont
réalisées à travers le langage de requête d'Hibernate (<xref linkend="queryhql">HQL</xref>).
</para>
<para>
La pseudo-syntaxe pour les expressions <literal>UPDATE</literal> et <literal>DELETE</literal>
est : <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>.
Certains points sont à noter :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Dans la clause from, le mot-clef FROM est optionnel
</para>
</listitem>
<listitem>
<para>
Il ne peut y avoir qu'une seule entité nommée dans la clause from ; elle peut
optionnellement avoir un alias. Si le nom de l'entité a un alias, alors
n'importe quelle référence de propriété doit être qualifiée en ayant un alias ;
si le nom de l'entité n'a pas d'alias, alors il est illégal pour n'importe quelle
référence de propriété d'être qualifiée.
</para>
</listitem>
<listitem>
<para>
Aucune jointure (implicite ou explicite) ne peut être spécifiée dans une requête HQL.
Les sous-requêtes peuvent être utilisées dans la clause where ; les sous-requêtes,
elles-mêmes, peuvent contenir des jointures.
</para>
</listitem>
<listitem>
<para>
La clause where est aussi optionnelle.
</para>
</listitem>
</itemizedlist>
<para>
Par exemple, pour exécuter un <literal>UPDATE</literal> HQL, utilisez la méthode
<literal>Query.executeUpdate()</literal> (la méthode est données pour ceux
qui sont familiers avec <literal>PreparedStatement.executeUpdate()</literal> de
JDBC) :
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// ou 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>
Pour exécuter un <literal>DELETE</literal> HQL, utilisez la même méthode
<literal>Query.executeUpdate()</literal> :
</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>
La valeur du <literal>int</literal> retourné par la méthode <literal>Query.executeUpdate()</literal>
indique le nombre d'entités affectées par l'opération. Considérez que cela peut ou pas
corréler le nombre de lignes affectés dans la base de données. Une opération HQL
pourrait entraîner l'exécution de multiples expressions SQL réelles, pour des classes
filles mappées par jointure (NdT: join-subclass), par exemple. Le nombre retourné
indique le nombre d'entités réelles affectées par l'expression. Retour à l'exemple de la
classe fille mappée par jointure, un effacement d'une des classes filles peut réellement
entraîner des suppressions pas seulement dans la table qui mappe la classe fille, mais
aussi dans la table "racine" et potentillement dans les tables des classes filles plus bas
dans la hiérarchie d'héritage.
</para>
<para>
La pseudo-syntaxe pour l'expression <literal>INSERT</literal> est :
<literal>INSERT INTO EntityName properties_list select_statement</literal>. Quelques
points sont à noter :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Seule la forme INSERT INTO ... SELECT ... est supportée ; pas la forme INSERT INTO ... VALUES ... .
</para>
<para>
La properties_list est analogue à la <literal>spécification de la colonne</literal>
The properties_list is analogous to the <literal>column speficiation</literal> dans
l'expression SQL <literal>INSERT</literal>. Pour les entités impliquées dans
un héritage mappé, seules les propriétés directement définies à ce niveau de classe
donné peuvent être utilisées dans properties_list. Les propriétés de la classe mère
ne sont pas permises ; et les propriétés des classes filles n'ont pas de sens. En
d'autres mots, les expressions <literal>INSERT</literal> par nature non polymorphiques.
</para>
</listitem>
<listitem>
<para>
select_statement peut être n'importe quelle requête de sélection HQl valide, avec
l'avertissement que les types de retour doivent correspondre aux types attendus par
l'insertion. Actuellement, c'est vérifié durant la compilation de la requête plutôt
que la vérification soit reléguée à la base de données. Notez cependant que cela
pourrait poser des problèmes entre les <literal>Type</literal>s d'Hibernate qui
sont <emphasis>équivalents</emphasis> opposé à <emphasis>égaux</emphasis>. Cela
pourrait poser des problèmes avec des disparités entre une propriété définie
comme un <literal>org.hibernate.type.DateType</literal> et une propriété définie
comme un <literal>org.hibernate.type.TimestampType</literal>, même si la base de données
ne ferait pas de distinction ou ne serait pas capable de gérer la conversion.
</para>
</listitem>
<listitem>
<para>
Pour la propriéte id, l'expression d'insertion vous donne deux options. Vous
pouvez soit spécifier explicitement la propriété id dans properties_list
(auquel cas sa valeur est extraite de l'expression de sélection correspondante),
soit l'omettre de properties_list (auquel cas une valeur générée est utilisée).
Cette dernière option est seulement disponible en utilisant le générateur d'identifiant
qui opère dans la base de données ; tenter d'utiliser cette option avec n'importe quel
type de générateur "en mémoire" causera une exception durant l'analyse. Notez
que pour les buts de cette discussion, les générateurs "en base" sont considérés
être <literal>org.hibernate.id.SequenceGenerator</literal> (et ses classes filles)
et n'importe quelles implémentations de
<literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
L'exception la plus notable ici est <literal>org.hibernate.id.TableHiLoGenerator</literal>,
qu ne peut pas être utilisée parce qu'il ne propose pas un moyen de d'exposer ses valeurs
par un select.
</para>
</listitem>
<listitem>
<para>
Pour des propriétés mappées comme <literal>version</literal> ou <literal>timestamp</literal>,
l'expression d'insertion vous donne deux options. Vous pouvez soit spécifier la propriété dans
properties_list (auquel cas sa valeur est extraite des expressions select correspondantes),
soit l'omettre de properties_list (auquel cas la <literal>valeur de graine</literal>
(NdT : seed value) définie par le <literal>org.hibernate.type.VersionType</literal> est utilisée).
</para>
</listitem>
</itemizedlist>
<para>
Un exemple d'exécution d'une expression <literal>INSERT</literal> HQL :
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="best-practices" revision="3">
<title>Meilleures pratiques</title>
<variablelist spacing="compact">
<varlistentry>
<term>Découpez finement vos classes et mappez les en utilisant <literal>&lt;component&gt;</literal>.</term>
<listitem>
<para>
Utilisez une classe <literal>Adresse</literal> pour encapsuler <literal>Rue</literal>,
<literal>Region</literal>, <literal>CodePostal</literal>.
Ceci permet la réutilisation du code et simplifie la maintenance.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Déclarez des propriétés d'identifiants dans les classes persistantes.</term>
<listitem>
<para>
Hibernate rend les propriétés d'identifiants optionnelles. Il existe beaucoup de raisons
pour lesquelles vous devriez les utiliser. Nous recommandons que vous utilisiez des identifiants
techniques (générés, et sans connotation métier).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Identifiez les clefs naturelles.</term>
<listitem>
<para>
Identifiez les clefs naturelles pour toutes les entités, et mappez les avec
<literal>&lt;natural-id&gt;</literal>. Implémentez <literal>equals()</literal> et
<literal>hashCode()</literal> pour comparer les propriétés qui composent la clef naturelle.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Placez chaque mapping de classe dans son propre fichier.</term>
<listitem>
<para>
N'utilisez pas un unique document de mapping. Mappez <literal>com.eg.Foo</literal> dans
le fichier <literal>com/eg/Foo.hbm.xml</literal>. Cela prend tout son sens lors
d'un travail en équipe.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Chargez les mappings comme des ressources.</term>
<listitem>
<para>
Déployez les mappings en même temps que les classes qu'ils mappent.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pensez à externaliser les chaînes de caractères.</term>
<listitem>
<para>
Ceci est une bonne habitude si vos requêtes appellent des fonctions SQL qui ne sont
pas au standard ANSI. Cette externalisation dans les fichiers de mapping rendra votre
application plus portable.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Utilisez les variables "bindées".</term>
<listitem>
<para>
Comme en JDBC, remplacez toujours les valeurs non constantes par "?". N'utilisez jamais
la manipulation des chaînes de caractères pour remplacer des valeurs non constantes dans
une requête ! Encore mieux, utilisez les paramètres nommés dans les requêtes.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Ne gérez pas vous même les connexions JDBC.</term>
<listitem>
<para>
Hibernate laisse l'application gérer les connexions JDBC. Vous ne devriez gérer vos connexions
qu'en dernier recours. Si vous ne pouvez pas utiliser les systèmes de connexions livrés,
réfléchissez à l'idée de fournir votre propre implémentation de <literal>org.hibernate.connection.ConnectionProvider</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pensez à utiliser les types utilisateurs.</term>
<listitem>
<para>
Supposez que vous ayez une type Java, de telle bibliothèque, qui a besoin d'être persisté mais
qui ne fournit pas les accesseurs nécessaires pour le mapper comme composant. Vous devriez
implémenter
<literal>org.hibernate.UserType</literal>.Cette approche libère le code de l'application
de l'implémentation des transformations vers / depuis les types Hibernate.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Utilisez du JDBC pur dans les goulets d'étranglement.</term>
<listitem>
<para>
Dans certaines parties critiques de votre système d'un point de vue performance, quelques opérations
peuvent tirer partie d'un appel JDBC natif.
Mais attendez de <emphasis>savoir</emphasis>
que c'est un goulet d'étranglement. Ne supposez jamais qu'un appel JDBC sera forcément plus
rapide. Si vous avez besoin d'utiliser JDBC directement, ouvrez une <literal>Session</literal>
Hibernate et utilisez la connexion SQL sous-jacente. Ainsi vous pourrez utiliser la même stratégie
de transation et la même gestion des connexions.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Comprendre le flush de <literal>Session</literal>.</term>
<listitem>
<para>
De temps en temps la Session synchronise ses états persistants avec la base de données.
Les performances seront affectées si ce processus arrive trop souvent. Vous pouvez parfois
minimiser les flush non nécessaires en désactivant le flush automatique ou même en changeant
l'ordre des opérations menées dans une transaction particulière.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Dans une architecture à trois couches, pensez à utiliser <literal>saveOrUpdate()</literal>.</term>
<listitem>
<para>
Quand vous utilisez une architecture à base de servlet / session bean, vous pourriez passer
des objets chargés dans le bean session vers et depuis la couche servlet / JSP. Utilisez
une nouvelle session pour traiter chaque requête.
Utilisez <literal>Session.merge()</literal> ou <literal>Session.saveOrUpdate()</literal> pour
synchroniser les objets avec la base de données.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Dans une architecture à deux couches, pensez à utiliser la déconnexion de session.</term>
<listitem>
<para>
Les transactions de bases de données doivent être aussi courtes que possible
pour une meilleure montée en charge.Cependant, il est souvent nécessaire d'implémenter
de longues <emphasis>transactions applicatives</emphasis>, une simple unité de travail du point de vue de
l'utilisateur. Une transaction applicative
peut s'étaler sur plusieurs cycles de requêtes/réponses du client.
Il est commun d'utiliser des objets détachés pour implémenter des transactions applicatives.
Une alternative, extrêmement appropriée dans une architecture à 2 couches, est de
maintenir un seul contact de persistance ouvert (session) pour toute la durée de vie
de la transaction applicative et simplement se déconnecter de la connexion JDBC à la fin de chaque requête,
et se reconnecter au début de la requête suivante. Ne partagez jamais une seule
session avec plus d'une transaction applicative, ou vous travaillerez avec des
données périmées.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Considérez que les exceptions ne sont pas rattrapables.</term>
<listitem>
<para>
Il s'agit plus d'une pratique obligatoire que d'une "meilleure pratique". Quand une exception
intervient, il faut faire un rollback de la <literal>Transaction</literal> et
fermer la <literal>Session</literal>.
Sinon, Hibernate ne peut garantir l'intégrité des états persistants en mémoire. En particulier,
n'utilisez pas <literal>Session.load()</literal> pour déterminer si une instance avec un identifiant
donné existe en base de données, utilisez <literal>Session.get()</literal> ou un requête.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Préférez le chargement tardif des associations.</term>
<listitem>
<para>
Utilisez le chargement complet avec modération.
Utilisez les proxies et les collections chargées tardivement
pour la plupart des associations vers des classes qui ne sont pas susceptibles
d'être complètement retenues dans le cache de second niveau.
Pour les assocations de classes en cache, où il y a une extrêmement
forte probabilité que l'élément soit en cache, désactivez explicitement le chargement
par jointures ouvertes en utilisant <literal>outer-join="false"</literal>.
Lorsqu'un chargement par jointure ouverte est approprié pour un cas d'utilisation
particulier, utilisez une requête avec un <literal>left join fetch</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Utilisez le pattern <emphasis>d'une ouverture de session dans une vue</emphasis>,
ou une <emphasis>phase d'assemblage</emphasis> disciplinée pour éviter des problèmes
avec des données non rapatriées.
</term>
<listitem>
<para>
Hibernate libère les développeurs de l'écriture fastidieuse des <emphasis>objets de transfert
de données (NdT : Data Transfer Objects)</emphasis> (DTO). Dans une architecture EJB traditionnelle,
les DTOs ont deux buts : premièrement, ils contournent le problème des "entity bean" qui ne sont pas
sérialisables ; deuxièmement, ils définissent implicitement une phase d'assemblage où toutes les
données utilisées par la vue sont rapatriées et organisées dans les DTOs avant de retourner sous le
contrôle de la couche de présentation. Hibernate élimine le premier but. Pourtant, vous aurez encore
besoin d'une phase d'assemblage (pensez vos méthodes métier comme ayant un contrat strict avec la
couche de présentation à propos de quelles données sont disponibles dans les objets détachés)
à moins que vous soyez préparés à garder le contexte de
persistance (la session) ouvert à travers tout le processus de rendu de la vue.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pensez à abstraite votre logique métier d'Hibernate.</term>
<listitem>
<para>
Cachez le mécanisme d'accès aux données (Hibernate) derrière une interface. Combinez les patterns
<emphasis>DAO</emphasis> et <emphasis>Thread Local Session</emphasis>. Vous pouvez même avoir quelques
classes persistées par du JDBC pur, associées à Hibernate via un <literal>UserType</literal> (ce conseil est
valable pour des applications de taille respectables ; il n'est pas valable pour une application
avec cinq tables).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>N'utilisez pas d'associations de mapping exotiques.</term>
<listitem>
<para>
De bons cas d'utilisation pour de vraies associations plusieurs-vers-plusieurs
sont rares. La plupart du temps vous avez besoin d'informations additionnelles
stockées dans la table d'association.
Dans ce cas, il est préférable d'utiliser deux associations un-vers-plusieurs vers une classe
de liaisons intermédiaire. En fait, nous pensons que la plupart des associations sont
de type un-vers-plusieurs ou plusieurs-vers-un, vous devez être très attentifs lorsque
vous utilisez autre chose et vous demander si c'est vraiment nécessaire.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Préférez les associations bidirectionnelles.</term>
<listitem>
<para>
Les associations unidirectionnelles sont plus difficiles à questionner.
Dans une grande application, la plupart des associations devraient être navigables dans les deux directions dans les requêtes.
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,402 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<chapter id="components">
<title>Mapping de composants</title>
<para>
La notion de <emphasis>composants</emphasis> est réutilisé dans différents contextes,
avec différents objectifs, à travers Hibernate.
</para>
<sect1 id="components-dependentobjects" revision="2" >
<title>Objects dépendants</title>
<para>
Le composant est un objet inclu dans un autre qui est sauvegardé comme une valeur, et
non pas comme une entité.
Le composant fait référence à la notion (au sens objet) de composition
(et non pas de composant au sens d'architecture de composants).
Par exemple on pourrait modélisé l'objet personne de cette façon:
</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>
Maintenant <literal>Name</literal> peut-être sauvegardé comme un composant de
<literal>Person</literal>. Remarquer que <literal>Name</literal> définit des methodes
d'accès et de modification pour ses propriétés persistantes, mais il n'a pas besoin
des interfaces ou des propriétés d'identification ( par exemple getId() ) qui sont propres aux entités.
</para>
<para>
Nous serions alors amené à mapper ce composant de cette façon:
</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>
La table person aurai les colonnes <literal>pid</literal>,
<literal>birthday</literal>,
<literal>initial</literal>,
<literal>first</literal> and
<literal>last</literal>.
</para>
<para>
Comme tous les types valeurs, les composants ne supportent pas les références partagés.
En d'autres mots, deux instances de person peuvent avoir un même nom, mais ces noms sont
indépendants, ils peuvent être identiques si on les compare par valeur mais ils représentent
deux objets distincts en mémoire. La notion de nullité pour un composant est
<emphasis>ad hoc</emphasis>. Quand il recharge l'objet qui contient le composant, Hibernate
supposera que si tous les champs du composants sont nuls alors le composant sera positionné
à la valeur null. Ce choix programmatif devrait être satisfaisant dans la plupart des cas.
</para>
<para>
Les propriétés d'un composant peuvent être de tous les types qu'Hibernate supporte habituellement
(collections, many-to-one associations, autres composants, etc). Les composants inclus ne doivent <emphasis>pas</emphasis>
être vus comme quelque chose d'exotique. Hibernate a été conçu pour supporter un modèle objet très granulaire.
</para>
<para>
Le <literal>&lt;component&gt;</literal> peut inclure dans la liste de ses propriétés
une référence au <literal>&lt;parent&gt;</literal> conteneur.
</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"/> <!-- référence arrière à Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
</sect1>
<sect1 id="components-incollections" revision="1">
<title>Collection d'objets dépendants</title>
<para>
Les collections d'objets dépendants sont supportés (exemple: un tableau de type
<literal>Name</literal>). Déclarer la collection de composants en remplaçant le tag <literal>&lt;element&gt;</literal>
par le tag <literal>&lt;composite-element&gt;</literal>.
</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>
Remarque: Si vous définissez un <literal>Set</literal> d'élément composite,
il est très important d'implémenter la méthode <literal>equals()</literal> et
<literal>hashCode()</literal> correctement.
</para>
<para>
Les élements composite peuvent aussi contenir des composants mais pas des collections.
Si votre élément composite contient aussi des composants, utilisez l'élément <literal>&lt;nested-composite-element&gt;</literal>
. Une collections de composants qui ccontiennent eux-mêmes des composants est un cas très exotique.
A ce stade demandez-vous si une association un-à-plusieurs ne serait pas plus approprié.
Essayez de re remodeler votre élément composite comme une entité ( Dans ce cas même si le modèle
Java est le même la logique de persitence et de relation sont tout de même différentes)
</para>
<para>
Remarque, le mapping d'éléments composites ne supporte pas la nullité des
propriétés lorsqu'on utilise un <literal>&lt;set&gt;</literal>. Hibernate
lorsqu'il supprime un objet utilise chaque colonne pour identifier un objet
(on ne peut pas utiliser des clés primaires distinctes dans une table d'éléments composites),
ce qui n'est pas possible avec des valeurs nulles. Vous devez donc choisir d'interdire la nullité
des propriétés d'un élément composite ou choisir un autre type de collection comme :
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal> ou <literal>&lt;idbag&gt;</literal>.
</para>
<para>
Un cas particulier d'élément composite est un élément composite qui inclut un élément
<literal>&lt;many-to-one&gt;</literal>. Un mapping comme celui-ci
vous permet d'associer les colonnes d'une table d'association plusieurs à plusieurs (many-to-many)
à la classse de l'élément composite. L'exemple suivant est une association plusieurs à plusieurs
de <literal>Order</literal> à <literal>Item</literal> à
<literal>purchaseDate</literal>, <literal>price</literal> et
<literal>quantity</literal> sont des propriétés de l'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>
Bien sûr, il ne peut pas y avoir de référence à l'achat (purchase) depuis l'article (item), pour
pouvoir naviguer de façon bidirectionnelle dans l'association. N'oubliez pas que les composants
sont de type valeurs et n'autorise pas les références partagées.
</para>
<para>Même les associations ternaires ou quaternaires sont possibles:</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>
Les éléments composites peuvent apparaître dans les requêtes en utilisant
la même syntaxe que associations
</para>
</sect1>
<sect1 id="components-asmapindex">
<title>Utiliser les composants comme index de map</title>
<para>
l'élément <literal>&lt;composite-map-key&gt;</literal>
vous permet d'utiliser une classe de composant comme indice de
<literal>Map</literal>. Assurez-vous d'avoir surdéfini
<literal>hashCode()</literal> et <literal>equals()</literal> dans la
classe du composant.
</para>
</sect1>
<sect1 id="components-compositeid" revision="1">
<title>Utiliser un composant comme identifiant</title>
<para>
Vous pouvez utiliser un composant comme identifiant d'une entité.
Mais pour cela la classe du composant doit respecter certaines règles.
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Elle doit implémenter <literal>java.io.Serializable</literal>.
</para>
</listitem>
<listitem>
<para>
Elle doit redéfinir <literal>equals()</literal> et
<literal>hashCode()</literal>, de façon cohérente avec le
fait qu'elle définit une clé composite dans la base de
données.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>
Remarque: avec hibernate3, la seconde règle n'est plus absolument
necessaire mais faîtes le quand même.</emphasis>
</para>
<para>
Vous ne pouvez pas utiliser de <literal>IdentifierGenerator</literal> pour générer
une clé composite, l'application devra définir elle même ses propres identifiants.
</para>
<para>
Utiliser l'élément <literal>&lt;composite-id&gt;</literal> (en incluant l'élément
<literal>&lt;key-property&gt;</literal>) à la place de l'habituel déclaration
<literal>&lt;id&gt;</literal>. Par exemple la classe
<literal>OrderLine</literal> qui dépend de la clé primaire
(composite) de <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>
Maintenant toutes clés étrangères référençant la table <literal>OrderLine</literal>
devra aussi être composite. Vous devez en tenir compte lorsque vous écrivez vos mapping d'association pour les autres classes.
Une association à <literal>OrderLine</literal> devrait être mappé de la façon suivante :
</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>
(Remarque: l'élément <literal>&lt;column&gt;</literal> est une alternative à l'attribut
<literal>column</literal> que l'on utilise partout.)
</para>
<para>
Une association <literal>plusieurs-à-plusieurs</literal> (many-to-many) à <literal>OrderLine</literal>
utilisera aussi une clé étrangère composite:
</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>
La collection des <literal>OrderLine</literal>s dans <literal>Order</literal>
utilisera:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(L'élément <literal>&lt;one-to-many&gt;</literal>, comme d'habitude, ne déclare pas de colonne.)
</para>
<para>
Si <literal>OrderLine</literal> lui-même possède une collection, celle-ci aura aussi
une clé composite étrangère.
</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>Composant Dynamique</title>
<para>
Vous pouvez même mapper une propriété de type <literal>Map</literal>:
</para>
<programlisting><![CDATA[<dynamic-component name="userAttributes">
<property name="foo" column="FOO"/>
<property name="bar" column="BAR"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>]]></programlisting>
<para>
La sémantique de l'association à un <literal>&lt;dynamic-component&gt;</literal>
est identique à celle que l'on utilise pour les composants.
L'avantage de ce type de mapping est qu'il pemet de déterminer les véritables propriétés
du bean au moment su déploiement en éditant simplement le document de mapping.
La manipulation du document de mapping pendant l'execution de l'application est aussi
possible en utilisant un parser DOM. Il ya même mieux, vous pouvez accéder (et changer)
le metamodel de configuration d'hibernate en utilisant l'objet <literal>Configuration</literal>
</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="events">
<title>Les intercepteurs et les événements</title>
<para>
Il est souvent utile pour l'application de réagir à certains événements
qui surviennent dans Hibernate. Cela autorise l'implémentation de certaines sortes de
fonctionnalités génériques, et d'extensions de fonctionnalités d'Hibernate.
</para>
<sect1 id="objectstate-interceptors" revision="2">
<title>Intercepteurs</title>
<para>
L'interface <literal>Interceptor</literal> fournit des "callbacks" de la session vers l'application
et permettent à l'application de consulter et/ou de manipuler des propriétés
d'un objet persistant avant qu'il soit sauvegardé, mis à jour, supprimé ou chargé.
Une utilisation possible de cette fonctionnalité est de tracer l'accès à l'information.
Par exemple, l'<literal>Interceptor</literal> suivant positionne
<literal>createTimestamp</literal> quand un <literal>Auditable</literal> est créé
et met à jour la propriété <literal>lastUpdateTimestamp</literal> quand un
<literal>Auditable</literal> est mis à jour.
</para>
<para>
Vous pouvez soit implémenter <literal>Interceptor</literal> directement ou (mieux)
étendre <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) {
// ne fait rien
}
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 postFlush(Iterator entities) {
System.out.println("Creations: " + creates + ", Updates: " + updates);
}
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>
L'intercepteur doit être spécifié quand une session est créée.
</para>
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
<para>
Vous pouvez aussi mettre un intercepteur au niveau global, en utilisant l'objet <literal>Configuration</literal>.
Dans ce cas, l'intercepteur doit être "threadsafe".
</para>
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
</sect1>
<sect1 id="objectstate-events" revision="3">
<title>Système d'événements</title>
<para>
Si vous devez réagir à des événements particuliers dans votre couche de persistance,
vous pouvez aussi utiliser l'architecture d'<emphasis>événements</emphasis> d'Hibernate3.
Le système d'événements peut être utilisé en supplément ou en remplacement des interceptors.
</para>
<para>
Essentiellement toutes les méthodes de l'interface <literal>Session</literal> sont corrélées à
un événement. Vous avez un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
(consultez la DTD du fichier de configuration XML ou le paquet <literal>org.hibernate.event</literal>
pour avoir la liste complète des types d'événement définis).
Quand une requête est faite à partir d'une de ces méthodes, la
<literal>Session</literal> Hibernate génère un événement approprié et le passe
au listener configuré pour ce type.
Par défaut, ces listeners implémentent le même traitement dans lequel ces méthodes
aboutissent toujours.
Cependant, vous êtes libre d'implémenter une version personnalisée d'une de ces
interfaces de listener (c'est-à-dire, le <literal>LoadEvent</literal> est traité par
l'implémentation de l'interface <literal>LoadEventListener</literal> déclarée), dans
quel cas leur implémentation devrait être responsable du traitement des
requêtes <literal>load()</literal> faites par la <literal>Session</literal>.
</para>
<para>
Les listeners devraient effectivement être considérés comme des singletons ; dans le sens
où ils sont partagés entre des requêtes, et donc ne devraient pas sauvegarder des états
de variables d'instance.
</para>
<para>
Un listener personnalisé devrait implémenter l'interface appropriée pour l'événement
qu'il veut traiter et/ou étendre une des classes de base (ou même l'événement prêt à
l'emploi utilisé par Hibernate comme ceux déclarés non-finaux à cette intention). Les
listeners personnalisés peuvent être soit inscrits par programmation à travers l'objet
<literal>Configuration</literal>, ou spécifiés la configuration XML d'Hibernate
(la configuration déclarative à travers le fichier de propriétés n'est pas supportée).
Voici un exemple de listener personnalisé pour l'événement de chargement :
</para>
<programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
// C'est une simple méthode définie par l'interface LoadEventListener
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
}
}]]></programlisting>
<para>
Vous avez aussi besoin d'une entrée de configuration disant à Hibernate d'utiliser
ce listener en plus du listener par défaut :
</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>
Vous pouvez aussi l'inscrire par programmation :
</para>
<programlisting><![CDATA[Configuration cfg = new Configuration();
LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
<para>
Les listeners inscrits déclarativement ne peuvent pas partager d'instances. Si le même
nom de classe est utilisée dans plusieurs éléments <literal>&lt;listener/&gt;</literal>,
chaque référence sera une instance distincte de cette classe. Si vous avez besoin de la
faculté de partager des instances de listener entre plusieurs types de listener, vous devez
utiliser l'approche d'inscription par programmation.
</para>
<para>
Pourquoi implémenter une interface et définir le type spécifique durant la configuration ?
Une implémentation de listener pourrait implémenter plusieurs interfaces de listener
d'événements. Avoir en plus le type défini durant l'inscription rend plus facile
l'activation ou la désactivation pendant la configuration.
</para>
</sect1>
<sect1 id="objectstate-decl-security" revision="2">
<title>Sécurité déclarative d'Hibernate</title>
<para>
Généralement, la sécurité déclarative dans les applications Hibernate est gérée dans la
couche de session. Maintenant, Hibernate3 permet à certaines actions d'être approuvées
via JACC, et autorisées via JAAS. Cette fonctionnalité optionnelle est construite
au dessus de l'architecture d'événements.
</para>
<para>
D'abord, vous devez configurer les listeners d'événements appropriés pour permettre
l'utilisation d'autorisations JAAS.
</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>
Notez que <literal>&lt;listener type="..." class="..."/&gt;</literal> est juste un raccourci
pour <literal>&lt;event type="..."&gt;&lt;listener class="..."/&gt;&lt;/event&gt;</literal>
quand il y a exactement un listener pour un type d'événement particulier.
</para>
<para>
Ensuite, toujours dans <literal>hibernate.cfg.xml</literal>, lier les permissions aux rôles :
</para>
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
<para>
Les noms de rôle sont les rôles compris par votre fournisseur JAAC.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,657 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="example-mappings">
<title>Exemple : quelques mappings</title>
<para>
Ce chapitre montre quelques mappings plus complexes.
</para>
<sect1 id="example-mappings-emp">
<title>Employeur/Employé (Employer/Employee)</title>
<para>
Le modèle suivant de relation entre <literal>Employer</literal> et
<literal>Employee</literal> utilise une vraie classe entité (<literal>Employment</literal>)
pour représenter l'association. On a fait cela parce qu'il peut y avoir plus d'une période
d'emploi pour les deux mêmes parties. Des composants sont utilisés pour modéliser les
valeurs monétaires et les noms des employés.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
Voici un document de mapping possible :
</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>
Et voici le schéma des tables générées par <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>Auteur/Travail (Author/Work)</title>
<para>
Soit le modèle de la relation entre <literal>Work</literal>, <literal>Author</literal>
et <literal>Person</literal>. Nous représentons la relation entre <literal>Work</literal>
et <literal>Author</literal> comme une association plusieurs-vers-plusieurs. Nous avons choisi de
représenter la relation entre <literal>Author</literal> et <literal>Person</literal>
comme une association un-vers-un. Une autre possibilité aurait été que
<literal>Author</literal> hérite de <literal>Person</literal>.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
Le mapping suivant représente exactement ces relations :
</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>
Il y a quatre tables dans ce mapping. <literal>works</literal>,
<literal>authors</literal> et <literal>persons</literal> qui contiennent
respectivement les données de work, author et person.
<literal>author_work</literal> est une table d'association qui lie authors
à works. Voici le schéma de tables, généré par <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>Client/Commande/Produit (Customer/Order/Product)</title>
<para>
Imaginons maintenant le modèle de relation entre <literal>Customer</literal>,
<literal>Order</literal>, <literal>LineItem</literal> et <literal>Product</literal>.
Il y a une association un-vers-plusieurs entre <literal>Customer</literal> et
<literal>Order</literal>, mais comment devrions nous représenter <literal>Order</literal> /
<literal>LineItem</literal> / <literal>Product</literal>? J'ai choisi de mapper
<literal>LineItem</literal> comme une classe d'association représentant l'association
plusieurs-vers-plusieurs entre <literal>Order</literal> et <literal>Product</literal>. Dans
Hibernate, on appelle cela un élément composite.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
</imageobject>
</mediaobject>
<para>
Le document de mapping :
</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> et
<literal>products</literal> contiennent les données de customer, order, order line item et product.
<literal>line_items</literal> est aussi la table d'association liant orders à 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>Divers mappings d'exemple</title>
<para>
Ces exemples sont tous pris de la suite de tests d'Hibernate. Vous en trouverez beaucoup d'autres.
Regardez dans le dossier <literal>test</literal> de la distribution d'Hibernate.
</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>Exemple de clef composée</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 avec une clef composée partagée</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>Contenu basé sur une 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 sur des clefs alternées</title>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="hilo"/>
</id>
<property name="name" length="100"/>
<one-to-one name="address"
property-ref="person"
cascade="all"
fetch="join"/>
<set name="accounts"
inverse="true">
<key column="userId"
property-ref="userId"/>
<one-to-many class="Account"/>
</set>
<property name="userId" length="8"/>
</class>
<class name="Address">
<id name="id">
<generator class="hilo"/>
</id>
<property name="address" length="300"/>
<property name="zip" length="5"/>
<property name="country" length="25"/>
<many-to-one name="person" unique="true" not-null="true"/>
</class>
<class name="Account">
<id name="accountId" length="32">
<generator class="uuid"/>
</id>
<many-to-one name="user"
column="userId"
property-ref="userId"/>
<property name="type" not-null="true"/>
</class>]]></programlisting>
</sect2>
</sect1>
</chapter>

View File

@ -0,0 +1,372 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="example-parentchild">
<title>Exemple : Père/Fils</title>
<para>
L'une des premières choses que les nouveaux utilisateurs essaient de faire avec Hibernate est de modéliser
une relation père/fils. Il y a deux approches différentes pour cela. Pour un certain nombre de raisons, la méthode la
plus courante, en particulier pour les nouveaux utilisateurs, est de modéliser les deux relations <literal>Père</literal>
et <literal>Fils</literal> comme des classes entités liées par une association <literal>&lt;one-to-many&gt;</literal> du
<literal>Père</literal> vers le <literal>Fils</literal> (l'autre approche est de déclarer le <literal>Fils</literal>
comme un <literal>&lt;composite-element&gt;</literal>). Il est évident que le sens de l'association un vers plusieurs
(dans Hibernate) est bien moins proche du sens habituel d'une relation père/fils que ne l'est celui d'un
élément cmposite. Nous allons vous expliquer comment utiliser une association <emphasis>un vers plusieurs bidirectionnelle
avec cascade</emphasis> afin de modéliser efficacement et élégamment une relation père/fils, ce n'est vraiment
pas difficile !
</para>
<sect1 id="example-parentchild-collections">
<title>Une note à propos des collections</title>
<para>
Les collections Hibernate sont considérées comme étant une partie logique
de l'entité dans laquelle elles sont contenues ; jamais des entités qu'elle
contient. C'est une distinction crutiale ! Les conséquences sont les suivantes :
</para>
<itemizedlist>
<listitem>
<para>
Quand nous ajoutons / retirons un objet d'une collection, le numéro de version du
propriétaire de la collection est incrémenté.
</para>
</listitem>
<listitem>
<para>
Si un objet qui a été enlevé d'une collection est une instance de type valeur (ex :
élément composite), cet objet cessera d'être persistant et son état sera complètement effacé
de la base de données. Par ailleurs, ajouter une instance de type valeur dans une collection
aura pour conséquence que son état sera immédiatement persistant.
</para>
</listitem>
<listitem>
<para>
Si une entité est enlevée d'une collection (association un-vers-plusieurs
ou plusieurs-vers-plusieurs), par défaut, elle ne sera pas effacée. Ce comportement
est complètement logique - une modification de l'un des états internes d'une entité
ne doit pas causer la disparition de l'entité associée !
De même, l'ajout d'une entité dans une collection n'engendre pas,
par défaut, la persistance de cette entité.
</para>
</listitem>
</itemizedlist>
<para>
Le comportement par défaut est donc que l'ajout d'une entité dans une collection créé
simplement le lien entre les deux entités, et qu'effacer une entité supprime ce lien.
C'est le comportement le plus approprié dans la plupart des cas. Ce comportement n'est
cependant pas approprié lorsque la vie du fils est liée au cycle de vie du père.
</para>
</sect1>
<sect1 id="example-parentchild-bidir">
<title>un-vers-plusieurs bidirectionnel</title>
<para>
Supposons que nous ayons une simple association <literal>&lt;one-to-many&gt;</literal>
de <literal>Parent</literal> vers <literal>Child</literal>.
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Si nous executions le code suivant
</para>
<programlisting><![CDATA[Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();]]></programlisting>
<para>
Hibernate exécuterait deux ordres SQL:
</para>
<itemizedlist>
<listitem>
<para>un <literal>INSERT</literal> pour créer l'enregistrement pour <literal>c</literal></para>
</listitem>
<listitem>
<para>
un <literal>UPDATE</literal> pour créer le lien de <literal>p</literal> vers
<literal>c</literal>
</para>
</listitem>
</itemizedlist>
<para>
Ceci est non seuleument inefficace, mais viole aussi toute contrainte <literal>NOT NULL</literal> sur
la colonne <literal>parent_id</literal>. Nous pouvons réparer la contrainte de nullité
en spécifiant <literal>not-null="true"</literal> dans le mapping de la collection :
</para>
<programlisting><![CDATA[<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Cependant ce n'est pas la solution recommandée.
</para>
<para>
La cause sous jacente à ce comportement est que le lien (la clé étrangère <literal>parent_id</literal>) de
<literal>p</literal> vers <literal>c</literal> n'est pas considérée comme faisant partie de l'état
de l'objet <literal>Child</literal> et n'est donc pas créé par l'<literal>INSERT</literal>.
La solution est donc que ce lien fasse partie du mapping de <literal>Child</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
<para>
(Nous avons aussi besoin d'ajouter la propriété <literal>parent</literal> dans la classe <literal>Child</literal>).
</para>
<para>
Maintenant que l'état du lien est géré par l'entité <literal>Child</literal>, nous spécifions à la
collection de ne pas mettre à jour le lien. Nous utilisons l'attribut <literal>inverse</literal>.
</para>
<programlisting><![CDATA[<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Le code suivant serait utilisé pour ajouter un nouveau <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>
Maintenant, seul un <literal>INSERT</literal> SQL est nécessaire !
</para>
<para>
Pour alléger encore un peu les choses, nous devrions créer une méthode <literal>addChild()</literal>
dans <literal>Parent</literal>.
</para>
<programlisting><![CDATA[public void addChild(Child c) {
c.setParent(this);
children.add(c);
}]]></programlisting>
<para>
Le code d'ajout d'un <literal>Child</literal> serait alors
</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>Cycle de vie en cascade</title>
<para>
L'appel explicite de <literal>save()</literal> est un peu fastidieux. Nous pouvons
simplifier cela en utilisant les cascades.
</para>
<programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>]]></programlisting>
<para>
Simplifie le code précédent en
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();]]></programlisting>
<para>
De la même manière, nous n'avons pas à itérer sur les fils lorsque nous sauvons
ou effacons un <literal>Parent</literal>. Le code suivant efface <literal>p</literal>
et tous ses fils de la base de données.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();]]></programlisting>
<para>
Par contre, ce 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>
n'effacera pas <literal>c</literal> de la base de données, il enlèvera seulement
le lien vers <literal>p</literal> (et causera une violation de contrainte
<literal>NOT NULL</literal>, dans ce cas).
Vous devez explicitement utiliser <literal>delete()</literal> sur <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>
Dans notre cas, un <literal>Child</literal> ne peut pas vraiment exister sans son père. Si nous
effacons un <literal>Child</literal> de la collection, nous voulons vraiment qu'il soit effacé.
Pour cela, nous devons utiliser <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>
A noter : même si le mapping de la collection spécifie <literal>inverse="true"</literal>, les cascades
sont toujours assurées par l'itération sur les éléments de la collection. Donc, si vous avez besoin
qu'un objet soit enregistré, effacé ou mis à jour par cascade, vous devez l'ajouter dans la colleciton.
Il ne suffit pas d'appeler explicitement <literal>setParent()</literal>.
</para>
</sect1>
<sect1 id="example-parentchild-update">
<title>Cascades et <literal>unsaved-value</literal></title>
<para>
Supposons que nous ayons chargé un <literal>Parent</literal> dans une <literal>Session</literal>,
que nous l'ayons ensuite modifié et que voulions persiter ces modifications dans une nouvelle session
en appelant <literal>update()</literal>.
Le <literal>Parent</literal> contiendra une collection de fils et, puisque la cascade est activée,
Hibernate a besoin de savoir quels fils viennent d'être instanciés et quels fils proviennent de la base
de données. Supposons aussi que <literal>Parent</literal> et <literal>Child</literal> ont tous deux
des identifiants du type <literal>Long</literal>.
Hibernate utilisera la propriété de l'identifiant et la propriété de la version/horodatage pour déterminer quels fils sont nouveaux
(vous pouvez aussi utiliser la propriété version ou timestamp, voir
<xref linkend="manipulatingdata-updating-detached"/>).
<emphasis>Dans Hibernate3, il n'est plus nécessaire de spécifier
une <literal>unsaved-value</literal> explicitement.</emphasis>
</para>
<para>
Le code suivant mettra à jour <literal>parent</literal> et <literal>child</literal>
et insérera <literal>newChild</literal>.
</para>
<programlisting><![CDATA[//parent et child ont été chargés dans une session précédente
parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
session.update(parent);
session.flush();]]></programlisting>
<para>
Ceci est très bien pour des identifiants générés, mais qu'en est-il des identifiants assignés et des
identifiants composés ? C'est plus difficile,
puisqu'Hibernate ne peut pas utiliser la propriété de l'identifiant pour distinguer un objet
nouvellement instancié (avec un identifiant assigné par l'utilisateur) d'un objet chargé dans une session précédente.
Dans ce cas, Hibernate utilisera soit la propriété de version ou d'horodatage, soit effectuera vraiment une requête au cache
de second niveau, soit, dans le pire des cas, à la base de données, pour voir si la ligne existe.
</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>
Il y a quelques principes à maîtriser dans ce chapitre et tout cela peut paraître déroutant la première fois.
Cependant, dans la pratique, tout fonctionne parfaitement. La plupart des applications Hibernate utilisent
le pattern père / fils.
</para>
<para>
Nous avons évoqué une alternative dans le premier paragraphe. Aucun des points traités précédemment n'existe
dans le cas d'un mapping <literal>&lt;composite-element&gt;</literal> qui possède exactement la sémantique
d'une relation père / fils. Malheureusement, il y a deux grandes limitations pour les classes éléments
composites : les éléments composites ne peuvent contenir de collections, et ils ne peuvent être les fils
d'entités autres que l'unique parent.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,432 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="example-weblog">
<title>Exemple : application Weblog</title>
<sect1 id="example-weblog-classes">
<title>Classes persistantes</title>
<para>
Les classes persistantes representent un weblog, et un article posté
dans un weblog. Il seront modélisés comme une relation père/fils
standard, mais nous allons utiliser un "bag" trié au lieu d'un 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>Mappings Hibernate</title>
<para>
Le mapping XML doit maintenant être relativement simple à vos yeux.
</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>Code Hibernate</title>
<para>
La classe suivante montre quelques utilisations que nous pouvons faire
de ces classes.
</para>
<programlisting><![CDATA[package eg;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class BlogMain {
private SessionFactory _sessions;
public void configure() throws HibernateException {
_sessions = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class)
.buildSessionFactory();
}
public void exportTables() throws HibernateException {
Configuration cfg = new Configuration()
.addClass(Blog.class)
.addClass(BlogItem.class);
new SchemaExport(cfg).create(true, true);
}
public Blog createBlog(String name) throws HibernateException {
Blog blog = new Blog();
blog.setName(name);
blog.setItems( new ArrayList() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.persist(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public BlogItem createBlogItem(Blog blog, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setBlog(blog);
item.setDatetime( Calendar.getInstance() );
blog.getItems().add(item);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(blog);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public BlogItem createBlogItem(Long blogid, String title, String text)
throws HibernateException {
BlogItem item = new BlogItem();
item.setTitle(title);
item.setText(text);
item.setDatetime( Calendar.getInstance() );
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Blog blog = (Blog) session.load(Blog.class, blogid);
item.setBlog(blog);
blog.getItems().add(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return item;
}
public void updateBlogItem(BlogItem item, String text)
throws HibernateException {
item.setText(text);
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.update(item);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public void updateBlogItem(Long itemid, String text)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
item.setText(text);
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
}
public List listAllBlogNamesAndItemCounts(int max)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"select blog.id, blog.name, count(blogItem) " +
"from Blog as blog " +
"left outer join blog.items as blogItem " +
"group by blog.name, blog.id " +
"order by max(blogItem.datetime)"
);
q.setMaxResults(max);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
public Blog getBlogAndAllItems(Long blogid)
throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
Blog blog = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"left outer join fetch blog.items " +
"where blog.id = :blogid"
);
q.setParameter("blogid", blogid);
blog = (Blog) q.uniqueResult();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return blog;
}
public List listBlogsAndRecentItems() throws HibernateException {
Session session = _sessions.openSession();
Transaction tx = null;
List result = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery(
"from Blog as blog " +
"inner join blog.items as blogItem " +
"where blogItem.datetime > :minDate"
);
Calendar cal = Calendar.getInstance();
cal.roll(Calendar.MONTH, false);
q.setCalendar("minDate", cal);
result = q.list();
tx.commit();
}
catch (HibernateException he) {
if (tx!=null) tx.rollback();
throw he;
}
finally {
session.close();
}
return result;
}
}]]></programlisting>
</sect1>
</chapter>

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="filters">
<title>Filtrer les données</title>
<para>
Hibernate3 fournit une nouvelle approche innovatrice pour gérer des données
avec des règles de "visibilité". Un <emphasis>filtre Hibernate</emphasis> est un filtre
global, nommé, paramétré qui peut être activé ou désactivé pour une session Hibernate
particulière.
</para>
<sect1 id="objectstate-filters">
<title>Filtres Hibernate</title>
<para>
Hibernate3 ajoute la capacité de prédéfinir des critères de filtre et d'attacher ces
filtres à une classe ou à une collection. Un critère de filtre est la faculté de définir
une clause de restriction très similaire à l'attribut "where" existant disponible sur
une classe et divers éléments d'une collection. Mis à part que ces conditions de filtre
peuvent être paramétrées. L'application peut alors prendre la décision à l'exécution
si des filtres donnés devraient être activés et quels devraient être leurs paramètres.
Des filtres peuvent être utilisés comme des vues de base de données, mais paramétrées
dans l'application.
</para>
<para>
Afin d'utiliser des filtres, ils doivent d'abord être définis, puis attachés aux éléments
de mapping appropriés. Pour définir un filtre, utilisez l'élément <literal>&lt;filter-def/&gt;</literal>
dans un élément <literal>&lt;hibernate-mapping/&gt;</literal> :
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>]]></programlisting>
<para>
Puis, ce filtre peut être attaché à une classe :
</para>
<programlisting><![CDATA[<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>]]></programlisting>
<para>
ou à une collection :
</para>
<programlisting><![CDATA[<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>]]></programlisting>
<para>
ou même aux deux (ou à plusieurs de chaque) en même temps.
</para>
<para>
Les méthodes sur <literal>Session</literal> sont : <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, et <literal>disableFilter(String filterName)</literal>.
Par défaut, les filtres <emphasis>ne sont pas</emphasis> activés pour une session donnée ;
ils doivent être explicitement activés en appelant la méthode
<literal>Session.enabledFilter()</literal>, laquelle retourne une instance de l'interface
<literal>Filter</literal>. Utiliser le simple filtre défini au-dessus ressemblerait à :
</para>
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
<para>
Notez que des méthodes sur l'interface org.hibernate.Filter autorisent le chaînage de beaucoup
de méthodes communes d'Hibernate.
</para>
<para>
Un exemple complet, utilisant des données temporelles avec une structure de date
d'enregistrement effectif :
</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>
Puis, afin de s'assurer que vous pouvez toujours récupérer les enregistrements actuellement
effectifs, activez simplement le filtre sur la session avant de récupérer des données des
employés :
</para>
<programlisting><![CDATA[Session session = ...;
session.enabledFilter("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>
Dans le HQL ci-dessus, bien que nous ayons seulement mentionné une contrainte de
salaire sur les resultats, à cause du filtre activé, la requête retournera seulement
les employés actuellement actifs qui ont un salaire supérieur à un million de dollars.
</para>
<para>
A noter : si vous prévoyez d'utiliser des filtres avec des jointures externes (soit
à travers HQL, soit par le chargement) faites attention à la direction de l'expression
de condition. Il est plus sûr de la positionner pour les jointures externes à gauche ;
en général, placez le paramètre d'abord, suivi du(des) nom(s) de colonne après l'opérateur.
</para>
</sect1>
</chapter>

View File

@ -0,0 +1,483 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="inheritance">
<title>Mapping d'héritage de classe</title>
<sect1 id="inheritance-strategies" revision="3">
<title>Les trois stratégies</title>
<para>
Hibernate supporte les trois stratégies d'héritage de base :
</para>
<itemizedlist>
<listitem>
<para>
une table par hiérarchie de classe (table per class hierarchy)
</para>
</listitem>
<listitem>
<para>
une table par classe fille (table per subclass)
</para>
</listitem>
<listitem>
<para>
une table par classe concrète (table per concrete class)
</para>
</listitem>
</itemizedlist>
<para>
Hibernate supporte en plus une quatrièmestratégie, légèrement différente, qui supporte le polymorphisme :
</para>
<itemizedlist>
<listitem>
<para>
le polymorphisme implicite
</para>
</listitem>
</itemizedlist>
<para>
Il est possible d'utiliser différentes stratégies de mapping pour différentes branches d'une même
hiérarchie d'héritage, et alors d'employer le polymorphisme implicite pour réaliser le
polymorphisme à travers toute la hiérarchie. Pourtant, Hibernate ne supporte pas de mélanger
des mappings <literal>&lt;subclass&gt;</literal> et
<literal>&lt;joined-subclass&gt;</literal> et <literal>&lt;union-subclass&gt;</literal>
pour le même élément <literal>&lt;class&gt;</literal> racine.
Il est possible de mélanger ensemble les stratégies d'une table par hiérarchie et d'une
table par sous-classe, pour le même élément <literal>&lt;class&gt;</literal>, en combinant
les éléments <literal>&lt;subclass&gt;</literal> et <literal>&lt;join&gt;</literal> (voir dessous).
</para>
<para>
Il est possible de définir des mappings de <literal>subclass</literal>, <literal>union-subclass</literal>,
et <literal>joined-subclass</literal> dans des documents de mapping séparés, directement sous
<literal>hibernate-mapping</literal>. Ceci vous permet d'étendre une hiérarchie de classe juste en
ajoutant un nouveau fichier de mapping. Vous devez spécifier un attribut <literal>extends</literal>
dans le mapping de la sous-classe, en nommant une super-classe précédemment mappée. Note :
précédemment cette foncionnalité rendait l'ordre des documents de mapping important. Depuis
Hibernate3, l'ordre des fichier de mapping n'importe plus lors de l'utilisation du mot-clef "extends".
L'ordre à l'intérieur d'un simple fichier de mapping impose encore de définir les classes mères
avant les classes filles.
</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>Une table par hiérarchie de classe</title>
<para>
Supposons que nous ayons une interface <literal>Payment</literal>, implémentée
par <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
<literal>ChequePayment</literal>. La stratégie une table par hiérarchie serait :
</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>
Une seule table est requise. Une grande limitation de cette
stratégie est que les colonnes déclarées par les classes filles, telles que <literal>CCTYPE</literal>,
ne peuvent avoir de contrainte <literal>NOT NULL</literal>.
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>Une table par classe fille</title>
<para>
La stratégie une table par classe fille serait :
</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>
Quatre tables sont requises. Les trois tables des classes filles ont
une clé primaire associée à la table classe mère (le modèle relationnel
est une association un-vers-un).
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
<title>Une table par classe fille, en utilisant un discriminant</title>
<para>
Notez que l'implémentation Hibernate de la stratégie un table par
classe fille ne nécessite pas de colonne discriminante dans la table
classe mère. D'autres implémentations de mappers Objet/Relationnel utilisent
une autre implémentation de la stratégie une table par classe fille qui nécessite
une colonne de type discriminant dans la table de la classe mère. L'approche
prise par Hibernate est plus difficile à implémenter mais plus correcte
d'une point de vue relationnel. Si vous aimeriez utiliser
une colonne discriminante avec la stratégie d'une table par classe fille, vous pourriez combiner
l'utilisation de <literal>&lt;subclass&gt;</literal> et
<literal>&lt;join&gt;</literal>, comme suit :
</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>
La déclaration optionnelle <literal>fetch="select"</literal> indique à Hibernate
de ne pas récupérer les données de la classe fille <literal>ChequePayment</literal> par une jointure externe lors des requêtes sur la classe mère.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>Mélange d'une table par hiérarchie de classe avec une table par classe fille</title>
<para>
Vous pouvez même mélanger les stratégies d'une table par hiérarchie de classe et d'une table par classe fille en utilisant cette approche :
</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>
Pour importe laquelle de ces stratégies, une association polymorphique vers la classe racine
<literal>Payment</literal> est mappée en utilisant <literal>&lt;many-to-one&gt;</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="2">
<title>Une table par classe concrète</title>
<para>
Il y a deux manières d'utiliser la stratégie d'une table par classe concrète. La première
est d'employer <literal>&lt;union-subclass&gt;</literal>.
</para>
<programlisting><![CDATA[<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>]]></programlisting>
<para>
Trois tables sont nécessaires pour les classes filles. Chaque table définit des colonnes
pour toutes les propriétés de la classe, incluant les propriétés héritéés.
</para>
<para>
La limitation de cette approche est que si une propriété est mappée sur la classe mère, le nom
de la colonne doit être le même pour toutes les classes filles. (Nous pourrions être plus souple
dans une future version d'Hibernate).
La stratégie du générateur d'identifiant n'est pas permise dans l'héritage de classes filles par
union, en effet la valeur (NdT : seed) de la clef primaire
doit être partagée par toutes les classes filles "union" d'une hiérarchie.
</para>
<para>
Si votre classe mère est abstraite, mappez la avec <literal>abstract="true"</literal>.
Bien sûr, si elle n'est pas abstraite, une table supplémentaire (par défaut,
<literal>PAYMENT</literal> dans l'exemple ci-dessus) est requise pour contenir des instances
de la classe mère.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Une table par classe concrète, en utilisant le polymorphisme implicite</title>
<para>
Une approche alternative est l'emploi du polymorphisme implicite :
</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>
Notez que nulle part nous ne mentionnons l'interface <literal>Payment</literal> explicitement.
Notez aussi que des propriétés de <literal>Payment</literal> sont mappées dans
chaque classe fille. Si vous voulez éviter des duplications, considérez l'utilisation des
entités XML (cf. <literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
dans la déclaration du <literal>DOCTYPE</literal> et <literal>&amp;allproperties;</literal> dans le mapping).
</para>
<para>
L'inconvénient de cette approche est qu'Hibernate ne génère pas d'<literal>UNION</literal>s SQL
lors de l'exécution des requêtes polymorphiques.
</para>
<para>
Pour cette stratégie de mapping, une association polymorphique pour <literal>Payment</literal>
est habituellement mappée en utilisant <literal>&lt;any&gt;</literal>.
</para>
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
<meta-value value="CREDIT" class="CreditCardPayment"/>
<meta-value value="CASH" class="CashPayment"/>
<meta-value value="CHEQUE" class="ChequePayment"/>
<column name="PAYMENT_CLASS"/>
<column name="PAYMENT_ID"/>
</any>]]></programlisting>
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>Mélange du polymorphisme implicite avec d'autres mappings d'héritage</title>
<para>
Il y a une chose supplémentaire à noter à propos de ce mapping. Puisque les classes filles sont
chacune mappées avec leur propre élément <literal>&lt;class&gt;</literal> (et puisque
<literal>Payment</literal> est juste une interface), chaque classe fille pourrait
facilement faire partie d'une autre hiérarchie
d'héritage ! (Et vous pouvez encore faire des requêtes polymorphiques pour l'interface <literal>Payment</literal>).
</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>
Encore une fois, nous ne mentionnons pas explicitement <literal>Payment</literal>.
Si nous exécutons une requête sur l'interface <literal>Payment</literal> - par
exemple, <literal>from Payment</literal> - Hibernate retournera
automatiquement les instances de <literal>CreditCardPayment</literal>
(et ses classes filles puisqu'elles implémentent aussi <literal>Payment</literal>),
<literal>CashPayment</literal> et <literal>ChequePayment</literal> mais pas
les instances de <literal>NonelectronicTransaction</literal>.
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>Limitations</title>
<para>
Il y a certaines limitations à l'approche du "polymorphisme implicite"
pour la stratégie de mapping d'une table par classe concrète.
Il y a plutôt moins de limitations restrictives aux mappings <literal>&lt;union-subclass&gt;</literal>.
</para>
<para>
La table suivante montre les limitations des mappings d'une table par classe concrète, et du polymorphisme implicite, dans Hibernate.
</para>
<table frame="topbot">
<title>Caractéristiques du mapping d'héritage</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>Stratégie d'héritage</entry>
<entry>many-to-one polymorphique</entry>
<entry>one-to-one polymorphique</entry>
<entry>one-to-many polymorphique</entry>
<entry>many-to-many polymorphique</entry>
<entry><literal>load()/get()</literal> polymorphique</entry>
<entry>Requêtes polymorphiques</entry>
<entry>Jointures polymorphiques</entry>
<entry>Récupération par jointure externe</entry>
</row>
</thead>
<tbody>
<row>
<entry>une table par hiérarchie de classe</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supportée</emphasis></entry>
</row>
<row>
<entry>une table par classe fille</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supportée</emphasis></entry>
</row>
<row>
<entry>une table par classe concrète (union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (pour <literal>inverse="true"</literal> seulement)</entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supportée</emphasis></entry>
</row>
<row>
<entry>une table par classe concrète (polymorphisme implicite)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>non supporté</emphasis></entry>
<entry><emphasis>non supporté</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>non supportées</emphasis></entry>
<entry><emphasis>non supportée</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,536 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<chapter id="persistent-classes" revision="2">
<title>Classes persistantes</title>
<para>
Les classes persistantes sont les classes d'une application qui implémentent
les entités d'un problème métier (ex. Client et Commande dans une application
de commerce électronique).
Toutes les instances d'une classe persistante ne sont pas forcément
dans l'état persistant - au lieu de cela, une instance peut être éphémère (NdT : transient) ou détachée.
</para>
<para>
Hibernate fonctionne de manière optimale lorsque ces classes suivent quelques règles
simples, aussi connues comme le modèle de programmation Plain Old Java Object
(POJO). Cependant, aucune de ces règles ne sont des besoins absolus. En effet, Hibernate3 suppose très peu de choses à propos
de la nature de vos objets persistants. Vous pouvez exprimer un modèle de domaine par d'autres moyens : utiliser des arbres
d'instances de <literal>Map</literal>, par exemple.
</para>
<sect1 id="persistent-classes-pojo">
<title>Un exemple simple de POJO</title>
<para>
Toute bonne application Java nécessite une classe persistante
représentant les félins.
</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>
Il y a quatre règles à suivre ici :
</para>
<sect2 id="persistent-classes-pojo-constructor" revision="1">
<title>Implémenter un constructeur sans argument</title>
<para>
<literal>Cat</literal> a un constructeur sans argument. Toutes les classes persistantes doivent avoir un
constructeur par défaut (lequel peut ne pas être public) pour qu'Hibernate puissent les instancier en utilisant
<literal>Constructor.newInstance()</literal>. Nous recommandons fortement d'avoir un constructeur par défaut avec
au moins une visibilité <emphasis>paquet</emphasis> pour la génération du proxy à l'exécution dans Hibernate.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-identifier" revision="2">
<title>Fournir une propriété d'indentifiant (optionnel)</title>
<para>
<literal>Cat</literal> possède une propriété appelée <literal>id</literal>.
Cette propriété mappe la valeur de la colonne de clé primaire de la table
d'une base de données.La propriété aurait pu s'appeler complètement autrement,
et son type aurait pu être n'importe quel type primitif, n'importe quel "encapsuleur"
de type primitif, <literal>java.lang.String</literal> ou <literal>java.util.Date</literal>.
(Si votre base de données héritée possède des clés composites, elles peuvent être mappées
en utilisant une classe définie par l'utilisateur et possédant les propriétés associées aux
types de la clé composite - voir la section concernant les identifiants composites plus tard).
</para>
<para>
La propriété d'identifiant est strictement optionnelle. Vous pouver l'oublier et laisser Hibernate
s'occuper des identifiants de l'objet en interne. Toutefois, nous ne le recommandons pas.
</para>
<para>
En fait, quelques fonctionnalités ne sont disponibles que pour les classes
déclarant un identifiant de propriété :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Les réattachements transitifs pour les objets détachés (mise à jour en cascade ou fusion en cascade) -
voir <xref linkend="objectstate-transitive"/>
</para>
</listitem>
<listitem>
<para>
<literal>Session.saveOrUpdate()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>Session.merge()</literal>
</para>
</listitem>
</itemizedlist>
<para>
Nous recommandons que vous déclariez les propriétés d'identifiant de manière
uniforme. Nous recommandons également que vous utilisiez un type nullable
(ie. non primitif).
</para>
</sect2>
<sect2 id="persistent-classes-pojo-final">
<title>Favoriser les classes non finales (optionnel)</title>
<para>
Une fonctionnalité clef d'Hibernate, les <emphasis>proxies</emphasis>, nécessitent
que la classe persistente soit non finale ou qu'elle soit l'implémentation d'une
interface qui déclare toutes les méthodes publiques.
</para>
<para>
Vous pouvez persister, grâce à Hibernate, les classes <literal>final</literal>
qui n'implémentent pas d'interface, mais vous ne pourrez pas utiliser les proxies pour les chargements d'associations paresseuses
- ce qui limitera vos possibilités d'ajustement des performances.
</para>
<para>
Vous devriez aussi éviter de déclarer des méthodes <literal>public final</literal> sur des classes
non-finales. Si vous voulez utiliser une classe avec une méthode <literal>public final</literal>, vous devez
explicitement désactiver les proxies en paramétrant
<literal>lazy="false"</literal>.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-accessors" revision="2">
<title>Déclarer les accesseurs et mutateurs des attributs persistants (optionnel)</title>
<para>
<literal>Cat</literal> déclare des mutateurs pour toutes ses champs persistants. Beaucoup d'autres
solutions de mapping Objet/relationnel persistent directement les variables d'instance. Nous pensons
qu'il est bien mieux de fournir une indirection entre le schéma relationnel et les structures de données internes de la classe.
Par défaut, Hibernate persiste les propriétés suivant le style JavaBean, et reconnaît les noms de méthodes de la forme <literal>
getFoo</literal>, <literal>isFoo</literal> et
<literal>setFoo</literal>. Nous pouvons changer pour un accès direct aux champs pour des propriétés particulières, si besoin est.
</para>
<para>
Les propriétés <emphasis>n'ont pas</emphasis> à être déclarées publiques -
Hibernate peut persister une propriété avec un paire de getter/setter de
visibilité par défault, <literal>protected</literal> ou <literal>
private</literal>.
</para>
</sect2>
</sect1>
<sect1 id="persistent-classes-inheritance">
<title>Implémenter l'héritage</title>
<para>
Une sous-classe doit également suivre la première et la seconde règle.
Elle hérite sa propriété d'identifiant de <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>Implémenter <literal>equals()</literal> et <literal>hashCode()</literal></title>
<para>
Vous devez surcharger les méthodes <literal>equals()</literal> et
<literal>hashCode()</literal> si vous
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
avez l'intention de mettre des instances de classes persistantes dans un <literal>Set</literal>
(la manière recommandée pour représenter des associations pluri-valuées)
<emphasis>et</emphasis>
</para>
</listitem>
<listitem>
<para>
avez l'intention d'utiliser le réattachement d'instances détachées
</para>
</listitem>
</itemizedlist>
<para>
Hibernate garantit l'équivalence de l'identité persistante (ligne de base de données) et l'identité Java seulement
à l'intérieur de la portée d'une session particulière. Donc dès que nous mélangeons des instances venant de différentes
sessions, nous devons implémenter <literal>equals()</literal> et
<literal>hashCode()</literal> si nous souhaitons avoir une sémantique correcte pour les <literal>Set</literal>s.
</para>
<para>
La manière la plus évidente est d'implémenter <literal>equals()</literal>/<literal>hashCode()</literal>
en comparant la valeur de l'identifiant des deux objets. Si cette valeur est identique, les deux
doivent représenter la même ligne de base de données, ils sont donc égaux (si les deux sont
ajoutés à un <literal>Set</literal>, nous n'aurons qu'un seul élément dans le
<literal>Set</literal>). Malheureusement, nous ne pouvons pas utiliser cette approche avec
des identifiants générés ! Hibernate n'assignera de
valeur d'identifiant qu'aux objets qui sont persistants, une instance nouvellement créée n'aura
donc pas de valeur d'identifiant ! De plus, si une instance est non sauvegardée et actuellement dans un <literal>Set</literal>,
le sauvegarder assignera une valeur d'identifiant à l'objet. Si <literal>equals()</literal> et <literal>hashCode()</literal>
sont basées sur la valeur de l'identifiant, le code de hachage devrait changer, rompant le contrat du <literal>Set</literal>.
Regardez sur le site web d'Hibernate pour une discussion complète de ce problème.
Notez que ceci n'est pas un problème d'Hibernate, mais la sémantique normale de Java pour l'identité d'un objet et l'égalité.
</para>
<para>
Nous recommandons donc d'implémenter
<literal>equals()</literal> et <literal>hashCode()</literal> en utilisant <emphasis>
l'égalité par clé métier</emphasis>.L'égalité par clé métier signifie que la méthode <literal>equals()</literal>
compare uniquement les propriétés qui forment une clé métier, une clé qui
identifierait notre instance dans le monde réel (une clé candidate
<emphasis>naturelle</emphasis>) :
</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>
Notez qu'une clef métier ne doit pas être solide comme une clef primaire de base de données
(voir <xref linkend="transactions-basics-identity"/>). Les propriétés
immuables ou uniques sont généralement de bonnes candidates pour une clef métier.
</para>
</sect1>
<sect1 id="persistent-classes-dynamicmodels">
<title>Modèles dynamiques</title>
<para>
<emphasis>Notez que la fonctionnalités suivantes sont actuellement considérées
comme expérimentales et peuvent changer dans un futur proche.</emphasis>
</para>
<para>
Les entités persistantes ne doivent pas nécessairement être représentées comme
des classes POJO ou des objets JavaBean à l'exécution. Hibernate supporte aussi les
modèles dynamiques (en utilisant des <literal>Map</literal>s de <literal>Map</literal>s
à l'exécution) et la représentation des entités comme des arbres DOM4J. Avec cette
approche, vous n'écrivez pas de classes persistantes, seulement des fichiers de mapping.
</para>
<para>
Par défaut, Hibernate fonctionne en mode POJO normal. Vous pouvez paramétrer
un mode de représentation d'entité par défaut pour une <literal>SessionFactory</literal>
particulière en utilisant l'option de configuration <literal>default_entity_mode</literal>
(voir <xref linkend="configuration-optional-properties"/>).
</para>
<para>
Les exemples suivants démontrent la représentation utilisant des <literal>Map</literal>s.
D'abord, dans le fichier de mapping, un <literal>entity-name</literal> doit être déclaré
au lieu (ou en plus) d'un nom de classe :
</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>
Notez que même si des associations sont déclarées en utilisant des noms de classe cible,
le type de cible d'une association peut aussi être une entité dynamique au lieu d'un POJO.
</para>
<para>
Après avoir configuré le mode d'entité par défaut à <literal>dynamic-map</literal>
pour la <literal>SessionFactory</literal>, nous pouvons lors de l'exécution fonctionner
avec des <literal>Map</literal>s de <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>
Les avantages d'un mapping dynamique sont un gain de temps pour le prototypage
sans la nécessité d'implémenter les classes d'entité. Pourtant, vous perdez la
vérification du typage au moment de la compilation et aurez plus d'exceptions à
gérer lors de l'exécution. Grâce au mapping d'Hibernate, le schéma de la base de
données peut facilement être normalisé et solidifié, permettant de rajouter une
implémentation propre du modèle de domaine plus tard.
</para>
<para>
Les modes de représentation d'une entité peut aussi être configuré par <literal>Session</literal> :
</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>
Veuillez noter que l'appel à <literal>getSession()</literal> en utilisant un
<literal>EntityMode</literal> se fait sur l'API <literal>Session</literal>, pas
<literal>SessionFactory</literal>. De cette manière, la nouvelle <literal>Session</literal>
partage les connexions JDBC, transactions et autres informations de contexte sous-jacentes.
Cela signifie que vous n'avez pas à appeler <literal>flush()</literal> et <literal>close()</literal>
sur la <literal>Session</literal> secondaire, et laissez aussi la gestion de la transaction
et de la connexion à l'unité de travail primaire.
</para>
<para>
Plus d'informations à propos de la représentation XML peuvent être trouvées dans
<xref linkend="xml"/>.
</para>
</sect1>
<sect1 id="persistent-classes-tuplizers" revision="0">
<title>Tuplizers</title>
<para>
<literal>org.hibernate.tuple.Tuplizer</literal>, et ses sous-interfaces, sont responsables
de la gestion d'une représentation particulière d'un morceau de données, en fonction du
<literal>org.hibernate.EntityMode</literal> de réprésentation. Si un morceau donné de données
est pensé comme une structure de données, alors un tuplizer est la chose qui sait comment
créer une telle structure de données, comment extraire des valeurs et injecter des valeurs dans
une telle structure de données. Par exemple, pour le mode d'entité POJO, le tuplizer correspondant
sait comment créer le POJO à travers son constructeur et comment accéder aux propriétés du POJO
utilisant les accesseurs de la propriété définie. Il y a deux types de Tuplizers haut niveau,
représenté par les interfaces <literal>org.hibernate.tuple.EntityTuplizer</literal> et
<literal>org.hibernate.tuple.ComponentTuplizer</literal>. Les <literal>EntityTuplizer</literal>s
sont responsables de la gestion des contrats mentionnés ci-dessus pour les entités, alors que
les <literal>ComponentTuplizer</literal>s s'occupent des composants.
</para>
<para>
Les utilisateurs peuvent aussi brancher leurs propres tuplizers. Peut-être vous est-il nécessaire qu'une
implémentation de <literal>java.util.Map</literal> autre que <literal>java.util.HashMap</literal>
soit utilisée dans le mode d'entité dynamic-map ; ou peut-être avez-vous besoin de définir une
statégie de génération de proxy différente de celle utilisée par défaut. Les deux devraient être
effectuées en définissant une implémentation de tuplizer utilisateur. Les définitions de tuplizers
sont attachées au mapping de l'entité ou du composant qu'ils sont censés gérer. Retour à l'exemple de
notre entité utilisateur :
</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.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>
<para>
TODO: Document user-extension framework in the property and proxy packages
</para>
</chapter>

Some files were not shown because too many files have changed in this diff Show More