Persistent Classes
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).
Persistent classes have, as the name implies, transient and also persistent
instance stored in the database.
Hibernate works best if these classes follow some simple rules, also known
as the Plain Old Java Object (POJO) programming model. However, Hibernate3
allows you to express a domain model in other ways: using trees of
Map instances, for example.
A simple POJO example
Most Java applications require a persistent class representing felines.
There are four main rules to follow here:
Declare accessors and mutators for persistent fields
Cat 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 getFoo, isFoo and
setFoo. You may however switch to direct field access for
particular properties, if needed.
Properties need not be declared public - Hibernate can
persist a property with a default, protected or
private get / set pair.
Implement a no-argument constructor
Cat has a no-argument constructor. All
persistent classes must have a default constructor (which may be non-public) so
Hibernate can instantiate them using Constructor.newInstance().
We recommend having a constructor with at least package
visibility for runtime proxy generation in Hibernate.
Provide an identifier property (optional)
Cat has a property called id. This property
holds 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, java.lang.String or java.util.Date. (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.)
The identifier property is optional. You can leave it off and let Hibernate keep track
of object identifiers internally. However, for many applications it is still
a good (and very popular) design decision.
What's more, some functionality is available only to classes which declare an
identifier property:
Transitive reattachment for detached objects (cascade update) -
see "Lifecycle Objects"
Session.saveOrUpdate()
We recommend you declare consistently-named identifier properties on persistent
classes. We further recommend that you use a nullable (ie. non-primitive) type.
Prefer non-final classes (optional)
A central feature of Hibernate, proxies, depends upon the
persistent class being either non-final, or the implementation of an interface
that declares all public methods.
You can persist final classes that do not implement an interface
with Hibernate, but you won't be able to use proxies for lazy associationfetching -
which will limit your options for performance tuning.
Implementing inheritance
A subclass must also observe the first and second rules. It inherits its
identifier property from the superclass, Cat.
Implementing equals() and hashCode()
You have to override the equals() and hashCode()
methods if you
intend to put instances of persistent classes in a Set
(the recommended way to represent many-valued associations)
and
intend to use reattachment of detached instances
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 equals() and
hashCode() if we wish to have meaningful semantics for
Sets.
The most obvious way is to implement equals()/hashCode()
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 Set,
we will only have one element in the Set). 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! We recommend
implementing equals() and hashCode() using
Business key equality.
Business key equality means that the equals()
method compares only the properties that form the business key, a key that would
identify our instance in the real world (a natural candidate key):
Dynamic models
Hibernate also supports dynamic domain models, using Maps of
Maps. With this approach, you don't write persistent classes,
a Hibernate mapping file for each "entity" is sufficient:
]]>
At runtime, you just instantiate HashMaps and use
the Hibernate entity name to refer to a particular type.
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.
TODO: Document user-extension framework in the property and proxy package