224 lines
12 KiB
XML
224 lines
12 KiB
XML
<chapter id="best-practices" revision="3">
|
|
<title>Best Practices</title>
|
|
|
|
<variablelist spacing="compact">
|
|
<varlistentry>
|
|
<term>Write fine-grained classes and map them using <literal><component></literal>.</term>
|
|
<listitem>
|
|
<para>
|
|
Use an <literal>Address</literal> class to encapsulate <literal>street</literal>,
|
|
<literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
|
|
This encourages code reuse and simplifies refactoring.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Declare identifier properties on persistent classes.</term>
|
|
<listitem>
|
|
<para>
|
|
Hibernate makes identifier properties optional. There are all sorts of reasons why
|
|
you should use them. We recommend that identifiers be 'synthetic' (generated, with
|
|
no business meaning).
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Identify natural keys.</term>
|
|
<listitem>
|
|
<para>
|
|
Identify natural keys for all entities, and map them using
|
|
<literal><natural-id></literal>. Implement <literal>equals()</literal> and
|
|
<literal>hashCode()</literal> to compare the properties that make up the natural key.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Place each class mapping in its own file.</term>
|
|
<listitem>
|
|
<para>
|
|
Don't use a single monolithic mapping document. Map <literal>com.eg.Foo</literal> in
|
|
the file <literal>com/eg/Foo.hbm.xml</literal>. This makes particularly good sense in
|
|
a team environment.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Load mappings as resources.</term>
|
|
<listitem>
|
|
<para>
|
|
Deploy the mappings along with the classes they map.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Consider externalising query strings.</term>
|
|
<listitem>
|
|
<para>
|
|
This is a good practice if your queries call non-ANSI-standard SQL functions.
|
|
Externalising the query strings to mapping files will make the application more
|
|
portable.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Use bind variables.</term>
|
|
<listitem>
|
|
<para>
|
|
As in JDBC, always replace non-constant values by "?". Never use string manipulation to
|
|
bind a non-constant value in a query! Even better, consider using named parameters in
|
|
queries.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Don't manage your own JDBC connections.</term>
|
|
<listitem>
|
|
<para>
|
|
Hibernate lets the application manage JDBC connections. This approach should be considered
|
|
a last-resort. If you can't use the built-in connections providers, consider providing your
|
|
own implementation of <literal>org.hibernate.connection.ConnectionProvider</literal>.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Consider using a custom type.</term>
|
|
<listitem>
|
|
<para>
|
|
Suppose you have a Java type, say from some library, that needs to be persisted but doesn't
|
|
provide the accessors needed to map it as a component. You should consider implementing
|
|
<literal>org.hibernate.UserType</literal>. This approach frees the application
|
|
code from implementing transformations to / from a Hibernate type.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Use hand-coded JDBC in bottlenecks.</term>
|
|
<listitem>
|
|
<para>
|
|
In performance-critical areas of the system, some kinds of operations might benefit from
|
|
direct JDBC. But please, wait until you <emphasis>know</emphasis> something is a bottleneck.
|
|
And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might
|
|
be worth opening a Hibernate <literal>Session</literal> and using that JDBC connection. That
|
|
way you can still use the same transaction strategy and underlying connection provider.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Understand <literal>Session</literal> flushing.</term>
|
|
<listitem>
|
|
<para>
|
|
From time to time the Session synchronizes its persistent state with the database. Performance will
|
|
be affected if this process occurs too often. You may sometimes minimize unnecessary flushing by
|
|
disabling automatic flushing or even by changing the order of queries and other operations within a
|
|
particular transaction.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>In a three tiered architecture, consider using detached objects.</term>
|
|
<listitem>
|
|
<para>
|
|
When using a servlet / session bean architecture, you could pass persistent objects loaded in
|
|
the session bean to and from the servlet / JSP layer. Use a new session to service each request.
|
|
Use <literal>Session.merge()</literal> or <literal>Session.saveOrUpdate()</literal> to
|
|
synchronize objects with the database.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>In a two tiered architecture, consider using long persistence contexts.</term>
|
|
<listitem>
|
|
<para>
|
|
Database Transactions have to be as short as possible for best scalability. However, it is often
|
|
neccessary to implement long running <emphasis>application transactions</emphasis>, a single
|
|
unit-of-work from the point of view of a user. An application transaction might span several
|
|
client request/response cycles. It is common to use detached objects to implement application
|
|
transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain
|
|
a single open persistence contact (session) for the whole lifecycle of the application transaction
|
|
and simply disconnect from the JDBC connection at the end of each request and reconnect at the
|
|
beginning of the subsequent request. Never share a single session across more than one application
|
|
transaction, or you will be working with stale data.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Don't treat exceptions as recoverable.</term>
|
|
<listitem>
|
|
<para>
|
|
This is more of a necessary practice than a "best" practice. When an exception occurs, roll back
|
|
the <literal>Transaction</literal> and close the <literal>Session</literal>. If you don't, Hibernate
|
|
can't guarantee that in-memory state accurately represents persistent state. As a special case of this,
|
|
do not use <literal>Session.load()</literal> to determine if an instance with the given identifier
|
|
exists on the database; use <literal>Session.get()</literal> or a query instead.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Prefer lazy fetching for associations.</term>
|
|
<listitem>
|
|
<para>
|
|
Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that
|
|
are not likely to be completely held in the second-level cache. For associations to cached classes,
|
|
where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using
|
|
<literal>lazy="false"</literal>. When an join fetching is appropriate to a particular use
|
|
case, use a query with a <literal>left join fetch</literal>.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>
|
|
Use the <emphasis>open session in view</emphasis> pattern, or a disciplined
|
|
<emphasis>assembly phase</emphasis> to avoid problems with unfetched data.
|
|
</term>
|
|
<listitem>
|
|
<para>
|
|
Hibernate frees the developer from writing tedious <emphasis>Data Transfer Objects</emphasis> (DTO).
|
|
In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem
|
|
that entity beans are not serializable; second, they implicitly define an assembly phase where
|
|
all data to be used by the view is fetched and marshalled into the DTOs before returning control
|
|
to the presentation tier. Hibernate eliminates the first purpose. However, you will still need
|
|
an assembly phase (think of your business methods as having a strict contract with the presentation
|
|
tier about what data is available in the detached objects) unless you are prepared to hold the
|
|
persistence context (the session) open across the view rendering process. This is not a limitation
|
|
of Hibernate! It is a fundamental requirement of safe transactional data access.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Consider abstracting your business logic from Hibernate.</term>
|
|
<listitem>
|
|
<para>
|
|
Hide (Hibernate) data-access code behind an interface. Combine the <emphasis>DAO</emphasis> and
|
|
<emphasis>Thread Local Session</emphasis> patterns. You can even have some classes persisted by
|
|
handcoded JDBC, associated to Hibernate via a <literal>UserType</literal>. (This advice is
|
|
intended for "sufficiently large" applications; it is not appropriate for an application with
|
|
five tables!)
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Don't use exotic association mappings.</term>
|
|
<listitem>
|
|
<para>
|
|
Good usecases for a real many-to-many associations are rare. Most of the time you need
|
|
additional information stored in the "link table". In this case, it is much better to
|
|
use two one-to-many associations to an intermediate link class. In fact, we think that
|
|
most associations are one-to-many and many-to-one, you should be careful when using any
|
|
other association style and ask yourself if it is really neccessary.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Prefer bidirectional associations.</term>
|
|
<listitem>
|
|
<para>
|
|
Unidirectional associations are more difficult to query. In a large application, almost
|
|
all associations must be navigable in both directions in queries.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
|
|
</chapter>
|
|
|