doc'd natural-id
updated best practices git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@6901 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
1ccd7b67d8
commit
24f3e76c9a
|
@ -1710,6 +1710,44 @@
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="mapping-declaration-naturalid">
|
||||
<title>natural-id</title>
|
||||
|
||||
<programlisting><![CDATA[<natural-id mutable="true|false"/>
|
||||
<property ... />
|
||||
<many-to-one ... />
|
||||
......
|
||||
</natural-id>]]></programlisting>
|
||||
|
||||
<para>
|
||||
Even though we recommend the use of surrogate keys as primary keys, you should still try
|
||||
to identify natural keys for all entities. A natural key is a property or combination of
|
||||
properties that is unique and non-null. If it is also immutable, even better. Map the
|
||||
properties of the natural key inside the <literal><natural-id></literal> element.
|
||||
Hibernate will generate the necessary unique key and nullability constraints, and your
|
||||
mapping will be more self-documenting.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
We strongly recommend that you implement <literal>equals()</literal> and
|
||||
<literal>hashCode()</literal> to compare the natural key properties of the entity.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This mapping is not intended for use with entities with natural primary keys.
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>mutable</literal> (optional, defaults to <literal>false</literal>):
|
||||
By default, natural identifier properties as assumed to be immutable (constant).
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="mapping-declaration-component" revision="2">
|
||||
<title>component, dynamic-component</title>
|
||||
|
||||
|
|
|
@ -18,9 +18,17 @@
|
|||
<para>
|
||||
Hibernate makes identifier properties optional. There are all sorts of reasons why
|
||||
you should use them. We recommend that identifiers be 'synthetic' (generated, with
|
||||
no business meaning). It doesn't make a difference if you use <literal>long</literal>
|
||||
or <literal>java.lang.Long</literal>; primitives might be syntactically easier to handle
|
||||
though.
|
||||
no business meaning).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Identify natural keys.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Identify natural keys for all entities, and map them using
|
||||
<literal><natural-id></literal>. Implement <literal>equals()</literal> and
|
||||
<literal>hashCode()</literal> to compare the properties that make up the natural key.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -47,7 +55,8 @@
|
|||
<listitem>
|
||||
<para>
|
||||
This is a good practice if your queries call non-ANSI-standard SQL functions.
|
||||
Externalising the query strings to mapping files will make the application more portable.
|
||||
Externalising the query strings to mapping files will make the application more
|
||||
portable.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -86,12 +95,11 @@
|
|||
<term>Use hand-coded JDBC in bottlenecks.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
In performance-critical areas of the system, some kinds of operations (eg. mass update /
|
||||
delete) might benefit from direct JDBC. But please, wait until you <emphasis>know</emphasis>
|
||||
something is a bottleneck. And don't assume that direct JDBC is necessarily faster. If need to
|
||||
use direct JDBC, it might be worth opening a Hibernate <literal>Session</literal> and using that SQL
|
||||
connection. That way you can still use the same transaction strategy and underlying connection
|
||||
provider.
|
||||
In performance-critical areas of the system, some kinds of operations might benefit from
|
||||
direct JDBC. But please, wait until you <emphasis>know</emphasis> something is a bottleneck.
|
||||
And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might
|
||||
be worth opening a Hibernate <literal>Session</literal> and using that JDBC connection. That
|
||||
way you can still use the same transaction strategy and underlying connection provider.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -107,27 +115,29 @@
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>In a three tiered architecture, consider using <literal>saveOrUpdate()</literal>.</term>
|
||||
<term>In a three tiered architecture, consider using detached objects.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
When using a servlet / session bean architecture, you could pass persistent objects loaded in
|
||||
the session bean to and from the servlet / JSP layer. Use a new session to service each request.
|
||||
Use <literal>Session.update()</literal> or <literal>Session.saveOrUpdate()</literal> to update the
|
||||
persistent state of an object.
|
||||
Use <literal>Session.merge()</literal> or <literal>Session.saveOrUpdate()</literal> to
|
||||
synchronize objects with the database.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>In a two tiered architecture, consider using session disconnection.</term>
|
||||
<term>In a two tiered architecture, consider using long persistence contexts.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Database Transactions have to be as short as possible for best scalability. However, it is often
|
||||
neccessary to implement long running Application Transactions, a single unit-of-work from the
|
||||
point of view of a user. This Application Transaction might span several client requests and
|
||||
response cycles. Either use Detached Objects or, in two tiered architectures, simply disconnect
|
||||
the Hibernate Session from the JDBC connection and reconnect it for each subsequent request. Never
|
||||
use a single Session for more than one Application Transaction usecase, otherwise, you will run
|
||||
into stale data.
|
||||
neccessary to implement long running <emphasis>application transactions</emphasis>, a single
|
||||
unit-of-work from the point of view of a user. An application transaction might span several
|
||||
client request/response cycles. It is common to use detached objects to implement application
|
||||
transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain
|
||||
a single open persistence contact (session) for the whole lifecycle of the application transaction
|
||||
and simply disconnect from the JDBC connection at the end of each request and reconnect at the
|
||||
beginning of the subsequent request. Never share a single session across more than one application
|
||||
transaction, or you will be working with stale data.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -139,7 +149,7 @@
|
|||
the <literal>Transaction</literal> and close the <literal>Session</literal>. If you don't, Hibernate
|
||||
can't guarantee that in-memory state accurately represents persistent state. As a special case of this,
|
||||
do not use <literal>Session.load()</literal> to determine if an instance with the given identifier
|
||||
exists on the database; use <literal>find()</literal> instead.
|
||||
exists on the database; use <literal>Session.get()</literal> or a query instead.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -147,11 +157,30 @@
|
|||
<term>Prefer lazy fetching for associations.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Use eager (outer-join) fetching sparingly. Use proxies and/or lazy collections for most associations
|
||||
to classes that are not cached at the JVM-level. For associations to cached classes, where there is
|
||||
a high probability of a cache hit, explicitly disable eager fetching using
|
||||
<literal>outer-join="false"</literal>. When an outer-join fetch is appropriate to a particular use
|
||||
case, use a query with a <literal>left join</literal>.
|
||||
Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that
|
||||
are not likely to be completely held in the second-level cache. For associations to cached classes,
|
||||
where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using
|
||||
<literal>lazy="false"</literal>. When an join fetching is appropriate to a particular use
|
||||
case, use a query with a <literal>left join fetch</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
Use the <emphasis>open session in view</emphasis> pattern, or a disciplined
|
||||
<emphasis>assembly phase</emphasis> to avoid problems with unfetched data.
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Hibernate frees the developer from writing tedious <emphasis>Data Transfer Objects</emphasis> (DTO).
|
||||
In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem
|
||||
that entity beans are not serializable; second, they implicitly define an assembly phase where
|
||||
all data to be used by the view is fetched and marshalled into the DTOs before returning control
|
||||
to the presentation tier. Hibernate eliminates the first purpose. However, you will still need
|
||||
an assembly phase (think of your business methods as having a strict contract with the presentation
|
||||
tier about what data is available in the detached objects) unless you are prepared to hold the
|
||||
persistence context (the session) open across the view rendering process. This is not a limitation
|
||||
of Hibernate! It is a fundamental requirement of safe transactional data access.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -167,23 +196,6 @@
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Implement <literal>equals()</literal> and <literal>hashCode()</literal> using a unique business key.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If you compare objects outside of the Session scope, you have to implement <literal>equals()</literal>
|
||||
and <literal>hashCode()</literal>. Inside the Session scope, Java object identity is guaranteed. If
|
||||
you implement these methods, never ever use the database identifier! A transient object doesn't have
|
||||
an identifier value and Hibernate would assign a value when the object is saved. If the object
|
||||
is in a Set while being saved, the hash code changes, breaking the contract. To implement
|
||||
<literal>equals()</literal> and <literal>hashCode()</literal>, use a unique business key, that is,
|
||||
compare a unique combination of class properties. Remember that this key has to be stable and unique
|
||||
only while the object is in a Set, not for the whole lifetime (not as stable as a database primary
|
||||
key). Never use collections in the <literal>equals()</literal> comparison (lazy loading) and be careful
|
||||
with other associated classes that might be proxied.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Don't use exotic association mappings.</term>
|
||||
<listitem>
|
||||
|
@ -196,6 +208,15 @@
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Prefer bidirectional associations.</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Unidirectional associations are more difficult to query. In a large application, almost
|
||||
all associations must be navigable in both directions in queries.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</chapter>
|
||||
|
|
|
@ -381,4 +381,56 @@ session.createCriteria(Cat.class, "cat")
|
|||
<!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
|
||||
user objects - similar to setResultClass in JDO2. General use of ResultTransformer
|
||||
could also be explained. -->
|
||||
|
||||
<sect1 id="query-criteria-naturalid">
|
||||
<title>Queries by natural identifier</title>
|
||||
|
||||
<para>
|
||||
For most queries, including criteria queries, the query cache is not very efficient,
|
||||
because query cache invalidation occurs too frequently. However, there is one special
|
||||
kind of query where we can optimize the cache invalidation algorithm: lookups by a
|
||||
constant natural key. In some applications, this kind of query occurs frequently.
|
||||
The criteria API provides special provision for this use case.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
First, you should map the natural key of your entity using
|
||||
<literal><natural-id></literal>, and enable use of the second-level cache.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[<class name="User">
|
||||
<cache usage="read-write"/>
|
||||
<id name="id">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<natural-id>
|
||||
<property name="name"/>
|
||||
<property name="org"/>
|
||||
</natural-id>
|
||||
<property name="password"/>
|
||||
</class>]]></programlisting>
|
||||
|
||||
<para>
|
||||
Note that this functionality is not intended for use with entities with
|
||||
<emphasis>mutable</emphasis> natural keys.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Next, enable the Hibernate query cache.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now, <literal>Restrictions.naturalId()</literal> allows us to make use of
|
||||
the more efficient cache algorithm.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[session.createCriteria(User.class)
|
||||
.add( Restrictions.naturalId()
|
||||
.set("name", "gavin")
|
||||
.set("org", "hb")
|
||||
).setCacheable(true)
|
||||
.uniqueResult();]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
Loading…
Reference in New Issue