Match to latest English XML

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14142 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
xhuang 2007-10-26 07:36:12 +00:00
parent f134d4ab5c
commit 1a1fc99092
12 changed files with 995 additions and 485 deletions

View File

@ -323,6 +323,14 @@
courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.context.ManagedSessionContext</literal> - current
sessions are tracked by thread of execution. However, you are responsible to
bind and unbind a <literal>Session</literal> instance with static methods
on this class, it does never open, flush, or close a <literal>Session</literal>.
</para>
</listitem>
</itemizedlist>
<para>

View File

@ -365,7 +365,14 @@ create table Address ( addressId bigint not null primary key )
<one-to-many class="Person"/>
</list>
</class>]]></programlisting>
<para>
It is important that you define <literal>not-null="true"</literal> on the
<literal>&lt;key&gt;</literal> element of the collection mapping if the
underlying foreign key column is <literal>NOT NULL</literal>. Don't only
declare <literal>not-null="true"</literal> on a possible nested
<literal>&lt;column&gt;</literal> element, but on the <literal>&lt;key&gt;</literal>
element.
</para>
</sect2>
<sect2 id="assoc-bidirectional-121">

View File

@ -98,7 +98,60 @@
des recherches de la DTD sur Internet, vérifiez votre déclaration de DTD par rapport
au contenu de votre classpath.
</para>
<sect3 id="mapping-declaration-entity-resolution">
<title>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>
<para>
@ -357,19 +410,14 @@
pour plus d'informations.
</para>
</callout>
<callout arearefs="class18">
<para>
<literal>catalog</literal> (optionnel) : The name of a database catalog used for this
class and its table.
</para>
</callout>
<callout arearefs="class19">
<para>
<literal>check</literal> (optionnel) : expression SQL utilisée pour générer une contrainte
de vérification multi-lignes pour la génération automatique de schéma.
</para>
</callout>
<callout arearefs="class20">
<callout arearefs="class19">
<para>
<literal>rowid</literal> (optionnel) : Hibernate peut utiliser des ROWID sur les bases de
données qui utilisent ce mécanisme. Par exemple avec Oracle, Hibernate peut utiliser la colonne additionnelle
@ -377,14 +425,14 @@
la localisation physique d'un tuple enregistré.
</para>
</callout>
<callout arearefs="class21">
<callout arearefs="class20">
<para>
<literal>subselect</literal> (optionnel) : Permet de mapper une entité immuable en lecture-seule
sur un sous-select de base de données. Utile pour avoir une vue au lieu d'une table en base, mais à éviter. Voir plus bas
pour plus d'information.
</para>
</callout>
<callout arearefs="class22">
<callout arearefs="class21">
<para>
<literal>abstract</literal> (optionnel) : Utilisé pour marquer des superclasses abstraites dans
des hiérarchies de <literal>&lt;union-subclass&gt;</literal>.
@ -733,6 +781,19 @@
avec une association <literal>&lt;one-to-one&gt;</literal> sur la clef primaire.
</para>
</listitem>
</varlistentry> <varlistentry>
<term><literal>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>
@ -829,6 +890,176 @@
</para>
</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="3">
<title>composite-id</title>
<programlisting><![CDATA[<composite-id

View File

@ -713,21 +713,28 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
<emphasis role="strong">ex.</emphasis>
<literal>on_close</literal> (default) | <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>
<row>
<entry>
<literal>hibernate.connection.<emphasis>&lt;propertyName&gt;</emphasis></literal>
<literal>hibernate.connection.</literal><emphasis>&lt;propertyName&gt;</emphasis>
</entry>
<entry>
Passe la propriété JDBC<literal>propertyName</literal>
Passe la propriété JDBC <emphasis>&lt;propertyName&gt;</emphasis>
à <literal>DriverManager.getConnection()</literal>.
</entry>
</row>
<row>
<entry>
<literal>hibernate.jndi.<emphasis>&lt;propertyName&gt;</emphasis></literal>
<literal>hibernate.jndi.</literal><emphasis>&lt;propertyName&gt;</emphasis>
</entry>
<entry>
Passe la propriété <literal>propertyName</literal> à l'<literal>InitialContextFactory</literal>
@ -1415,7 +1422,13 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
<programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
<para>
You can pick a different XML configuration file using
</para>
<programlisting><![CDATA[SessionFactory sf = new Configuration()
.configure("catdb.cfg.xml")
.buildSessionFactory();]]></programlisting>
</sect1>

View File

@ -117,14 +117,25 @@ public class AuditInterceptor extends EmptyInterceptor {
}]]></programlisting>
<para>
L'intercepteur doit être spécifié quand une session est créée.
Interceptors come in two flavors: <literal>Session</literal>-scoped and
<literal>SessionFactory</literal>-scoped.
</para>
<para>
A <literal>Session</literal>-scoped interceptor is specified
when a session is opened using one of the overloaded SessionFactory.openSession()
methods accepting an <literal>Interceptor</literal>.
</para>
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
<para>
Vous pouvez aussi mettre un intercepteur au niveau global, en utilisant l'objet <literal>Configuration</literal>.
Dans ce cas, l'intercepteur doit être "threadsafe".
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>

View File

@ -719,7 +719,7 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
<entry>OSCache</entry>
<entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
<entry>mémoire, disque</entry>
<entry>oui (invalidation de cluster)</entry>
<entry></entry>
<entry>oui</entry>
</row>
<row>

View File

@ -214,6 +214,43 @@
<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 clause select</title>
@ -905,33 +942,9 @@ from Cat as cat]]></programlisting>
</para>
<para>
Pour des sous-requêtes avec plus d'une expression dans le select, vous pouvez utiliser un constructeur de tuples :
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>
Notez que sur certaines bases de données (mais par Oracle ou HSQL), vous pouvez utiliser des constructeurs de tuples
dans d'autres contextes, par exemple lors du requêtage de composants ou de types utilisateur composites :
</para>
<programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
<para>
Ce qui est équivalent à la forme plus verbeuse suivante :
</para>
<programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
<para>
Il y a deux bonnes raisons que vous ne puissiez ne pas vouloir faire cette sorte de choses : d'abord, ce n'est
pas complètement portable entre les plateformes de base de données ; deuxièmement, la requête est maintenant
dépendante de l'ordre des propriétés dans le document de mapping.
</para>
</sect1>
<sect1 id="queryhql-examples">

View File

@ -23,122 +23,226 @@
Dans des cas extrêmement simples, nous pouvons utiliser la forme suivante :
</para>
<programlisting>List cats = sess.createSQLQuery("select * from cats")
.addEntity(Cat.class)
.list();</programlisting>
<para>Cette requête a spécifié :</para>
<sect2>
<title>Scalar queries</title>
<para>The most basic SQL query is to get a list of scalars
(values).</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
]]></programlisting>
<para>These will both return a List of Object arrays (Object[]) with
scalar values for each column in the CATS table. Hibernate will use
ResultSetMetadata to deduce the actual order and types of the returned
scalar values.</para>
<para>To avoid the overhead of using
<literal>ResultSetMetadata</literal> or simply to be more explicit in
what is returned one can use <literal>addScalar()</literal>.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>la requête SQL</para>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>l'entité retournée par la requête</para>
<para>the columns and types to return</para>
</listitem>
</itemizedlist>
<para>
Ici, les noms de colonne des résultats sont supposés être les mêmes que les noms de colonne spécifiés dans le
document de mapping. Cela peut être problématique pour des requêtes SQL qui joignent de multiple tables, puisque
les mêmes noms de colonne peuvent apparaître dans plus d'une table. La forme suivante n'est pas vulnérable à la
duplication des noms de colonne :
</para>
<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>
<programlisting>List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class)
.list();</programlisting>
<para>It is possible to leave out the type information for all or some
of the scalars.</para>
<para>Cette requête a spécifié :</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>la requête SQL, avec un paramètre fictif pour Hibernate pour injecter les alias de colonne</para>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>l'entité retournée par la requête, et son alias de table SQL</para>
<para>the entity returned by the query</para>
</listitem>
</itemizedlist>
<para>
La méthode <literal>addEntity()</literal> associe l'alias de la table SQL
avec la classe de l'entité retournée, et détermine la forme de l'ensemble des résultats de la requête.
</para>
<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>
La méthode <literal>addJoin()</literal> peut être utilisée pour charger des associations vers d'autres
entités et collections.
</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>List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
<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("kitten", "cat.kittens")
.list();</programlisting>
.addJoin("cat.dog");
]]></programlisting>
<para>
Une requête SQL native pourrait retourner une simple valeur scalaire ou une combinaison de scalaires et d'entités.
</para>
<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>Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();</programlisting>
<para>Vous pouvez alternativement décrire les informations de mapping des résultats dans vos fichiers hbm
et les utiliser pour vos requêtes.</para>
<programlisting>List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();</programlisting>
</sect1>
<sect1 id="querysql-aliasreferences">
<title>Alias et références de propriété</title>
<para>
La notation <literal>{cat.*}</literal> utilisée au-dessus est un raccourci pour "toutes les propriétés".
Alternativement, vous pouvez lister explicitement les colonnes, mais même ce cas que nous laissons à Hibernate
injecte des alias de colonne SQL pour chaque propriété. Le remplaçant pour un alias de colonne
est juste le nom de la propriété qualifié par l'alias de la table.
Dans l'exemple suivant, nous récupérons des <literal>Cat</literal>s à partir d'une table différente
(<literal>cat_log</literal>) de celle déclarée dans les méta-données de mapping.
Notez que nous pouvons même utiliser les alias de propriété dans la clause "where" si nous le souhaitons.
</para>
<para>
La syntaxe <literal>{}</literal> <emphasis>n'est pas</emphasis> requise pour le requêtes nommées. Voir
<xref linkend="querysql-namedqueries" />.
</para>
<programlisting>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)
<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)
.setLong("catId", catId)
.list();</programlisting>
.addJoin("cat.dogs");
]]></programlisting>
<para>
<emphasis>À noter :</emphasis> si vous listez chaque propriété explicitement, vous devez inclure
toutes les propriétés de la classe <emphasis>et ses sous-classes</emphasis> !
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>
<para>
La table suivante montre les différentes possibilités d'utilisation de l'injection d'alias. À noter : les noms
des alias dans le résultat sont des exemples, chaque alias aura un nom unique et probablement différent lors de l'utilisation.
</para>
<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>Noms d'injection d'alias</title>
<title>Alias injection names</title>
<tgroup cols="4">
<tgroup cols="3">
<colspec colwidth="1*" />
<colspec colwidth="1*" />
@ -149,23 +253,23 @@ List loggedCats = sess.createSQLQuery(sql)
<row>
<entry>Description</entry>
<entry>Syntaxe</entry>
<entry>Syntax</entry>
<entry>Exemple</entry>
<entry>Example</entry>
</row>
</thead>
<tbody>
<row>
<entry>Une simple propriété</entry>
<entry>A simple property</entry>
<entry><literal>{[aliasname].[propertyname]}</literal></entry>
<entry><literal>{[aliasname].[propertyname]</literal></entry>
<entry><literal>A_NAME as {item.name}</literal></entry>
</row>
<row>
<entry>Une propriété composée</entry>
<entry>A composite property</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
@ -174,7 +278,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Discriminant d'une entité</entry>
<entry>Discriminator of an entity</entry>
<entry><literal>{[aliasname].class}</literal></entry>
@ -182,7 +286,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Toutes les propriétés d'une entité</entry>
<entry>All properties of an entity</entry>
<entry><literal>{[aliasname].*}</literal></entry>
@ -190,7 +294,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Une clef de collection</entry>
<entry>A collection key</entry>
<entry><literal>{[aliasname].key}</literal></entry>
@ -198,7 +302,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>L'identifiant d'une collection</entry>
<entry>The id of an collection</entry>
<entry><literal>{[aliasname].id}</literal></entry>
@ -206,17 +310,15 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>L'élément d'une collection</entry>
<entry>The element of an collection</entry>
<entry><literal>{[aliasname].element}</literal></entry>
<entry><literal>XID as {coll.element}</literal></entry>
<entry></entry>
</row>
<row>
<entry>Propriété de l'élément dans la collection</entry>
<entry>roperty of the element in the collection</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
@ -224,7 +326,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Toutes les propriétés de l'élément dans la collection</entry>
<entry>All properties of the element in the collection</entry>
<entry><literal>{[aliasname].element.*}</literal></entry>
@ -232,7 +334,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Toutes les propriétés de la collection</entry>
<entry>All properties of the the collection</entry>
<entry><literal>{[aliasname].*}</literal></entry>
@ -241,7 +343,58 @@ List loggedCats = sess.createSQLQuery(sql)
</tbody>
</tgroup>
</table>
</sect1>
</sect3>
</sect2>
<sect2>
<title>Returning non-managed entities</title>
<para>It is possible to apply a ResultTransformer to native sql queries. Allowing it to e.g. return non-managed entities.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
<para>This query specified:</para>
<itemizedlist>
<listitem>
<para>the SQL query string</para>
</listitem>
<listitem>
<para>a result transformer</para>
</listitem>
</itemizedlist>
<para>
The above query will return a list of <literal>CatDTO</literal> which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding
properties or fields.
</para>
</sect2>
<sect2>
<title>Handling inheritance</title>
<para>Native sql queries which query for entities that is mapped as part
of an inheritance must include all properties for the baseclass and all
it subclasses.</para>
</sect2>
<sect2>
<title>Parameters</title>
<para>Native sql queries support positional as well as named
parameters:</para>
<programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
</sect2>
</sect1>
<sect1 id="querysql-namedqueries" revision="3">
<title>Requêtes SQL nommées</title>
@ -252,19 +405,19 @@ List loggedCats = sess.createSQLQuery(sql)
cas, nous <emphasis>n'avons pas besoin</emphasis> d'appeler <literal>addEntity()</literal>.
</para>
<programlisting>&lt;sql-query name="persons"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
<programlisting><![CDATA[<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<programlisting>List people = sess.getNamedQuery("persons")
<programlisting><![CDATA[List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();</programlisting>
.list();]]></programlisting>
<para>
Les éléments <literal>&lt;return-join&gt;</literal> et
@ -272,9 +425,9 @@ List loggedCats = sess.createSQLQuery(sql)
des associations et définir des requêtes qui initialisent des collections.
</para>
<programlisting>&lt;sql-query name="personsWith"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
&lt;return-join alias="address" property="person.mailingAddress"/&gt;
<programlisting><![CDATA[<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
@ -286,20 +439,20 @@ List loggedCats = sess.createSQLQuery(sql)
JOIN ADDRESS address
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<para>
Une requête SQL nommée peut retourner une valeur scalaire. Vous devez
spécifier l'alias de colonne et le type Hibernate utilisant l'élément
<literal>&lt;return-scalar&gt;</literal> :</para>
<programlisting>&lt;sql-query name="mySqlQuery"&gt;
&lt;return-scalar column="name" type="string"/&gt;
&lt;return-scalar column="age" type="long"/&gt;
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<para>
Vous pouvez externaliser les informations de mapping des résultats dans un
@ -308,12 +461,12 @@ List loggedCats = sess.createSQLQuery(sql)
<literal>setResultSetMapping()</literal>.
</para>
<programlisting>&lt;resultset name="personAddress"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
&lt;return-join alias="address" property="person.mailingAddress"/&gt;
&lt;/resultset&gt;
<programlisting><![CDATA[<resultset name="personAddress">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
</resultset>
&lt;sql-query name="personsWith" resultset-ref="personAddress"&gt;
<sql-query name="personsWith" resultset-ref="personAddress">
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
@ -325,7 +478,15 @@ List loggedCats = sess.createSQLQuery(sql)
JOIN ADDRESS address
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
</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>Utilisation de return-property pour spécifier explicitement les noms des colonnes/alias</title>
@ -336,18 +497,18 @@ List loggedCats = sess.createSQLQuery(sql)
<literal>{}</literal> pour laisser Hibernate injecter ses propres alias.
</para>
<programlisting>&lt;sql-query name="mySqlQuery"&gt;
&lt;return alias="person" class="eg.Person"&gt;
&lt;return-property name="name" column="myName"/&gt;
&lt;return-property name="age" column="myAge"/&gt;
&lt;return-property name="sex" column="mySex"/&gt;
&lt;/return&gt;
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
&lt;/sql-query&gt;
</programlisting>
</sql-query>
]]></programlisting>
<para>
<literal>&lt;return-property&gt;</literal> fonctionne aussi avec de
@ -355,21 +516,22 @@ List loggedCats = sess.createSQLQuery(sql)
qui ne peut pas permettre une bonne granularité des propriétés multi-colonnes.
</para>
<programlisting>&lt;sql-query name="organizationCurrentEmployments"&gt;
&lt;return alias="emp" class="Employment"&gt;
&lt;return-property name="salary"&gt;
&lt;return-column name="VALUE"/&gt;
&lt;return-column name="CURRENCY"/&gt;
&lt;/return-property&gt;
&lt;return-property name="endDate" column="myEndDate"/&gt;
&lt;/return&gt;
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<para>
Notez que dans cet exemple nous avons utilisé <literal>&lt;return-property&gt;</literal>
@ -396,36 +558,37 @@ List loggedCats = sess.createSQLQuery(sql)
version supérieure :
</para>
<programlisting>CREATE OR REPLACE FUNCTION selectAllEmployments
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
AS
st_cursor SYS_REFCURSOR;
BEGIN
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;</programlisting>
END;]]></programlisting>
<para>Pour utiliser cette requête dans Hibernate vous avez besoin de la mapper via une requête nommée.</para>
<programlisting>&lt;sql-query name="selectAllEmployees_SP" callable="true"&gt;
&lt;return alias="emp" class="Employment"&gt;
&lt;return-property name="employee" column="EMPLOYEE"/&gt;
&lt;return-property name="employer" column="EMPLOYER"/&gt;
&lt;return-property name="startDate" column="STARTDATE"/&gt;
&lt;return-property name="endDate" column="ENDDATE"/&gt;
&lt;return-property name="regionCode" column="REGIONCODE"/&gt;
&lt;return-property name="id" column="EID"/&gt;
&lt;return-property name="salary"&gt;
&lt;return-column name="VALUE"/&gt;
&lt;return-column name="CURRENCY"/&gt;
&lt;/return-property&gt;
&lt;/return&gt;
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<para>
Notez que les procédures stockées retournent, pour le moment, seulement des
@ -447,7 +610,10 @@ BEGIN
<para>Les requêtes de procédures stockées ne peuvent pas être paginées avec
<literal>setFirstResult()/setMaxResults()</literal>.</para>
<para>Recommended call form is standard SQL92: <literal>{ ? = call
functionName(&lt;parameters&gt;) }</literal> or <literal>{ ? = call
procedureName(&lt;parameters&gt;}</literal>. Native call syntax is not
supported.</para>
<para>Pour Oracle les règles suivantes s'appliquent :</para>
<itemizedlist spacing="compact">
@ -493,15 +659,15 @@ BEGIN
<literal>&lt;sql-delete&gt;</literal>, et
<literal>&lt;sql-update&gt;</literal> surchargent ces chaînes de caractères :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
&lt;generator class="increment"/&gt;
&lt;/id&gt;
&lt;property name="name" not-null="true"/&gt;
&lt;sql-insert&gt;INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )&lt;/sql-insert&gt;
&lt;sql-update&gt;UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?&lt;/sql-update&gt;
&lt;sql-delete&gt;DELETE FROM PERSON WHERE ID=?&lt;/sql-delete&gt;
&lt;/class&gt;</programlisting>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>]]></programlisting>
<para>Le SQL est directement exécuté dans votre base de données, donc vous êtes libre d'utiliser
le dialecte que vous souhaitez. Cela réduira bien sûr la portabilité de votre mapping si vous
@ -509,15 +675,16 @@ BEGIN
<para>Les procédures stockées sont supportées si l'attribut <literal>callable</literal> est paramétré :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
&lt;generator class="increment"/&gt;
&lt;/id&gt;
&lt;property name="name" not-null="true"/&gt;
&lt;sql-insert callable="true"&gt;{call createPerson (?, ?)}&lt;/sql-insert&gt;
&lt;sql-delete callable="true"&gt;{? = call deletePerson (?)}&lt;/sql-delete&gt;
&lt;sql-update callable="true"&gt;{? = call updatePerson (?, ?)}&lt;/sql-update&gt;
&lt;/class&gt;</programlisting>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>L'ordre des paramètres positionnels est actuellement vital, car ils doivent être dans la
même séquence qu'Hibernate les attend.</para>
@ -536,9 +703,10 @@ BEGIN
Hibernate inscrit toujours la première expression comme un paramètre de sortie numérique pour les
opérations CUD :</para>
<programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
BEGIN
update PERSON
set
@ -548,7 +716,7 @@ BEGIN
return SQL%ROWCOUNT;
END updatePerson;</programlisting>
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
@ -556,53 +724,52 @@ END updatePerson;</programlisting>
<para>Vous pouvez aussi déclarer vos propres requêtes SQL (ou HQL) pour le chargement d'entité :</para>
<programlisting>&lt;sql-query name="person"&gt;
&lt;return alias="pers" class="Person" lock-mode="upgrade"/&gt;
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<para>Ceci est juste une déclaration de requête nommée, comme vu plus tôt. Vous pouvez référencer
cette requête nommée dans un mapping de classe :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
&lt;generator class="increment"/&gt;
&lt;/id&gt;
&lt;property name="name" not-null="true"/&gt;
&lt;loader query-ref="person"/&gt;
&lt;/class&gt;</programlisting>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>Ceci fonctionne même avec des procédures stockées.</para>
<para>Vous pouvez même définir une requête pour le chargement d'une collection :</para>
<programlisting><![CDATA[<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>]]></programlisting>
<programlisting>&lt;set name="employments" inverse="true"&gt;
&lt;key/&gt;
&lt;one-to-many class="Employment"/&gt;
&lt;loader query-ref="employments"/&gt;
&lt;/set&gt;</programlisting>
<programlisting>&lt;sql-query name="employments"&gt;
&lt;load-collection alias="emp" role="Person.employments"/&gt;
<programlisting><![CDATA[<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
<para>Vous pourriez même définir un chargeur d'entité qui charge une collection par jointure :</para>
<programlisting>&lt;sql-query name="person"&gt;
&lt;return alias="pers" class="Person"/&gt;
&lt;return-join alias="emp" property="pers.employments"/&gt;
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
&lt;/sql-query&gt;</programlisting>
</sql-query>]]></programlisting>
</sect1>
</chapter>

View File

@ -95,6 +95,30 @@ Long generatedId = (Long) sess.save(fritz);]]></programlisting>
avec la sémantique définie plus tôt dans le brouillon d'EJB3.
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<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>
Alternativement, vous pouvez assigner l'identifiant en utilisant une version
surchargée de <literal>save()</literal>.
@ -478,6 +502,13 @@ List cats = q.list();]]></programlisting>
utilisé, vous pouvez aussi définir des requêtes SQL nativez dans les méta-données, ou
migrer des requêtes existantes vers Hibernate en les plaçant dans les fichiers de mapping.
</para>
<para> UNTRANSLATED!
Also note that a query declaration inside a <literal>&lt;hibernate-mapping&gt;</literal>
element requires a global unique name for the query, while a query declaration inside a
<literal>&lt;class&gt;</literal> element is made unique automatically by prepending the
fully qualified name of the class, for example
<literal>eg.Cat.ByNameAndMaximumWeight</literal>.
</para>
</sect3>

View File

@ -219,7 +219,9 @@
<entry>
spécifie le nom d'une contrainte de clé étrangère générée pour
une association, utilisez-la avec les éléments de mapping
&lt;one-to-one&gt;, &lt;many-to-one&gt;, &lt;key&gt;, et &lt;many-to-many&gt;
<literal>&lt;one-to-one&gt;</literal>,
<literal>&lt;many-to-one&gt;</literal>, <literal>&lt;key&gt;</literal>,
or <literal>&lt;many-to-many&gt;</literal>
Notez que les extrêmités <literal>inverse="true"</literal>
se seront pas prises en compte par <literal>SchemaExport</literal>.
</entry>
@ -451,8 +453,8 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
</para>
<para>
<literal>java -cp </literal><emphasis>classpath_hibernate</emphasis>
<literal>net.sf.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options fichiers_de_mapping</emphasis>
<literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
<literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
</para>
<table frame="topbot">
@ -482,6 +484,9 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>lire les propriétés de la base de données à partir d'un fichier</entry>
</row> <row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>specify a <literal>.cfg.xml</literal> file</entry>
</row>
</tbody>
</tgroup>

View File

@ -800,7 +800,12 @@ catch (RuntimeException e) {
pour conserver la <literal>Session</literal> et évitez de la sérialiser et de la transférer à la couche de présentation (i.e. Il est préférable de ne pas
la conserver dans la session <literal>HttpSession</literal> .)
</para>
<para>
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">

View File

@ -681,7 +681,9 @@ public class EventManager {
La méthode <literal>getCurrentSession()</literal> renvoie toujours l'unité de travail courante.
Souvenez vous que nous avons basculé notre option de configuration au mécanisme basé sur le "thread"
dans <literal>hibernate.cfg.xml</literal>. Par conséquent, le scope de l'unité de travail
courante est le thread java courant d'exécution. Ceci n'est pas totalement vrai. Une
courante est le thread java courant d'exécution. Ceci n'est pas totalement vrai. </para>
<para>Une
<literal>Session</literal> commence lorsqu'elle est vraiment utilisée la première fois,
Lorsque nous appelons pour la première fois <literal>getCurrentSession()</literal>.
Ensuite, elle est liée, par Hibernate, au thread courant. Lorsque la transaction s'achève
@ -691,7 +693,17 @@ public class EventManager {
Ce modèle de programmation "<emphasis>thread-bound</emphasis>" est le moyen le plus
populaire d'utiliser Hibernate.
</para>
<para> UNTRANSLATED
!
Related to the unit of work scope, should the Hibernate <literal>Session</literal> be used to
execute one or several database operations? The above example uses one <literal>Session</literal>
for one operation. This is pure coincidence, the example is just not complex enough to show any
other approach. The scope of a Hibernate <literal>Session</literal> is flexible but you should
never design your application to use a new Hibernate <literal>Session</literal> for
<emphasis>every</emphasis> database operation. So even if you see it a few more times in
the following (very trivial) examples, consider <emphasis>session-per-operation</emphasis>
an anti-pattern. A real (web) application is shown later in this tutorial.
</para>
<para>
Lisez <xref linkend="transactions"/> pour plus d'informations sur la gestion des transactions et leur démarcations.
Nous n'avons pas géré les erreurs et rollback sur l'exemple précédent.
@ -801,7 +813,13 @@ else if (args[0].equals("list")) {
les événements que vous avez stockés jusque là. Vous pouvez bien sûr aussi appeler l'action
<literal>store</literal> plusieurs fois.
</para>
<para> UNTRANSLATED!
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>
</sect1>
@ -1285,7 +1303,12 @@ public void removeFromEvent(Event event) {
doesn't scale anymore with our growing application.
</para>
-->
</sect2>
<para>
Let's turn this into a small web application.
</para>
</sect1>
<sect1 id="tutorial-webapp">
@ -1307,25 +1330,16 @@ public void removeFromEvent(Event event) {
<programlisting><![CDATA[package events;
// Imports
// Imports
public class EventManagerServlet extends HttpServlet {
private final SimpleDateFormat dateFormatter =
new SimpleDateFormat("dd.MM.yyyy");
public class EventManagerServlet extends HttpServlet {
// Servlet code
}]]></programlisting>
}]]></programlisting>
<para>
Le <literal>dateFormatter</literal> est un outil que nous utiliserons plus tard pour convertir les objets
<literal>Date</literal> depuis et vers des chaines de caractères. Il est propice de n'avoir qu'un
formatter comme membre de la servlet.
</para>
<para>
La servlet n'accepte que les requêtes HTTP <literal>GET</literal>, la méthode à implémenter est donc
<literal>doGet()</literal>:
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,
@ -1359,7 +1373,12 @@ public class EventManagerServlet extends HttpServlet {
tous les accès à la base de données interviennent au sein de la transactiton, peu importe que les données
soient lues ou écrites (nous n'utilisons pas le mode auto-commit dans les applications).
</para>
<para> UNTRANSLATED
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>
Ensuite, les actions possibles de la requêtes sont exécutées et la réponse HTML
est rendue. Nous en parlerons plus tard.