466 lines
17 KiB
XML
466 lines
17 KiB
XML
<chapter id="persistent-classes" revision="2">
|
|
<title>Persistent Classes</title>
|
|
|
|
<para>
|
|
Persistent classes are classes in an application that implement the entities
|
|
of the business problem (e.g. Customer and Order in an E-commerce application).
|
|
Not all instances of a persistent class are considered to be in the persistent
|
|
state - an instance may instead be transient or detached.
|
|
</para>
|
|
|
|
<para>
|
|
Hibernate works best if these classes follow some simple rules, also known
|
|
as the Plain Old Java Object (POJO) programming model. However none of these
|
|
rules are hard requirements. Indeed, Hibernate3 assumes very little about
|
|
the nature of your persistent objects. You may express a domain model in other
|
|
ways: using trees of <literal>Map</literal> instances, for example.
|
|
</para>
|
|
|
|
<sect1 id="persistent-classes-pojo">
|
|
<title>A simple POJO example</title>
|
|
|
|
<para>
|
|
Most Java applications require a persistent class representing felines.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[package eg;
|
|
import java.util.Set;
|
|
import java.util.Date;
|
|
|
|
public class Cat {
|
|
private Long id; // identifier
|
|
|
|
private Date birthdate;
|
|
private Color color;
|
|
private char sex;
|
|
private float weight;
|
|
private int litterId;
|
|
|
|
private Cat mother;
|
|
private Set kittens = new HashSet();
|
|
|
|
private void setId(Long id) {
|
|
this.id=id;
|
|
}
|
|
public Long getId() {
|
|
return id;
|
|
}
|
|
|
|
void setBirthdate(Date date) {
|
|
birthdate = date;
|
|
}
|
|
public Date getBirthdate() {
|
|
return birthdate;
|
|
}
|
|
|
|
void setWeight(float weight) {
|
|
this.weight = weight;
|
|
}
|
|
public float getWeight() {
|
|
return weight;
|
|
}
|
|
|
|
public Color getColor() {
|
|
return color;
|
|
}
|
|
void setColor(Color color) {
|
|
this.color = color;
|
|
}
|
|
|
|
void setSex(char sex) {
|
|
this.sex=sex;
|
|
}
|
|
public char getSex() {
|
|
return sex;
|
|
}
|
|
|
|
void setLitterId(int id) {
|
|
this.litterId = id;
|
|
}
|
|
public int getLitterId() {
|
|
return litterId;
|
|
}
|
|
|
|
void setMother(Cat mother) {
|
|
this.mother = mother;
|
|
}
|
|
public Cat getMother() {
|
|
return mother;
|
|
}
|
|
void setKittens(Set kittens) {
|
|
this.kittens = kittens;
|
|
}
|
|
public Set getKittens() {
|
|
return kittens;
|
|
}
|
|
|
|
// addKitten not needed by Hibernate
|
|
public void addKitten(Cat kitten) {
|
|
kitten.setMother(this);
|
|
kitten.setLitterId( kittens.size() );
|
|
kittens.add(kitten);
|
|
}
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
There are four main rules to follow here:
|
|
</para>
|
|
|
|
|
|
<sect2 id="persistent-classes-pojo-accessors" revision="1">
|
|
<title>Declare accessors and mutators for persistent fields</title>
|
|
|
|
<para>
|
|
<literal>Cat</literal> declares accessor methods for all its persistent fields.
|
|
Many other ORM tools directly persist instance variables. We believe
|
|
it is far better to decouple this implementation detail from the persistence
|
|
mechanism. Hibernate persists JavaBeans style properties, and recognizes method
|
|
names of the form <literal>getFoo</literal>, <literal>isFoo</literal> and
|
|
<literal>setFoo</literal>. You may however switch to direct field access for
|
|
particular properties, if needed.
|
|
</para>
|
|
|
|
<para>
|
|
Properties need <emphasis>not</emphasis> be declared public - Hibernate can
|
|
persist a property with a default, <literal>protected</literal> or <literal>
|
|
private</literal> get / set pair.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="persistent-classes-pojo-constructor" revision="1">
|
|
<title>Implement a no-argument constructor</title>
|
|
|
|
<para>
|
|
<literal>Cat</literal> has a no-argument constructor. All
|
|
persistent classes must have a default constructor (which may be non-public) so
|
|
Hibernate can instantiate them using <literal>Constructor.newInstance()</literal>.
|
|
We recommend having a constructor with at least <emphasis>package</emphasis>
|
|
visibility for runtime proxy generation in Hibernate.
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 id="persistent-classes-pojo-identifier" revision="2">
|
|
<title>Provide an identifier property (optional)</title>
|
|
|
|
<para>
|
|
<literal>Cat</literal> has a property called <literal>id</literal>. This property
|
|
maps to the primary key column of a database table. The property might have been called
|
|
anything, and its type might have been any primitive type, any primitive "wrapper"
|
|
type, <literal>java.lang.String</literal> or <literal>java.util.Date</literal>. (If
|
|
your legacy database table has composite keys, you can even use a user-defined class
|
|
with properties of these types - see the section on composite identifiers later.)
|
|
</para>
|
|
|
|
<para>
|
|
The identifier property is strictly optional. You can leave them off and let Hibernate
|
|
keep track of object identifiers internally. We do not recommend this, however.
|
|
</para>
|
|
|
|
<para>
|
|
In fact, some functionality is available only to classes which declare an
|
|
identifier property:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
Transitive reattachment for detached objects (cascade update or cascade
|
|
merge) - see <xref linkend="objectstate-transitive"/>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>Session.saveOrUpdate()</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>Session.merge()</literal>
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
We recommend you declare consistently-named identifier properties on persistent
|
|
classes. We further recommend that you use a nullable (ie. non-primitive) type.
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 id="persistent-classes-pojo-final">
|
|
<title>Prefer non-final classes (optional)</title>
|
|
<para>
|
|
A central feature of Hibernate, <emphasis>proxies</emphasis>, depends upon the
|
|
persistent class being either non-final, or the implementation of an interface
|
|
that declares all public methods.
|
|
</para>
|
|
<para>
|
|
You can persist <literal>final</literal> classes that do not implement an interface
|
|
with Hibernate, but you won't be able to use proxies for lazy association fetching -
|
|
which will limit your options for performance tuning.
|
|
</para>
|
|
<para>
|
|
You should also avoid declaring <literal>public final</literal> methods on the
|
|
non-final classes. If you want to use a class with a <literal>public final</literal>
|
|
method, you must explicitly disable proying by setting <literal>lazy="false"</literal>.
|
|
</para>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="persistent-classes-inheritance">
|
|
<title>Implementing inheritance</title>
|
|
|
|
<para>
|
|
A subclass must also observe the first and second rules. It inherits its
|
|
identifier property from the superclass, <literal>Cat</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[package eg;
|
|
|
|
public class DomesticCat extends Cat {
|
|
private String name;
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
protected void setName(String name) {
|
|
this.name=name;
|
|
}
|
|
}]]></programlisting>
|
|
</sect1>
|
|
|
|
<sect1 id="persistent-classes-equalshashcode" revision="1">
|
|
<title>Implementing <literal>equals()</literal> and <literal>hashCode()</literal></title>
|
|
|
|
<para>
|
|
You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
|
|
methods if you
|
|
</para>
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
intend to put instances of persistent classes in a <literal>Set</literal>
|
|
(the recommended way to represent many-valued associations)
|
|
<emphasis>and</emphasis>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
intend to use reattachment of detached instances
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
Hibernate guarantees equivalence of persistent identity (database row) and Java identity
|
|
only inside a particular session scope. So as soon as we mix instances retrieved in
|
|
different sessions, we must implement <literal>equals()</literal> and
|
|
<literal>hashCode()</literal> if we wish to have meaningful semantics for
|
|
<literal>Set</literal>s.
|
|
</para>
|
|
|
|
<para>
|
|
The most obvious way is to implement <literal>equals()</literal>/<literal>hashCode()</literal>
|
|
by comparing the identifier value of both objects. If the value is the same, both must
|
|
be the same database row, they are therefore equal (if both are added to a <literal>Set</literal>,
|
|
we will only have one element in the <literal>Set</literal>). Unfortunately, we can't use that
|
|
approach with generated identifiers! Hibernate will only assign identifier values to objects
|
|
that are persistent, a newly created instance will not have any identifier value! Furthermore,
|
|
if an instance is unsaved and currently in a <literal>Set</literal>, saving it will assign
|
|
an identifier value to the object. If <literal>equals()</literal> and <literal>hashCode()</literal>
|
|
are based on the identifier value, the hash code would change, breaking the contract of the
|
|
<literal>Set</literal>. See the Hibernate website for a full discussion of this problem. Note
|
|
that this is not a Hibernate issue, but normal Java semantics of object identity and equality.
|
|
</para>
|
|
|
|
<para>
|
|
We recommend implementing <literal>equals()</literal> and <literal>hashCode()</literal>
|
|
using <emphasis>Business key equality</emphasis>. Business key equality means that the
|
|
<literal>equals()</literal> method compares only the properties that form the business
|
|
key, a key that would identify our instance in the real world (a
|
|
<emphasis>natural</emphasis> candidate key):
|
|
</para>
|
|
|
|
<programlisting><![CDATA[public class Cat {
|
|
|
|
...
|
|
public boolean equals(Object other) {
|
|
if (this == other) return true;
|
|
if ( !(other instanceof Cat) ) return false;
|
|
|
|
final Cat cat = (Cat) other;
|
|
|
|
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
|
|
if ( !cat.getMother().equals( getMother() ) ) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public int hashCode() {
|
|
int result;
|
|
result = getMother().hashCode();
|
|
result = 29 * result + getLitterId();
|
|
return result;
|
|
}
|
|
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
Note that a business key does not have to be as solid as a database
|
|
primary key candidate (see <xref linkend="transactions-basics-identity"/>).
|
|
Immutable or unique properties are usually good
|
|
candidates for a business key.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="persistent-classes-dynamicmodels">
|
|
<title>Dynamic models</title>
|
|
|
|
<para>
|
|
<emphasis>Note that the following features are currently considered
|
|
experimental and may change in the near future.</emphasis>
|
|
</para>
|
|
|
|
<para>
|
|
Persistent entities don't necessarily have to be represented as POJO classes
|
|
or as JavaBean objects at runtime. Hibernate also supports dynamic models
|
|
(using <literal>Map</literal>s of <literal>Map</literal>s at runtime) and the
|
|
representation of entities as DOM4J trees. With this approach, you don't
|
|
write persistent classes, only mapping files.
|
|
</para>
|
|
|
|
<para>
|
|
By default, Hibernate works in normal POJO mode. You may set a default entity
|
|
representation mode for a particular <literal>SessionFactory</literal> using the
|
|
<literal>default_entity_mode</literal> configuration option (see
|
|
<xref linkend="configuration-optional-properties"/>.
|
|
</para>
|
|
|
|
<para>
|
|
The following examples demonstrates the representation using <literal>Map</literal>s.
|
|
First, in the mapping file, an <literal>entity-name</literal> has to be declared
|
|
instead of (or in addition to) a class name:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<hibernate-mapping>
|
|
|
|
<class entity-name="Customer">
|
|
|
|
<id name="id"
|
|
type="long"
|
|
column="ID">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
|
|
<property name="name"
|
|
column="NAME"
|
|
type="string"/>
|
|
|
|
<property name="address"
|
|
column="ADDRESS"
|
|
type="string"/>
|
|
|
|
<many-to-one name="organization"
|
|
column="ORGANIZATION_ID"
|
|
class="Organization"/>
|
|
|
|
<bag name="orders"
|
|
inverse="true"
|
|
lazy="false"
|
|
cascade="all">
|
|
<key column="CUSTOMER_ID"/>
|
|
<one-to-many class="Order"/>
|
|
</bag>
|
|
|
|
</class>
|
|
|
|
</hibernate-mapping>]]></programlisting>
|
|
|
|
<para>
|
|
|
|
Note that even though associations are declared using target class names,
|
|
the target type of an associations may also be a dynamic entity instead
|
|
of a POJO.
|
|
</para>
|
|
|
|
<para>
|
|
After setting the default entity mode to <literal>dynamic-map</literal>
|
|
for the <literal>SessionFactory</literal>, we can at runtime work with
|
|
<literal>Map</literal>s of <literal>Map</literal>s:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[Session s = openSession();
|
|
Transaction tx = s.beginTransaction();
|
|
Session s = openSession();
|
|
|
|
// Create a customer
|
|
Map david = new HashMap();
|
|
david.put("name", "David");
|
|
|
|
// Create an organization
|
|
Map foobar = new HashMap();
|
|
foobar.put("name", "Foobar Inc.");
|
|
|
|
// Link both
|
|
david.put("organization", foobar);
|
|
|
|
// Save both
|
|
s.save("Customer", david);
|
|
s.save("Organization", foobar);
|
|
|
|
tx.commit();
|
|
s.close();]]></programlisting>
|
|
|
|
<para>
|
|
The advantages of a dynamic mapping are quick turnaround time for prototyping
|
|
without the need for entity class implementation. However, you lose compile-time
|
|
type checking and will very likely deal with many exceptions at runtime. Thanks
|
|
to the Hibernate mapping, the database schema can easily be normalized and sound,
|
|
allowing to add a proper domain model implementation on top later on.
|
|
</para>
|
|
|
|
<para>
|
|
Entity representation modes can also be set on a per <literal>Session</literal>
|
|
basis:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
|
|
|
|
// Create a customer
|
|
Map david = new HashMap();
|
|
david.put("name", "David");
|
|
dynamicSession.save("Customer", david);
|
|
...
|
|
dynamicSession.flush();
|
|
dynamicSession.close()
|
|
...
|
|
// Continue on pojoSession
|
|
]]></programlisting>
|
|
|
|
|
|
<para>
|
|
Please note that the call to <literal>getSession()</literal> using an
|
|
<literal>EntityMode</literal> is on the <literal>Session</literal> API, not the
|
|
<literal>SessionFactory</literal>. That way, the new <literal>Session</literal>
|
|
shares the underlying JDBC connection, transaction, and other context
|
|
information. This means you don't have tocall <literal>flush()</literal>
|
|
and <literal>close()</literal> on the secondary <literal>Session</literal>, and
|
|
also leave the transaction and connection handling to the primary unit of work.
|
|
</para>
|
|
|
|
<para>
|
|
More information about the XML representation capabilities can be found
|
|
in <xref linkend="xml"/>.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<para>
|
|
TODO: Document user-extension framework in the property and proxy packages
|
|
</para>
|
|
|
|
</chapter>
|
|
|