Match to latest English XML
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14153 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
9817470c54
commit
9fdf63d3cb
|
@ -332,6 +332,45 @@ create table Person ( personId bigint not null primary key, addressId bigint not
|
|||
create table Address ( addressId bigint not null primary key )
|
||||
]]></programlisting>
|
||||
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
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>UNTRANSLATED!
|
||||
It is important that you define <literal>not-null="true"</literal> on the
|
||||
<literal><key></literal> element of the collection mapping if the
|
||||
underlying foreign key column is <literal>NOT NULL</literal>. Don't only
|
||||
declare <literal>not-null="true"</literal> on a possible nested
|
||||
<literal><column></literal> element, but on the <literal><key></literal>
|
||||
element.
|
||||
</para>
|
||||
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-bidirectional-121">
|
||||
|
|
|
@ -111,7 +111,59 @@
|
|||
búsquedas del DTD usando una conexión de Internet, chequea tu declaración
|
||||
de DTD contra la contenida en el classpath.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect3 id="mapping-declaration-entity-resolution">
|
||||
<title>UNTRANSLATED! EntityResolver</title>
|
||||
<para>
|
||||
As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The
|
||||
manner in which it does this is by registering a custom <literal>org.xml.sax.EntityResolver</literal>
|
||||
implementation with the SAXReader it uses to read in the xml files. This custom
|
||||
<literal>EntityResolver</literal> recognizes two different systemId namespaces.
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
a <literal>hibernate namespace</literal> is recognized whenever the
|
||||
resolver encounteres a systemId starting with
|
||||
<literal>http://hibernate.sourceforge.net/</literal>; the resolver
|
||||
attempts to resolve these entities via the classlaoder which loaded
|
||||
the Hibernate classes.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
a <literal>user namespace</literal> is recognized whenever the
|
||||
resolver encounteres a systemId using a <literal>classpath://</literal>
|
||||
URL protocol; the resolver will attempt to resolve these entities
|
||||
via (1) the current thread context classloader and (2) the
|
||||
classloader which loaded the Hibernate classes.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
An example of utilizing user namespacing:
|
||||
</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" [
|
||||
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
|
||||
]>
|
||||
|
||||
<hibernate-mapping package="your.domain">
|
||||
<class name="MyEntity">
|
||||
<id name="id" type="my-custom-id-type">
|
||||
...
|
||||
</id>
|
||||
<class>
|
||||
&types;
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
<para>
|
||||
Where <literal>types.xml</literal> is a resource in the <literal>your.domain</literal>
|
||||
package and contains a custom <link linkend="mapping-types-custom">typedef</link>.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="mapping-declaration-mapping" revision="3">
|
||||
<title>hibernate-mapping</title>
|
||||
|
@ -758,6 +810,21 @@
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>UNTRANSLATED! sequence-identity</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
a specialized sequence generation strategy which utilizes a
|
||||
database sequence for the actual value generation, but combines
|
||||
this with JDBC3 getGeneratedKeys to actually return the generated
|
||||
identifier value as part of the insert statement execution. This
|
||||
strategy is only known to be supported on Oracle 10g drivers
|
||||
targetted for JDK 1.4. Note comments on these insert statements
|
||||
are disabled due to a bug in the Oracle drivers.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
@ -877,6 +944,172 @@
|
|||
</sect3>
|
||||
|
||||
</sect2>
|
||||
<sect2 id="mapping-declaration-id-enhanced">
|
||||
<title>Enhanced identifier generators</title>
|
||||
|
||||
<para>
|
||||
Starting with release 3.2.3, there are 2 new generators which represent a re-thinking of 2 different
|
||||
aspects of identifier generation. The first aspect is database portability; the second is optimization
|
||||
(not having to query the database for every request for a new identifier value). These two new
|
||||
generators are intended to take the place of some of the named generators described above (starting
|
||||
in 3.3.x); however, they are included in the current releases and can be referenced by FQN.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The first of these new generators is <literal>org.hibernate.id.enhanced.SequenceStyleGenerator</literal>
|
||||
which is intended firstly as a replacement for the <literal>sequence</literal> generator and secondly as
|
||||
a better portability generator than <literal>native</literal> (because <literal>native</literal>
|
||||
(generally) chooses between <literal>identity</literal> and <literal>sequence</literal> which have
|
||||
largely different semantics which can cause subtle isssues in applications eyeing portability).
|
||||
<literal>org.hibernate.id.enhanced.SequenceStyleGenerator</literal> however achieves portability in
|
||||
a different manner. It chooses between using a table or a sequence in the database to store its
|
||||
incrementing values depending on the capabilities of the dialect being used. The difference between this
|
||||
and <literal>native</literal> is that table-based and sequence-based storage have the same exact
|
||||
semantic (in fact sequences are exactly what Hibernate tries to emmulate with its table-based
|
||||
generators). This generator has a number of configuration parameters:
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>sequence_name</literal> (optional, defaults to <literal>hibernate_sequence</literal>):
|
||||
The name of the sequence (or table) to be used.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>initial_value</literal> (optional, defaults to <literal>1</literal>): The initial
|
||||
value to be retrieved from the sequence/table. In sequence creation terms, this is analogous
|
||||
to the clause typical named "STARTS WITH".
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>increment_size</literal> (optional, defaults to <literal>1</literal>): The value by
|
||||
which subsequent calls to the sequence/table should differ. In sequence creation terms, this
|
||||
is analogous to the clause typical named "INCREMENT BY".
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>force_table_use</literal> (optional, defaults to <literal>false</literal>): Should
|
||||
we force the use of a table as the backing structure even though the dialect might support
|
||||
sequence?
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>value_column</literal> (optional, defaults to <literal>next_val</literal>): Only
|
||||
relevant for table structures! The name of the column on the table which is used to
|
||||
hold the value.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>optimizer</literal> (optional, defaults to <literal>none</literal>):
|
||||
See <xref linkend="mapping-declaration-id-enhanced-optimizers"/>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
The second of these new generators is <literal>org.hibernate.id.enhanced.TableGenerator</literal> which
|
||||
is intended firstly as a replacement for the <literal>table</literal> generator (although it actually
|
||||
functions much more like <literal>org.hibernate.id.MultipleHiLoPerTableGenerator</literal>) and secondly
|
||||
as a re-implementation of <literal>org.hibernate.id.MultipleHiLoPerTableGenerator</literal> utilizing the
|
||||
notion of pluggable optimiziers. Essentially this generator defines a table capable of holding
|
||||
a number of different increment values simultaneously by using multiple distinctly keyed rows. This
|
||||
generator has a number of configuration parameters:
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>table_name</literal> (optional, defaults to <literal>hibernate_sequences</literal>):
|
||||
The name of the table to be used.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>value_column_name</literal> (optional, defaults to <literal>next_val</literal>):
|
||||
The name of the column on the table which is used to hold the value.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>segment_column_name</literal> (optional, defaults to <literal>sequence_name</literal>):
|
||||
The name of the column on the table which is used to hold the "segement key". This is the
|
||||
value which distinctly identifies which increment value to use.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>segment_value</literal> (optional, defaults to <literal>default</literal>):
|
||||
The "segment key" value for the segment from which we want to pull increment values for
|
||||
this generator.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>segment_value_length</literal> (optional, defaults to <literal>255</literal>):
|
||||
Used for schema generation; the column size to create this segment key column.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>initial_value</literal> (optional, defaults to <literal>1</literal>):
|
||||
The initial value to be retrieved from the table.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>increment_size</literal> (optional, defaults to <literal>1</literal>):
|
||||
The value by which subsequent calls to the table should differ.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>optimizer</literal> (optional, defaults to <literal></literal>):
|
||||
See <xref linkend="mapping-declaration-id-enhanced-optimizers"/>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="mapping-declaration-id-enhanced-optimizers">
|
||||
<title>Identifier generator optimization</title>
|
||||
<para>
|
||||
For identifier generators which store values in the database, it is inefficient for them to hit the
|
||||
database on each and every call to generate a new identifier value. Instead, you'd ideally want to
|
||||
group a bunch of them in memory and only hit the database when you have exhausted your in-memory
|
||||
value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators
|
||||
(<xref linkend="mapping-declaration-id-enhanced"/> support this notion.
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>none</literal> (generally this is the default if no optimizer was specified): This
|
||||
says to not perform any optimizations, and hit the database each and every request.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>hilo</literal>: applies a hi/lo algorithm around the database retrieved values. The
|
||||
values from the database for this optimizer are expected to be sequential. The values
|
||||
retrieved from the database structure for this optimizer indicates the "group number"; the
|
||||
<literal>increment_size</literal> is multiplied by that value in memory to define a group
|
||||
"hi value".
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>pooled</literal>: like was discussed for <literal>hilo</literal>, this optimizers
|
||||
attempts to minimize the number of hits to the database. Here, however, we simply store
|
||||
the starting value for the "next group" into the database structure rather than a sequential
|
||||
value in combination with an in-memory grouping algorithm. <literal>increment_size</literal>
|
||||
here refers to the values coming from the database.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="mapping-declaration-compositeid" revision="2">
|
||||
<title>composite-id</title>
|
||||
|
@ -921,12 +1154,64 @@
|
|||
Los atributos descriptos debajo solamente se aplican a este enfoque alternativo:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A second approach is what we call a <emphasis>mapped</emphasis> composite identifier,
|
||||
where the identifier properties named inside the <literal><composite-id></literal>
|
||||
element are duplicated on both the persistent class and a separate identifier class.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
|
||||
<key-property name="medicareNumber"/>
|
||||
<key-property name="dependent"/>
|
||||
</composite-id>]]></programlisting>
|
||||
|
||||
<para>
|
||||
In this example, both the composite identifier class, <literal>MedicareId</literal>,
|
||||
and the entity class itself have properties named <literal>medicareNumber</literal>
|
||||
and <literal>dependent</literal>. The identifier class must override
|
||||
<literal>equals()</literal> and <literal>hashCode()</literal> and implement.
|
||||
<literal>Serializable</literal>. The disadvantage of this approach is quite
|
||||
obvious—code duplication.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following attributes are used to specify a mapped composite identifier:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>mapped</literal> (optional, defaults to <literal>false</literal>):
|
||||
indicates that a mapped composite identifier is used, and that the contained
|
||||
property mappings refer to both the entity class and the composite identifier
|
||||
class.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>class</literal> (optional, but required for a mapped composite identifier):
|
||||
The class used as a composite identifier.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
We will describe a third, even more convenient approach where the composite identifier
|
||||
is implemented as a component class in <xref linkend="components-compositeid"/>. The
|
||||
attributes described below apply only to this alternative approach:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>name</literal> (opcional): Una propiedad de tipo componente que tiene el identificador
|
||||
compuesto (ver siguiente sección).
|
||||
</para>
|
||||
</listitem> <listitem>
|
||||
<para>
|
||||
<literal>access</literal> (optional - defaults to <literal>property</literal>):
|
||||
The strategy Hibernate should use for accessing the property value.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
|
@ -934,20 +1219,12 @@
|
|||
por reflección): La clase del componente usado como identificador compuesto (ver siguiente sección).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>unsaved-value</literal> (opcional - por defecto a <literal>undefined</literal>):
|
||||
Indica que las instancias transitorias deben ser consideradas como recién instanciadas,
|
||||
si se establece a <literal>any</literal>, o separadas, si se establece a <literal>none</literal>.
|
||||
Lo mejor
|
||||
|
||||
Indicates that transient instances should be considered newly instantiated, if set
|
||||
to <literal>any</literal>, or detached, if set to <literal>none</literal>.
|
||||
Lo mejor en todos los casos es dejar el valor por defecto.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
This third approach, an <emphasis>identifier component</emphasis> is the one we recommend
|
||||
for almost all applications.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="mapping-declaration-discriminator" revision="3">
|
||||
|
@ -1099,6 +1376,21 @@
|
|||
propiedad identificadora.)
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="version6">
|
||||
<para> UNTRANSLATED!
|
||||
<literal>generated</literal> (optional - defaults to <literal>never</literal>):
|
||||
Specifies that this version property value is actually generated by the database.
|
||||
See the discussion of <link linkend="mapping-generated">generated properties</link>.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="version7">
|
||||
<para>UNTRANSLATED!
|
||||
<literal>insert</literal> (optional - defaults to <literal>true</literal>):
|
||||
Specifies whether the version column should be included in SQL insert statements.
|
||||
May be set to <literal>false</literal> if and only if the database column
|
||||
is defined with a default value of <literal>0</literal>.
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
||||
|
@ -1172,7 +1464,26 @@
|
|||
(<literal>undefined</literal> especifica que debe usarse el valor de la propiedad
|
||||
identificadora.)
|
||||
</para>
|
||||
</callout>
|
||||
</callout> <callout arearefs="timestamp5">
|
||||
<para> UNTRANSLATED!
|
||||
<literal>source</literal> (optional - defaults to <literal>vm</literal>):
|
||||
From where should Hibernate retrieve the timestamp value? From the database,
|
||||
or from the current JVM? Database-based timestamps incur an overhead because
|
||||
Hibernate must hit the database in order to determine the "next value",
|
||||
but will be safer for use in clustered environments. Note also, that not
|
||||
all <literal>Dialect</literal>s are known to support retrieving of the
|
||||
database's current timestamp, while others might be unsafe for usage
|
||||
in locking due to lack of precision (Oracle 8 for example).
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="timestamp6">
|
||||
<para> UNTRANSLATED!
|
||||
<literal>generated</literal> (optional - defaults to <literal>never</literal>):
|
||||
Specifies that this timestamp property value is actually generated by the database.
|
||||
See the discussion of <link linkend="mapping-generated">generated properties</link>.
|
||||
</para>
|
||||
</callout>
|
||||
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
||||
|
@ -1295,6 +1606,12 @@
|
|||
de un bloqueo optimista. En otras palabras, determina si debe ocurrir un incremento de versión
|
||||
cuando la propiedad este sucia (desactualizada).
|
||||
</para>
|
||||
</callout> <callout arearefs="property12">
|
||||
<para> UNTRANSLATED!
|
||||
<literal>generated</literal> (optional - defaults to <literal>never</literal>):
|
||||
Specifies that this property value is actually generated by the database.
|
||||
See the discussion of <link linkend="mapping-generated">generated properties</link>.
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
@ -1580,6 +1897,9 @@
|
|||
Si la clave única referenciada abarca múltiples propiedades de la entidad asociada,
|
||||
debes mapear las propiedades dentro de un elemento <literal><properties></literal>.
|
||||
</para>
|
||||
<para>UNTRANSLATED!
|
||||
If the referenced unique key is the property of a component, you may specify a property path:
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
@ -1686,6 +2006,10 @@
|
|||
la aplicación de proxies es imposible e Hibernate traerá temprano la
|
||||
asociación!</emphasis>
|
||||
</para>
|
||||
</callout> <callout arearefs="onetoone10">
|
||||
<para>
|
||||
<literal>entity-name</literal> (optional): The entity name of the associated class.
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
@ -2075,33 +2399,13 @@
|
|||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
||||
<para>
|
||||
Cada subclase debe declarar sus propias propiedades persistentes y subclases.
|
||||
Se asume que las propiedades <literal><version></literal> y <literal><id></literal>
|
||||
son heredadas de la clase raíz. Cada subclase en una jerarquía debe
|
||||
definir un <literal>discriminator-value</literal> único. Si no se especifica ninguno,
|
||||
se usa el nombre completamente cualificado de clase Java.
|
||||
<para> UNTRANSLATED!
|
||||
Each subclass should declare its own persistent properties and subclasses.
|
||||
<literal><version></literal> and <literal><id></literal> properties
|
||||
are assumed to be inherited from the root class. Each subclass in a heirarchy must
|
||||
define a unique <literal>discriminator-value</literal>. If none is specified, the
|
||||
fully qualified Java class name is used.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Es posible definir mapeos <literal>subclass</literal>, <literal>union-subclass</literal>,
|
||||
y <literal>joined-subclass</literal> en documentos de mapeo separados, directamente debajo
|
||||
de <literal>hibernate-mapping</literal>. Esto te permite extender una jerarquía de clases
|
||||
con sólo agregar un nuevo fichero de mapeo. Debes especificar un atributo
|
||||
<literal>extends</literal> en el mapeo de la subclase, mencionando una superclase previamente mapeada.
|
||||
Nota: Previamente esta funcionalidad hacía importante el orden de los documentos de mapeo.
|
||||
Desde Hibernate3, el orden de los ficheros de mapeo no importa cuando se usa la palabra reservada
|
||||
extends. El orden dentro de un mismo fichero de mapeo todavía necesita ser definido como
|
||||
superclases antes de subclases.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
<hibernate-mapping>
|
||||
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
|
||||
<property name="name" type="string"/>
|
||||
</subclass>
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
|
||||
<para>
|
||||
Para información acerca de mapeos de herencia, ver <xref linkend="inheritance"/>.
|
||||
</para>
|
||||
|
@ -2871,7 +3175,24 @@
|
|||
ser malo e inconsistente).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</varlistentry> <varlistentry>
|
||||
<term>
|
||||
<literal>UNTRANSLATED! imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
|
||||
imm_serializable, imm_binary</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Type mappings for what are usually considered mutable Java types, where
|
||||
Hibernate makes certain optimizations appropriate only for immutable
|
||||
Java types, and the application treats the object as immutable. For
|
||||
example, you should not call <literal>Date.setTime()</literal> for an
|
||||
instance mapped as <literal>imm_timestamp</literal>. To change the
|
||||
value of the property, and have that change made persistent, the
|
||||
application must assign a new (nonidentical) object to the property.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
|
|
@ -31,14 +31,20 @@ session.close();]]></programlisting>
|
|||
</para>
|
||||
|
||||
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
|
||||
|
||||
<para id="disablebatching" revision="1">
|
||||
UNTRANSLATED! Note that Hibernate disables insert batching at the JDBC level transparently if you
|
||||
use an <literal>identiy</literal> identifier generator.
|
||||
</para>
|
||||
<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>
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
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>Inserciones en lote</title>
|
||||
|
||||
|
@ -97,6 +103,56 @@ session.close();]]></programlisting>
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="batch-statelesssession">
|
||||
<title>UNTRANSLATED! The StatelessSession interface</title>
|
||||
<para>
|
||||
Alternatively, Hibernate provides a command-oriented API that may be used for
|
||||
streaming data to and from the database in the form of detached objects. A
|
||||
<literal>StatelessSession</literal> has no persistence context associated
|
||||
with it and does not provide many of the higher-level life cycle semantics.
|
||||
In particular, a stateless session does not implement a first-level cache nor
|
||||
interact with any second-level or query cache. It does not implement
|
||||
transactional write-behind or automatic dirty checking. Operations performed
|
||||
using a stateless session do not ever cascade to associated instances. Collections
|
||||
are ignored by a stateless session. Operations performed via a stateless session
|
||||
bypass Hibernate's event model and interceptors. Stateless sessions are vulnerable
|
||||
to data aliasing effects, due to the lack of a first-level cache. A stateless
|
||||
session is a lower-level abstraction, much closer to the underlying JDBC.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
ScrollableResults customers = session.getNamedQuery("GetCustomers")
|
||||
.scroll(ScrollMode.FORWARD_ONLY);
|
||||
while ( customers.next() ) {
|
||||
Customer customer = (Customer) customers.get(0);
|
||||
customer.updateStuff(...);
|
||||
session.update(customer);
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Note that in this code example, the <literal>Customer</literal> instances returned
|
||||
by the query are immediately detached. They are never associated with any persistence
|
||||
context.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>insert(), update()</literal> and <literal>delete()</literal> operations
|
||||
defined by the <literal>StatelessSession</literal> interface are considered to be
|
||||
direct database row-level operations, which result in immediate execution of a SQL
|
||||
<literal>INSERT, UPDATE</literal> or <literal>DELETE</literal> respectively. Thus,
|
||||
they have very different semantics to the <literal>save(), saveOrUpdate()</literal>
|
||||
and <literal>delete()</literal> operations defined by the <literal>Session</literal>
|
||||
interface.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="batch-direct">
|
||||
<title>update/delete en masa</title>
|
||||
|
||||
|
@ -125,14 +181,17 @@ session.close();]]></programlisting>
|
|||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Puede haber sólo una clase mencionada en la cláusula-from, y <emphasis>no puede</emphasis>
|
||||
tener un alias.
|
||||
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 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.
|
||||
No <link linkend="queryhql-joins-forms">joins</link> (either implicit or explicit)
|
||||
can be specified in a bulk HQL query. Sub-queries may be used in the where-clause;
|
||||
the subqueries, themselves, may contain joins.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
|
@ -144,24 +203,50 @@ session.close();]]></programlisting>
|
|||
|
||||
<para>
|
||||
Como un ejemplo, para ejecutar un <literal>UPDATE</literal> HQL, usa el
|
||||
método <literal>Query.executeUpdate()</literal>:
|
||||
método <literal>Query.executeUpdate()</literal>(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 set name = :newName where name = :oldName";
|
||||
int updatedEntities = s.createQuery( hqlUpdate )
|
||||
.setString( "newName", newName )
|
||||
.setString( "oldName", oldName )
|
||||
.executeUpdate();
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
|
||||
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
|
||||
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
|
||||
int updatedEntities = s.createQuery( hqlUpdate )
|
||||
.setString( "newName", newName )
|
||||
.setString( "oldName", oldName )
|
||||
.executeUpdate();
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
HQL <literal>UPDATE</literal> statements, by default do not effect the
|
||||
<link linkend="mapping-declaration-version">version</link>
|
||||
or the <link linkend="mapping-declaration-timestamp">timestamp</link> property values
|
||||
for the affected entities; this is in keeping with the EJB3 specification. However,
|
||||
you can force Hibernate to properly reset the <literal>version</literal> or
|
||||
<literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
|
||||
This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
|
||||
keyword.
|
||||
</para>
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
|
||||
int updatedEntities = s.createQuery( hqlUpdate )
|
||||
.setString( "newName", newName )
|
||||
.setString( "oldName", oldName )
|
||||
.executeUpdate();
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
|
||||
are not allowed in conjunction with a <literal>update versioned</literal> statement.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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 ejecutar un <literal>DELETE</literal> HQL, usa el mismo método <literal>Query.executeUpdate()</literal>:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
|
|
|
@ -230,13 +230,20 @@ kittens = cat.getKittens(); // Okay, kittens collection is a Set
|
|||
propiedad.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="mappingcollection12">
|
||||
<callout arearefs="mappingcollection13">
|
||||
<para>
|
||||
<literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
|
||||
Especifica que los cambios de estado de la colección resultan en
|
||||
incrementos de versión de la entidad dueña. (Para asociaciones
|
||||
uno a muchos, frecuentemente es razonable deshabilitar esta opción.)
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="mappingcollection14">
|
||||
<para>
|
||||
<literal>mutable</literal> (optional - defaults to <literal>true</literal>):
|
||||
A value of <literal>false</literal> specifies that the elements of the
|
||||
collection never change (a minor performance optimization in some cases).
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
@ -555,6 +562,12 @@ kittens = cat.getKittens(); // Okay, kittens collection is a Set
|
|||
<literal>entity-name</literal> (opcional): El nombre de entidad de la clase
|
||||
asociada, como una alternativa a <literal>class</literal>.
|
||||
</para>
|
||||
</callout> <callout arearefs="manytomany8">
|
||||
<para> UNTRANSLATED!
|
||||
<literal>property-ref</literal>: (optional) The name of a property of the associated
|
||||
class that is joined to this foreign key. If not specified, the primary key of
|
||||
the associated class is used.
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
|
|
@ -392,11 +392,25 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
</entry>
|
||||
<entry>
|
||||
Escribe todas las sentencias SQL a la consola.
|
||||
<para>
|
||||
<emphasis role="strong">ej.</emphasis>
|
||||
<literal>true</literal> | <literal>false</literal>
|
||||
This is an alternative
|
||||
to setting the log category <literal>org.hibernate.SQL</literal>
|
||||
to <literal>debug</literal>.
|
||||
<para>
|
||||
<emphasis role="strong">eg.</emphasis>
|
||||
<literal>true</literal> | <literal>false</literal>
|
||||
</para>
|
||||
</entry>
|
||||
</row> <row>
|
||||
<entry>
|
||||
<literal>hibernate.format_sql</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Pretty print the SQL in the log and console.
|
||||
<para>
|
||||
<emphasis role="strong">eg.</emphasis>
|
||||
<literal>true</literal> | <literal>false</literal>
|
||||
</para>
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
|
@ -715,6 +729,13 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
<emphasis role="strong">ej.</emphasis>
|
||||
<literal>on_close</literal> (por defecto)| <literal>after_transaction</literal> |
|
||||
<literal>after_statement</literal> | <literal>auto</literal>
|
||||
</para> <para>
|
||||
Note that this setting only affects <literal>Session</literal>s returned from
|
||||
<literal>SessionFactory.openSession</literal>. For <literal>Session</literal>s
|
||||
obtained through <literal>SessionFactory.getCurrentSession</literal>, the
|
||||
<literal>CurrentSessionContext</literal> implementation configured for use
|
||||
controls the connection release mode for those <literal>Session</literal>s.
|
||||
See <xref linkend="architecture-current-session"/>
|
||||
</para>
|
||||
</entry>
|
||||
</row>
|
||||
|
@ -928,7 +949,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
<entry>
|
||||
De habilitarse, la sesión será cerrada automáticamente
|
||||
durante la fase posterior a la compleción de la transacción.
|
||||
(Muy útil cuando se usa Hibernate con CMT).
|
||||
(Muy útil cuando se usa Hibernate con CMT).<xref linkend="architecture-current-session"/>
|
||||
<para>
|
||||
<emphasis role="strong">ej.</emphasis>
|
||||
<literal>true</literal> | <literal>false</literal>
|
||||
|
@ -950,7 +971,22 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
<entry>Propósito</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody> <row>
|
||||
<entry>
|
||||
<literal>hibernate.current_session_context_class</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Supply a (custom) strategy for the scoping of the "current"
|
||||
<literal>Session</literal>. See
|
||||
<xref linkend="architecture-current-session"/> for more
|
||||
information about the built-in strategies.
|
||||
<para>
|
||||
<emphasis role="strong">eg.</emphasis>
|
||||
<literal>jta</literal> | <literal>thread</literal> |
|
||||
<literal>managed</literal> | <literal>custom.Class</literal>
|
||||
</para>
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>hibernate.query.factory_class</literal>
|
||||
|
@ -1636,7 +1672,10 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
de arranque (o clase de utilidad) en tu aplicación, a menos qie uses el despliegue
|
||||
JMX con el <literal>HibernateService</literal> (discutido luego).
|
||||
</para>
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
If you use a JNDI <literal>SessionFactory</literal>, an EJB or any other class may
|
||||
obtain the <literal>SessionFactory</literal> using a JNDI lookup.
|
||||
</para>
|
||||
<para>
|
||||
Si usas una <literal>SessionFactory</literal> de JNDI, un EJB o cualquier otra
|
||||
clase puede obtener la <literal>SessionFactory</literal> usando una búsqueda
|
||||
|
@ -1651,36 +1690,27 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
<sect2 id="configuration-j2ee-currentsession" revision="1">
|
||||
<title>Ligado automático de JTA y Session</title>
|
||||
|
||||
<para>
|
||||
Para entornos no manejados hemos sugerido <literal>HibernateUtil</literal> con una
|
||||
<literal>SessionFactory</literal> estática, y administración de la
|
||||
<literal>Session</literal> de Hibernate. Este enfoque no es fácil de usar
|
||||
en un entorno EJB, al poder ejecutarse muchos EJBs dentro de la misma transacción
|
||||
pero no en la misma hebra. Recomendados que ligues la <literal>SessionFactory</literal>
|
||||
a JNDI en un entorno manejado.
|
||||
<para> UNTRANSLATED!
|
||||
The easiest way to handle <literal>Session</literal>s and transactions is
|
||||
Hibernates automatic "current" <literal>Session</literal> management.
|
||||
See the discussion of <link linkend="architecture-current-session">current sessions</link>.
|
||||
Using the <literal>"jta"</literal> session context, if there is no Hibernate
|
||||
<literal>Session</literal> associated with the current JTA transaction, one will
|
||||
be started and associated with that JTA transaction the first time you call
|
||||
<literal>sessionFactory.getCurrentSession()</literal>. The <literal>Session</literal>s
|
||||
retrieved via <literal>getCurrentSession()</literal> in <literal>"jta"</literal> context
|
||||
will be set to automatically flush before the transaction completes, close
|
||||
after the transaction completes, and aggressively release JDBC connections
|
||||
after each statement. This allows the <literal>Session</literal>s to
|
||||
be managed by the life cycle of the JTA transaction to which it is associated,
|
||||
keeping user code clean of such management concerns. Your code can either use
|
||||
JTA programmatically through <literal>UserTransaction</literal>, or (recommended
|
||||
for portable code) use the Hibernate <literal>Transaction</literal> API to set
|
||||
transaction boundaries. If you run in an EJB container, declarative transaction
|
||||
demarcation with CMT is preferred.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
En vez de rodar tu propia utilidad de <literal>ThreadLocal</literal>,
|
||||
usa el método <literal>getCurrentSession()</literal> en la
|
||||
<literal>SessionFactory</literal> para obtener una <literal>Session</literal>
|
||||
de Hibernate. Si no hubiese una <literal>Session</literal> de Hibernate en la
|
||||
transacción JTA actual, se arrancará y asignará una.
|
||||
Ambas opciones de configuración <literal>hibernate.transaction.flush_before_completion</literal>
|
||||
y <literal>hibernate.transaction.auto_close_session</literal>, serán establecidas
|
||||
automáticamente para cada <literal>Session</literal> que obtengas con
|
||||
<literal>getCurrentSession()</literal>, de modo que éstas serán
|
||||
limpiadas (flushed) y cerradas automáticamente cuando el contenedor complete
|
||||
las transacciones JTA.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Si tu, por ejemplo, usas el patrón de diseño DAO para escribir tu
|
||||
capa de persistencia, todos los DAO's buscan la <literal>SessionFactory</literal>
|
||||
cuando se necesite y abren la sesión "actual". No hay necesidad de pasar
|
||||
las instancias de <literal>SessionFactory</literal> o <literal>Session</literal>
|
||||
alrededor entre el código de control y el código DAO.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
creado y actualiza la propiedad <literal>lastUpdateTimestamp</literal> cuando un
|
||||
<literal>Auditable</literal> es acutalizado.
|
||||
</para>
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
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;
|
||||
|
@ -103,14 +106,24 @@ public class AuditInterceptor implements Interceptor, Serializable {
|
|||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
El interceptor podría ser especificado cuando se crea la sesión:
|
||||
<para> UNTRANSLATED!
|
||||
Interceptors come in two flavors: <literal>Session</literal>-scoped and
|
||||
<literal>SessionFactory</literal>-scoped.
|
||||
</para>
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
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>
|
||||
Puedes además establecer un interceptor a un nivel global, usando la <literal>Configuration</literal>:
|
||||
<para> UNTRANSLATED!
|
||||
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>
|
||||
|
@ -218,7 +231,11 @@ cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() )
|
|||
<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> UNTRANSLATED!
|
||||
Note that <literal><listener type="..." class="..."/></literal> is just a shorthand
|
||||
for <literal><event type="..."><listener class="..."/></event></literal>
|
||||
when there is exactly one listener for a particular event type.
|
||||
</para>
|
||||
<para>
|
||||
Seguido, aún en <literal>hibernate.cfg.xml</literal>, liga los permisos a roles:
|
||||
</para>
|
||||
|
|
|
@ -42,6 +42,24 @@
|
|||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
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>
|
||||
<para>
|
||||
Es posible usar estrategias de mapeo diferentes para diferentes
|
||||
ramificaciones de la misma jerarquía de herencia, y entonces usar
|
||||
|
@ -258,7 +276,12 @@
|
|||
de unión de subclase, de hecho la semilla de clave primaria tiene que ser
|
||||
compartida a través de todas las subclases unidas de una jerarquía.
|
||||
</para>
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
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">
|
||||
|
|
|
@ -68,6 +68,20 @@
|
|||
<emphasis>Recuperación perezosa de colecciones</emphasis> - se recupera una colección cuando la
|
||||
aplicación invoca una operación sobre la colección. (Esto es por defecto para las colecciones.)
|
||||
</para>
|
||||
</listitem> <listitem>
|
||||
<para>
|
||||
<emphasis>"Extra-lazy" collection fetching</emphasis> - individual
|
||||
elements of the collection are accessed from the database as needed.
|
||||
Hibernate tries not to fetch the whole collection into memory unless
|
||||
absolutely needed (suitable for very large collections)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Proxy fetching</emphasis> - a single-valued association is
|
||||
fetched when a method other than the identifier getter is invoked
|
||||
upon the associated object.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
|
@ -186,9 +200,17 @@ Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></prog
|
|||
<para>
|
||||
las consultas de <literal>Criteria</literal>
|
||||
</para>
|
||||
</listitem> <listitem>
|
||||
<para>
|
||||
HQL queries if <literal>subselect</literal> fetching is used
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
No matter what fetching strategy you use, the defined non-lazy graph is guaranteed
|
||||
to be loaded into memory. Note that this might result in several immediate selects
|
||||
being used to execute a particular HQL query.
|
||||
</para>
|
||||
<para>
|
||||
Usualmente, no usamos el documento de mapeo para personalizar la recuperación. En cambio, mantenemos el
|
||||
comportamiento por defecto, y lo sobrescribimos para una transacción en particular, usando
|
||||
|
@ -353,7 +375,12 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
|
|||
Hibernate detectará las clase persistentes que sobrescriban <literal>equals()</literal> o
|
||||
<literal>hashCode()</literal>.
|
||||
</para>
|
||||
|
||||
<para> UNTRANSLATED!!!
|
||||
By choosing <literal>lazy="no-proxy"</literal> instead of the default
|
||||
<literal>lazy="proxy"</literal>, we can avoid the problems associated with typecasting.
|
||||
However, we will require buildtime bytecode instrumentation, and all operations
|
||||
will result in immediate proxy initialization.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="performance-fetching-initialization">
|
||||
|
@ -673,7 +700,8 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
|
|||
|
||||
<programlistingco>
|
||||
<areaspec>
|
||||
<area id="cache1" coords="2 70"/>
|
||||
<area id="cache1" coords="2 70"/><area id="cache2" coords="3 70"/>
|
||||
<area id="cache3" coords="4 70"/>
|
||||
</areaspec>
|
||||
<programlisting><![CDATA[<cache
|
||||
usage="transactional|read-write|nonstrict-read-write|read-only"
|
||||
|
@ -687,6 +715,20 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
|
|||
<literal>nonstrict-read-write</literal> o
|
||||
<literal>read-only</literal>
|
||||
</para>
|
||||
</callout> <callout arearefs="cache2">
|
||||
<para>
|
||||
<literal>region</literal> (optional, defaults to the class or
|
||||
collection role name) specifies the name of the second level cache
|
||||
region
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="cache3">
|
||||
<para>
|
||||
<literal>include</literal> (optional, defaults to <literal>all</literal>)
|
||||
<literal>non-lazy</literal> specifies that properties of the entity mapped
|
||||
with <literal>lazy="true"</literal> may not be cached when attribute-level
|
||||
lazy fetching is enabled
|
||||
</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
|
|
|
@ -470,6 +470,71 @@ dynamicSession.close()
|
|||
representación XML en <xref linkend="xml"/>.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
<sect1 id="persistent-classes-tuplizers" revision="1">
|
||||
<title>UNTRANSLATED!!! 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>
|
||||
|
|
|
@ -183,6 +183,65 @@
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="queryhql-joins-forms">
|
||||
<title>UNTRANSLATED!!! Forms of join syntax</title>
|
||||
|
||||
<para>
|
||||
HQL supports two forms of association joining: <literal>implicit</literal> and <literal>explicit</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The queries shown in the previous section all use the <literal>explicit</literal> form where
|
||||
the join keyword is explicitly used in the from clause. This is the recommended form.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>implicit</literal> form does not use the join keyword. Instead, the
|
||||
associations are "dereferenced" using dot-notation. <literal>implicit</literal> joins
|
||||
can appear in any of the HQL clauses. <literal>implicit</literal> join result
|
||||
in inner joins in the resulting SQL statement.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="queryhql-identifier-property">
|
||||
<title>Refering to identifier property</title>
|
||||
|
||||
<para>
|
||||
There are, generally speaking, 2 ways to refer to an entity's identifier property:
|
||||
</para>
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
The special property (lowercase) <literal>id</literal> may be used to reference the identifier
|
||||
property of an entity <emphasis>provided that entity does not define a non-identifier property
|
||||
named id</emphasis>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If the entity defines a named identifier property, you may use that property name.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
References to composite identifier properties follow the same naming rules. If the
|
||||
entity has a non-identifier property named id, the composite identifier property can only
|
||||
be referenced by its defined named; otherwise, the special <literal>id</literal> property
|
||||
can be used to rerference the identifier property.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note: this has changed significantly starting in version 3.2.2. In previous versions,
|
||||
<literal>id</literal> <emphasis>always</emphasis> referred to the identifier property no
|
||||
matter what its actual name. A ramification of that decision was that non-identifier
|
||||
properties named <literal>id</literal> could never be referenced in Hibernate queries.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="queryhql-select">
|
||||
<title>La cláusula select</title>
|
||||
|
||||
|
@ -844,55 +903,37 @@ order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
|
|||
consulta exterior).
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[from Cat as fatcat
|
||||
where fatcat.weight > (
|
||||
select avg(cat.weight) from DomesticCat cat
|
||||
)]]></programlisting>
|
||||
<programlisting><![CDATA[from Cat as fatcat
|
||||
where fatcat.weight > (
|
||||
select avg(cat.weight) from DomesticCat cat
|
||||
)]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[from DomesticCat as cat
|
||||
where cat.name = some (
|
||||
select name.nickName from Name as name
|
||||
)]]></programlisting>
|
||||
<programlisting><![CDATA[from DomesticCat as cat
|
||||
where cat.name = some (
|
||||
select name.nickName from Name as name
|
||||
)]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[from Cat as cat
|
||||
where not exists (
|
||||
from Cat as mate where mate.mate = cat
|
||||
)]]></programlisting>
|
||||
<programlisting><![CDATA[from Cat as cat
|
||||
where not exists (
|
||||
from Cat as mate where mate.mate = cat
|
||||
)]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[from DomesticCat as cat
|
||||
where cat.name not in (
|
||||
select name.nickName from Name as name
|
||||
)]]></programlisting>
|
||||
<programlisting><![CDATA[from DomesticCat as cat
|
||||
where cat.name not in (
|
||||
select name.nickName from Name as name
|
||||
)]]></programlisting>
|
||||
|
||||
<para>
|
||||
Para las subconsultas con más de una expresión en la lista de selección, puedes usar un constructor
|
||||
de tuplas:
|
||||
<programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
|
||||
from Cat as cat]]></programlisting>
|
||||
|
||||
<para>
|
||||
Note that HQL subqueries may occur only in the select or where clauses.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that subqueries can also utilize <literal>row value constructor</literal> syntax. See
|
||||
<xref linkend="queryhql-tuple"/> for more details.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[from Cat as cat
|
||||
where not ( cat.name, cat.color ) in (
|
||||
select cat.name, cat.color from DomesticCat cat
|
||||
)]]></programlisting>
|
||||
|
||||
<para>
|
||||
Nota que en algunas bases de datos (pero no en Oracle o HSQL), puedes usar constructores de tuplar en
|
||||
otros contextos, por ejemplo al consultar componentes o tipos de usuario compuestos:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
|
||||
|
||||
<para>
|
||||
Que es equivalente a la más verborrágica:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
|
||||
|
||||
<para>
|
||||
Existen dos buenas razones por las cuales podrías no querer hacer este tipo de cosa: primero, no es
|
||||
completamente portable entre plataformas de base de datos; segundo, la consulta ahora es dependiente
|
||||
del orden de propiedades en el documento de mapeo.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="queryhql-examples">
|
||||
|
|
|
@ -16,97 +16,387 @@
|
|||
las operaciones de creación, actualización, borrado y carga.
|
||||
</para>
|
||||
|
||||
<sect1 id="querysql-creating">
|
||||
<title>Creando una <literal>Query</literal> de SQL nativo</title>
|
||||
<sect1 id="querysql-creating" revision="4">
|
||||
<title>Using a <literal>SQLQuery</literal></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>
|
||||
<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>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
|
||||
.addEntity("cat", Cat.class)
|
||||
.setMaxResults(50)
|
||||
.list();]]></programlisting>
|
||||
<sect2>
|
||||
<title>Scalar queries</title>
|
||||
|
||||
<para>
|
||||
Esta consulta especificada:
|
||||
</para>
|
||||
<para>The most basic SQL query is to get a list of scalars
|
||||
(values).</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>
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
|
||||
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
El mé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>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>
|
||||
El método <literal>addJoin()</literal> puede ser usado para cargar asociaciones a otras entidades y
|
||||
colecciones.
|
||||
</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[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>
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
|
||||
.addScalar("ID", Hibernate.LONG)
|
||||
.addScalar("NAME", Hibernate.STRING)
|
||||
.addScalar("BIRTHDATE", Hibernate.DATE)
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
Una consulta SQL nativa podría devolver un valor escalar simple o una combinación de escalares y entidades.
|
||||
</para>
|
||||
<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>
|
||||
|
||||
<para>
|
||||
At this stage we are reaching the limits of what is possible with native queries without starting to
|
||||
enhance the sql queries to make them usable in Hibernate; the problems starts to arise when returning
|
||||
multiple entities of the same type or when the default alias/column names are not enough.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Returning multiple entities</title>
|
||||
|
||||
<para>Until now the result set column names are assumed to be the same
|
||||
as the column names specified in the mapping document. This can be
|
||||
problematic for SQL queries which join multiple tables, since the same
|
||||
column names may appear in more than one table.</para>
|
||||
|
||||
<para>Column alias injection is needed in the following query (which
|
||||
most likely will fail):</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
|
||||
.addEntity("cat", Cat.class)
|
||||
.addEntity("mother", Cat.class)
|
||||
]]></programlisting>
|
||||
|
||||
<para>The intention for this query is to return two Cat instances per
|
||||
row, a cat and its mother. This will fail since there is a conflict of
|
||||
names since they are mapped to the same column names and on some
|
||||
databases the returned column aliases will most likely be on the form
|
||||
"c.ID", "c.NAME", etc. which are not equal to the columns 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>
|
||||
|
||||
<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ón <literal>{cat.*}</literal> usada arriba es un atajo para "todas las propiedades".
|
||||
Alternativamente, puedes listar las columnas explí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ó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íamos incluso usar los alias de propiedad en la
|
||||
cláusula where si quisieramos.
|
||||
</para>
|
||||
<para>
|
||||
La sintá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ícitamente, ¡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>
|
||||
|
@ -164,7 +454,38 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
p.AGE AS age,
|
||||
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
|
||||
</sql-query>]]></programlisting>
|
||||
<para>You can externalize the resultset mapping informations in a
|
||||
<literal><resultset></literal> element to either reuse them 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},
|
||||
address.STREET AS {address.street},
|
||||
address.CITY AS {address.city},
|
||||
address.STATE AS {address.state},
|
||||
address.ZIP AS {address.zip}
|
||||
FROM PERSON person
|
||||
JOIN ADDRESS address
|
||||
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
|
||||
WHERE person.NAME LIKE :namePattern
|
||||
</sql-query>]]></programlisting>
|
||||
|
||||
<para>You can alternatively use the resultset mapping information in your
|
||||
hbm files directly in java code.</para>
|
||||
|
||||
<programlisting><![CDATA[List cats = sess.createSQLQuery(
|
||||
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
|
||||
)
|
||||
.setResultSetMapping("catAndKitten")
|
||||
.list();]]></programlisting>
|
||||
<sect2 id="propertyresults">
|
||||
<title>Usando return-property para especificar explícitamente nombres de columna/alias</title>
|
||||
|
||||
|
|
|
@ -101,7 +101,28 @@ Long generatedId = (Long) sess.save(fritz);]]></programlisting>
|
|||
usar <literal>persist()</literal> en vez de <literal>save()</literal>, con la semántica
|
||||
definida en el temprano borrador de EJB3.
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para> UNTRANSLATED!!!
|
||||
<literal>persist()</literal> makes a transient instance persistent.
|
||||
However, it doesn't guarantee that the identifier value will be assigned to
|
||||
the persistent instance immediately, the assignment might happen at flush time.
|
||||
<literal>persist()</literal> also guarantees that it will not execute an
|
||||
<literal>INSERT</literal> statement if it is called outside of transaction
|
||||
boundaries. This is useful in long-running conversations with an extended
|
||||
Session/persistence context.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>save()</literal> does guarantee to return an identifier. If an INSERT
|
||||
has to be executed to get the identifier ( e.g. "identity" generator, not
|
||||
"sequence"), this INSERT happens immediately, no matter if you are inside or
|
||||
outside of a transaction. This is problematic in a long-running conversation
|
||||
with an extended Session/persistence context.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Alternativamente, puedes asignar el identificador usando una versión sobrecargada
|
||||
de <literal>save()</literal>.
|
||||
|
@ -485,7 +506,13 @@ List cats = q.list();]]></programlisting>
|
|||
usado; puedes además definir consultas SQL nativas en metadatos, o migrar
|
||||
consultas existentes a Hibernate colocándolas en ficheros de mapeo.
|
||||
</para>
|
||||
|
||||
<para> UNTRANSLATED!
|
||||
Also note that a query declaration inside a <literal><hibernate-mapping></literal>
|
||||
element requires a global unique name for the query, while a query declaration inside a
|
||||
<literal><class></literal> element is made unique automatically by prepending the
|
||||
fully qualified name of the class, for example
|
||||
<literal>eg.Cat.ByNameAndMaximumWeight</literal>.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
@ -1187,7 +1214,14 @@ tx.commit(); // flush occurs]]></programlisting>
|
|||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para> UNTRANSLATED!!!
|
||||
Finally, note that cascading of operations can be applied to an object graph at
|
||||
<emphasis>call time</emphasis> or at <emphasis>flush time</emphasis>. All operations,
|
||||
if enabled, are cascaded to associated entities reachable when the operation is
|
||||
executed. However, <literal>save-upate</literal> and <literal>delete-orphan</literal>
|
||||
are transitive for all associated entities reachable during flush of the
|
||||
<literal>Session</literal>.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="objectstate-metadata">
|
||||
|
|
|
@ -77,45 +77,68 @@
|
|||
Con este atributo puedes establecer el tamaño de una columna. (O, para tipos de datos
|
||||
numéricos/decimales, la precisión.)
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
|
||||
<programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
|
||||
<para>
|
||||
Algunas etiquetas también aceptan un atributo <literal>not-null</literal> (para generar una restricció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>
|
||||
<programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
|
||||
|
||||
<para>
|
||||
Algunas etiquetas aceptan un atributo <literal>index</literal> para especificar el nombre de un índice
|
||||
para esa columna. Se puede usar un atributo <literal>unique-key</literal> para agrupar columnas en una
|
||||
restricció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ón, sólo para
|
||||
agrupar las columnas en el fichero de mapeo.
|
||||
</para>
|
||||
<programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
|
||||
|
||||
<para>
|
||||
Ejemplos:
|
||||
</para>
|
||||
<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[<property name="foo" type="string" length="64" not-null="true"/>
|
||||
<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>
|
||||
|
||||
<many-to-one name="bar" foreign-key="fk_foo_bar" not-null="true"/>
|
||||
<programlisting><![CDATA[<property name="lastName" index="CustName"/>
|
||||
<property name="firstName" index="CustName"/>]]></programlisting>
|
||||
|
||||
<element column="serial_number" type="long" not-null="true" unique="true"/>]]></programlisting>
|
||||
<para>
|
||||
A <literal>foreign-key</literal> attribute may be used to override the name
|
||||
of any generated foreign key constraint.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Alternativamente, estos elementos aceptan tambíen un elemento hijo <literal><column></literal>.
|
||||
Esto es particularmente útil para tipos multicolumnas:
|
||||
</para>
|
||||
<programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[<property name="foo" type="string">
|
||||
<column name="foo" length="64" not-null="true" sql-type="text"/>
|
||||
<para>
|
||||
Many mapping elements also accept a child <literal><column></literal> element.
|
||||
This is particularly useful for mapping multi-column types:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
|
||||
<column name="last" not-null="true" index="bar_idx" length="30"/>
|
||||
<column name="first" not-null="true" index="bar_idx" length="20"/>
|
||||
<column name="initial"/>
|
||||
</property>]]></programlisting>
|
||||
|
||||
<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>
|
||||
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>
|
||||
El atributo <literal>sql-type</literal> permite al usuario sobrescribir el mapeo por defecto de
|
||||
|
@ -155,7 +178,16 @@
|
|||
<entry>number</entry>
|
||||
<entry>largo de columna/precisión decimal</entry>
|
||||
</row>
|
||||
<row>
|
||||
<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>
|
||||
<entry><literal>not-null</literal></entry>
|
||||
<entry><literal>true|false</literal></entry>
|
||||
<entry>especifica que la columna debe ser no nulable</entry>
|
||||
|
@ -180,8 +212,9 @@
|
|||
<entry><literal>foreign_key_name</literal></entry>
|
||||
<entry>
|
||||
especifica el nombre de la restricción de clave foránea generada por una
|
||||
asociación, úsalo en los elementos de mapeo <one-to-one>, <many-to-one>,
|
||||
<key>, y <many-to-many>. Nota que los lados
|
||||
asociación, úsalo e <literal><one-to-one></literal>,
|
||||
<literal><many-to-one></literal>, <literal><key></literal>,
|
||||
or <literal><many-to-many></literal> . Nota que los lados
|
||||
<literal>inverse="true"</literal> no serán considerados por
|
||||
<literal>SchemaExport</literal>.
|
||||
</entry>
|
||||
|
@ -193,6 +226,12 @@
|
|||
sobrescribe el tipo de columna por defecto (sólo atributo del elemento
|
||||
<literal><column></literal>)
|
||||
</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>
|
||||
|
@ -260,6 +299,9 @@
|
|||
<row>
|
||||
<entry><literal>--drop</literal></entry>
|
||||
<entry>sólo desechar las tablas</entry>
|
||||
</row> <row>
|
||||
<entry><literal>--create</literal></entry>
|
||||
<entry>only create the tables</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>--text</literal></entry>
|
||||
|
@ -268,6 +310,9 @@
|
|||
<row>
|
||||
<entry><literal>--output=my_schema.ddl</literal></entry>
|
||||
<entry>enviar la salida del guión ddl a un fichero</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>
|
||||
|
@ -415,10 +460,20 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
|
|||
<row>
|
||||
<entry><literal>--quiet</literal></entry>
|
||||
<entry>no enviar a salida estándar el guión</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>lee las propiedades de base de datos de un fichero</entry>
|
||||
</row> <row>
|
||||
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
|
||||
<entry>specify a <literal>.cfg.xml</literal> file</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
|
|
|
@ -14,7 +14,13 @@
|
|||
Básicamente, usa Hibernate como usarías JDBC directo (o JTA/CMT) con tus recursos de base de
|
||||
datos.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Hibernate does not lock objects in memory. Your application can expect the behavior as
|
||||
defined by the isolation level of your database transactions. Note that thanks to the
|
||||
<literal>Session</literal>, which is also a transaction-scoped cache, Hibernate
|
||||
provides repeatable reads for lookup by identifier and entity queries (not
|
||||
reporting queries that return scalar values).
|
||||
</para>
|
||||
<para>
|
||||
Sin embargo, además del versionado automático, Hibernate ofrece una API (menor) para
|
||||
bloqueo pesimista de filas, usando la sintáxis <literal>SELECT FOR UPDATE</literal>.
|
||||
|
@ -90,22 +96,37 @@
|
|||
aplicaciones.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
El desafío yace en la implementación: no sólo tienen que comenzarse y terminarse correctamente
|
||||
la <literal>Session</literal> y la transacción, sino que además tienen que estar accesibles
|
||||
para las operaciones de acceso a datos. La demarcación de una unidad de trabajo se implementa
|
||||
idealmente usando un interceptor que se ejecuta cuando una petició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ón, usando una variable
|
||||
<literal>ThreadLocal</literal>. Esto permite un fácil acceso (como acceder a una variable static)
|
||||
en tódo el código que se ejecuta en esta hebra. Dependiendo del mecanismo de demarcación de
|
||||
transacciones de base de datos que elijas, podrías mantener también el contexto de la transacción
|
||||
en una variable <literal>ThreadLocal</literal>. Los patrones de implementación para esto son
|
||||
conocidos como <emphasis>Sesión Local de Hebra (ThreadLocal Session)</emphasis> y
|
||||
<emphasis>Sesión Abierta en Vista (Open Session in View)</emphasis>. Puedes extender fá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>
|
||||
The challenge lies in the implementation. Hibernate provides built-in management of
|
||||
the "current session" to simplify this pattern. All you have to do is start a
|
||||
transaction when a server request has to be processed, and end the transaction
|
||||
before the response is send to the client. You can do this in any way you
|
||||
like, common solutions are <literal>ServletFilter</literal>, AOP interceptor with a
|
||||
pointcut on the service methods, or a proxy/interception container. An EJB container
|
||||
is a standardized way to implement cross-cutting aspects such as transaction
|
||||
demarcation on EJB session beans, declaratively with CMT. If you decide to
|
||||
use programmatic transaction demarcation, prefer the Hibernate <literal>Transaction</literal>
|
||||
API shown later in this chapter, for ease of use and code portability.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Your application code can access a "current session" to process the request
|
||||
by simply calling <literal>sessionFactory.getCurrentSession()</literal> anywhere
|
||||
and as often as needed. You will always get a <literal>Session</literal> scoped
|
||||
to the current database transaction. This has to be configured for either
|
||||
resource-local or JTA environments, see <xref linkend="architecture-current-session"/>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Sometimes it is convenient to extend the scope of a <literal>Session</literal> and
|
||||
database transaction until the "view has been rendered". This is especially useful
|
||||
in servlet applications that utilize a separate rendering phase after the request
|
||||
has been processed. Extending the database transaction until view rendering is
|
||||
complete is easy to do if you implement your own interceptor. However, it is not
|
||||
easily doable if you rely on EJBs with container-managed transactions, as a
|
||||
transaction will be completed when an EJB method returns, before rendering of any
|
||||
view can start. See the Hibernate website and forum for tips and examples around
|
||||
this <emphasis>Open Session in View</emphasis> pattern.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
@ -421,9 +442,7 @@ finally {
|
|||
<para>
|
||||
No tienes que limpiar con <literal>flush()</literal> la <literal>Session</literal> explícitamente -
|
||||
la llamada a <literal>commit()</literal> automáticamente dispara la sincronización.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Una llamada a <literal>close()</literal> marca el fin de una sesión. La principal implicación
|
||||
de <literal>close()</literal> es que la conexión JDBC será abandonada por la sesión.
|
||||
</para>
|
||||
|
@ -625,6 +644,50 @@ Session sess = factory.getCurrentSession();
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-demarcation-timeout">
|
||||
<title>Transaction timeout</title>
|
||||
|
||||
<para>
|
||||
One extremely important feature provided by a managed environment like EJB
|
||||
that is never provided for non-managed code is transaction timeout. Transaction
|
||||
timeouts ensure that no misbehaving transaction can indefinitely tie up
|
||||
resources while returning no response to the user. Outside a managed (JTA)
|
||||
environment, Hibernate cannot fully provide this functionality. However,
|
||||
Hibernate can at least control data access operations, ensuring that database
|
||||
level deadlocks and queries with huge result sets are limited by a defined
|
||||
timeout. In a managed environment, Hibernate can delegate transaction timeout
|
||||
to JTA. This functioanlity is abstracted by the Hibernate
|
||||
<literal>Transaction</literal> object.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
Session sess = factory.openSession();
|
||||
try {
|
||||
//set transaction timeout to 3 seconds
|
||||
sess.getTransaction().setTimeout(3);
|
||||
sess.getTransaction().begin();
|
||||
|
||||
// do some work
|
||||
...
|
||||
|
||||
sess.getTransaction().commit()
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
sess.getTransaction().rollback();
|
||||
throw e; // or display error message
|
||||
}
|
||||
finally {
|
||||
sess.close();
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
Note that <literal>setTimeout()</literal> may not be called in a CMT bean,
|
||||
where transaction timeouts must be defined declaratively.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="transactions-optimistic">
|
||||
|
@ -700,7 +763,7 @@ session.close();]]></programlisting>
|
|||
La <literal>Session</literal> se desconecta de cualquier conexión JDBC subyacente
|
||||
al esperar por una interacción del usuario. Este enfoque es el más eficiente en términos
|
||||
de acceso a base de datos. La aplicación no necesita tratar por sí misma con el chequeo de
|
||||
versiones, ni re-uniendo instancias separadas, ni tiene que recargar instancias en cada
|
||||
versiones, ni re-uniendo instancias separadas, ni tiene que recargar instancias en cadatransactions-demarcation-timeout
|
||||
transacción de base de datos.
|
||||
</para>
|
||||
|
||||
|
@ -742,7 +805,12 @@ session.disconnect(); // Return JDBC connection ]]></programlisting>
|
|||
<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>
|
||||
|
||||
<para> UNTRANSLATED!!!
|
||||
The extended session pattern, or <emphasis>session-per-conversation</emphasis>, is
|
||||
more difficult to implement with automatic current session context management.
|
||||
You need to supply your own implementation of the <literal>CurrentSessionContext</literal>
|
||||
for this, see the Hibernate Wiki for examples.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-optimistic-detached">
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
Michael Gloegl. Las bibliotecas de terceros que mencionamos son para JDK 1.4
|
||||
y 5.0. Podrías necesitar otras para JDK 1.3.
|
||||
</para>
|
||||
<para> UNTRANSLATED!!!
|
||||
The source code for the tutorial is included in the distribution in the
|
||||
<literal>doc/reference/tutorial/</literal> directory.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
@ -786,17 +790,39 @@ else if (args[0].equals("list")) {
|
|||
Puedes, por supuesto, crear consultas más complejas con HQL.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Si ahora llamas a Ant con <literal>-Daction=list</literal>, debes ver los eventos
|
||||
que has almacenado hasta ahora. Puede sorprenderte que esto no funcione, al menos
|
||||
si has seguido este tutorial paso por paso; el resultado siempre estará
|
||||
vacío. La razon de esto es la opción <literal>hbm2ddl.auto</literal>
|
||||
en la configuración de Hibernate: Hibernate recreará la base de datos
|
||||
en cada ejecución. Deshabilítala quitando la opción, y verás
|
||||
resultados en tu listado después que llames a la acción <literal>store</literal>
|
||||
unas cuantas veces. La generación y exportación de esquema es útil
|
||||
mayormente en testeo unitario.
|
||||
<para>
|
||||
Now, to execute and test all of this, follow these steps:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Run <literal>ant run -Daction=store</literal> to store something into the database
|
||||
and, of course, to generate the database schema before through hbm2ddl.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Now disable hbm2ddl by commenting out the property in your <literal>hibernate.cfg.xml</literal>
|
||||
file. Usually you only leave it turned on in continous unit testing, but another
|
||||
run of hbm2ddl would <emphasis>drop</emphasis> everything you have stored - the
|
||||
<literal>create</literal> configuration setting actually translates into "drop all
|
||||
tables from the schema, then re-create all tables, when the SessionFactory is build".
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
If you now call Ant with <literal>-Daction=list</literal>, you should see the events
|
||||
you have stored so far. You can of course also call the <literal>store</literal> action a few
|
||||
times more.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note: Most new Hibernate users fail at this point and we see questions about
|
||||
<emphasis>Table not found</emphasis> error messages regularly. However, if you follow the
|
||||
steps outlined above you will not have this problem, as hbm2ddl creates the database
|
||||
schema on the first run, and subsequent application restarts will use this schema. If
|
||||
you change the mapping and/or database schema, you have to re-enable hbm2ddl once again.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
@ -1119,11 +1145,25 @@ public void setEmailAddresses(Set emailAddresses) {
|
|||
direcciones de email duplicadas por persona, que es exactamente la semántica
|
||||
que necesitamos para un conjunto en Java.
|
||||
</para>
|
||||
<programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
|
||||
|
||||
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
|
||||
session.beginTransaction();
|
||||
|
||||
Person aPerson = (Person) session.load(Person.class, personId);
|
||||
|
||||
// The getEmailAddresses() might trigger a lazy load of the collection
|
||||
aPerson.getEmailAddresses().add(emailAddress);
|
||||
|
||||
session.getTransaction().commit();
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
Puedes ahora intentar y agregar elementos a esta colección, al igual que
|
||||
hicimos antes enlazando personas y eventos. Es el mismo código en Java.
|
||||
</para>
|
||||
hicimos antes enlazando personas y eventos. Es el mismo código en Java.</para><para>
|
||||
This time we didnt' use a <emphasis>fetch</emphasis> query to initialize the collection.
|
||||
Hence, the call to its getter method will trigger an additional select to initialize
|
||||
it, so we can add an element to it. Monitor the SQL log and try to optimize this with
|
||||
an eager fetch.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
@ -1246,7 +1286,279 @@ public void removeFromEvent(Event event) {
|
|||
</para>
|
||||
-->
|
||||
</sect2>
|
||||
</sect1>
|
||||
<para>
|
||||
Let's turn this into a small web application.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="tutorial-webapp">
|
||||
<title>Part 3 - The EventManager web application</title>
|
||||
|
||||
<para>
|
||||
A Hibernate web application uses <literal>Session</literal> and <literal>Transaction</literal>
|
||||
almost like a standalone application. However, some common patterns are useful. We now write
|
||||
an <literal>EventManagerServlet</literal>. This servlet can list all events stored in the
|
||||
database, and it provides an HTML form to enter new events.
|
||||
</para>
|
||||
|
||||
<sect2 id="tutorial-webapp-servlet" revision="2">
|
||||
<title>Writing the basic servlet</title>
|
||||
|
||||
<para>
|
||||
Create a new class in your source directory, in the <literal>events</literal>
|
||||
package:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[package events;
|
||||
|
||||
// Imports
|
||||
|
||||
public class EventManagerServlet extends HttpServlet {
|
||||
|
||||
// Servlet code
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
The servlet handles HTTP <literal>GET</literal> requests only, hence, the method
|
||||
we implement is <literal>doGet()</literal>:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[protected void doGet(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
|
||||
|
||||
try {
|
||||
// Begin unit of work
|
||||
HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().beginTransaction();
|
||||
|
||||
// Process request and render page...
|
||||
|
||||
// End unit of work
|
||||
HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().getTransaction().commit();
|
||||
|
||||
} catch (Exception ex) {
|
||||
HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().getTransaction().rollback();
|
||||
throw new ServletException(ex);
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
The pattern we are applying here is called <emphasis>session-per-request</emphasis>.
|
||||
When a request hits the servlet, a new Hibernate <literal>Session</literal> is
|
||||
opened through the first call to <literal>getCurrentSession()</literal> on the
|
||||
<literal>SessionFactory</literal>. Then a database transaction is started—all
|
||||
data access as to occur inside a transaction, no matter if data is read or written
|
||||
(we don't use the auto-commit mode in applications).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Do <emphasis>not</emphasis> use a new Hibernate <literal>Session</literal> for
|
||||
every database operation. Use one Hibernate <literal>Session</literal> that is
|
||||
scoped to the whole request. Use <literal>getCurrentSession()</literal>, so that
|
||||
it is automatically bound to the current Java thread.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Next, the possible actions of the request are processed and the response HTML
|
||||
is rendered. We'll get to that part soon.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, the unit of work ends when processing and rendering is complete. If any
|
||||
problem occured during processing or rendering, an exception will be thrown
|
||||
and the database transaction rolled back. This completes the
|
||||
<literal>session-per-request</literal> pattern. Instead of the transaction
|
||||
demarcation code in every servlet you could also write a servlet filter.
|
||||
See the Hibernate website and Wiki for more information about this pattern,
|
||||
called <emphasis>Open Session in View</emphasis>—you'll need it as soon
|
||||
as you consider rendering your view in JSP, not in a servlet.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-webapp-processing" revision="1">
|
||||
<title>Processing and rendering</title>
|
||||
|
||||
<para>
|
||||
Let's implement the processing of the request and rendering of the page.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[// Write HTML header
|
||||
PrintWriter out = response.getWriter();
|
||||
out.println("<html><head><title>Event Manager</title></head><body>");
|
||||
|
||||
// Handle actions
|
||||
if ( "store".equals(request.getParameter("action")) ) {
|
||||
|
||||
String eventTitle = request.getParameter("eventTitle");
|
||||
String eventDate = request.getParameter("eventDate");
|
||||
|
||||
if ( "".equals(eventTitle) || "".equals(eventDate) ) {
|
||||
out.println("<b><i>Please enter event title and date.</i></b>");
|
||||
} else {
|
||||
createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
|
||||
out.println("<b><i>Added event.</i></b>");
|
||||
}
|
||||
}
|
||||
|
||||
// Print page
|
||||
printEventForm(out);
|
||||
listEvents(out, dateFormatter);
|
||||
|
||||
// Write HTML footer
|
||||
out.println("</body></html>");
|
||||
out.flush();
|
||||
out.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Granted, this coding style with a mix of Java and HTML would not scale
|
||||
in a more complex application—keep in mind that we are only illustrating
|
||||
basic Hibernate concepts in this tutorial. The code prints an HTML
|
||||
header and a footer. Inside this page, an HTML form for event entry and
|
||||
a list of all events in the database are printed. The first method is
|
||||
trivial and only outputs HTML:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[private void printEventForm(PrintWriter out) {
|
||||
out.println("<h2>Add new event:</h2>");
|
||||
out.println("<form>");
|
||||
out.println("Title: <input name='eventTitle' length='50'/><br/>");
|
||||
out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
|
||||
out.println("<input type='submit' name='action' value='store'/>");
|
||||
out.println("</form>");
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
The <literal>listEvents()</literal> method uses the Hibernate
|
||||
<literal>Session</literal> bound to the current thread to execute
|
||||
a query:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
|
||||
|
||||
List result = HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().createCriteria(Event.class).list();
|
||||
if (result.size() > 0) {
|
||||
out.println("<h2>Events in database:</h2>");
|
||||
out.println("<table border='1'>");
|
||||
out.println("<tr>");
|
||||
out.println("<th>Event title</th>");
|
||||
out.println("<th>Event date</th>");
|
||||
out.println("</tr>");
|
||||
for (Iterator it = result.iterator(); it.hasNext();) {
|
||||
Event event = (Event) it.next();
|
||||
out.println("<tr>");
|
||||
out.println("<td>" + event.getTitle() + "</td>");
|
||||
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
|
||||
out.println("</tr>");
|
||||
}
|
||||
out.println("</table>");
|
||||
}
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
Finally, the <literal>store</literal> action is dispatched to the
|
||||
<literal>createAndStoreEvent()</literal> method, which also uses
|
||||
the <literal>Session</literal> of the current thread:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
|
||||
Event theEvent = new Event();
|
||||
theEvent.setTitle(title);
|
||||
theEvent.setDate(theDate);
|
||||
|
||||
HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().save(theEvent);
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
That's it, the servlet is complete. A request to the servlet will be processed
|
||||
in a single <literal>Session</literal> and <literal>Transaction</literal>. As
|
||||
earlier in the standalone application, Hibernate can automatically bind these
|
||||
ojects to the current thread of execution. This gives you the freedom to layer
|
||||
your code and access the <literal>SessionFactory</literal> in any way you like.
|
||||
Usually you'd use a more sophisticated design and move the data access code
|
||||
into data access objects (the DAO pattern). See the Hibernate Wiki for more
|
||||
examples.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-webapp-deploy">
|
||||
<title>Deploying and testing</title>
|
||||
|
||||
<para>
|
||||
To deploy this application you have to create a web archive, a WAR. Add the
|
||||
following Ant target to your <literal>build.xml</literal>:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<target name="war" depends="compile">
|
||||
<war destfile="hibernate-tutorial.war" webxml="web.xml">
|
||||
<lib dir="${librarydir}">
|
||||
<exclude name="jsdk*.jar"/>
|
||||
</lib>
|
||||
|
||||
<classes dir="${targetdir}"/>
|
||||
</war>
|
||||
</target>]]></programlisting>
|
||||
|
||||
<para>
|
||||
This target creates a file called <literal>hibernate-tutorial.war</literal>
|
||||
in your project directory. It packages all libraries and the <literal>web.xml</literal>
|
||||
descriptor, which is expected in the base directory of your project:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app version="2.4"
|
||||
xmlns="http://java.sun.com/xml/ns/j2ee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Event Manager</servlet-name>
|
||||
<servlet-class>events.EventManagerServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Event Manager</servlet-name>
|
||||
<url-pattern>/eventmanager</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>]]></programlisting>
|
||||
|
||||
<para>
|
||||
Before you compile and deploy the web application, note that an additional library
|
||||
is required: <literal>jsdk.jar</literal>. This is the Java servlet development kit,
|
||||
if you don't have this library already, get it from the Sun website and copy it to
|
||||
your library directory. However, it will be only used for compliation and excluded
|
||||
from the WAR package.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To build and deploy call <literal>ant war</literal> in your project directory
|
||||
and copy the <literal>hibernate-tutorial.war</literal> file into your Tomcat
|
||||
<literal>webapp</literal> directory. If you don't have Tomcat installed, download
|
||||
it and follow the installation instructions. You don't have to change any Tomcat
|
||||
configuration to deploy this application though.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once deployed and Tomcat is running, access the application at
|
||||
<literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>. Make
|
||||
sure you watch the Tomcat log to see Hibernate initialize when the first
|
||||
request hits your servlet (the static initializer in <literal>HibernateUtil</literal>
|
||||
is called) and to get the detailed output if any exceptions occurs.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="tutorial-summary">
|
||||
<title>Summary</title>
|
||||
|
|
Loading…
Reference in New Issue