hibernate-orm/reference/en/modules/best_practices.xml

224 lines
12 KiB
XML
Raw Normal View History

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