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).
Not all instances of a persistent class are considered to be in the persistent
state - an instance may instead be transient or detached.
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 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:
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 that Hibernate can instantiate
them using Constructor.newInstance(). We strongly recommend having a
default 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
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, 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 strictly optional. You can leave them off and let Hibernate
keep track of object identifiers internally. We do not recommend this, however.
In fact, some functionality is available only to classes which declare an
identifier property:
Transitive reattachment for detached objects (cascade update or cascade
merge) - see
Session.saveOrUpdate()
Session.merge()
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 association fetching -
which will limit your options for performance tuning.
You should also avoid declaring public final methods on the
non-final classes. If you want to use a class with a public final
method, you must explicitly disable proying by setting lazy="false".
Declare accessors and mutators for persistent fields (optional)
Cat declares accessor methods for all its persistent fields.
Many other ORM tools directly persist instance variables. We believe it is
better to provide an indirection between the relational schema and internal
data structures of the class. By default, Hibernate persists JavaBeans style
properties, and recognizes method names of the form getFoo,
isFoo and setFoo. You may 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.
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! Furthermore,
if an instance is unsaved and currently in a Set, saving it will assign
an identifier value to the object. If equals() and hashCode()
are based on the identifier value, the hash code would change, breaking the contract of the
Set. 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.
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):
Note that a business key does not have to be as solid as a database
primary key candidate (see ).
Immutable or unique properties are usually good
candidates for a business key.
Dynamic models
Note that the following features are currently considered
experimental and may change in the near future.
Persistent entities don't necessarily have to be represented as POJO classes
or as JavaBean objects at runtime. Hibernate also supports dynamic models
(using Maps of Maps at runtime) and the
representation of entities as DOM4J trees. With this approach, you don't
write persistent classes, only mapping files.
By default, Hibernate works in normal POJO mode. You may set a default entity
representation mode for a particular SessionFactory using the
default_entity_mode configuration option (see
.
The following examples demonstrates the representation using Maps.
First, in the mapping file, an entity-name has to be declared
instead of (or in addition to) a class name:
]]>
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.
After setting the default entity mode to dynamic-map
for the SessionFactory, we can at runtime work with
Maps of Maps:
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.
Entity representation modes can also be set on a per Session
basis:
Please note that the call to getSession() using an
EntityMode is on the Session API, not the
SessionFactory. That way, the new Session
shares the underlying JDBC connection, transaction, and other context
information. This means you don't have tocall flush()
and close() on the secondary Session, and
also leave the transaction and connection handling to the primary unit of work.
More information about the XML representation capabilities can be found
in .
Tuplizers
org.hibernate.tuple.Tuplizer, and its sub-interfaces, are responsible
for managing a particular representation of a piece of data, given that representation's
org.hibernate.EntityMode. If a given piece of data is thought of as
a data structure, then a tuplizer is the thing which knows how to create such a data structure
and how to extract values from and inject values into such a data structure. For example,
for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its
constructor and how to access the POJO properties using the defined property accessors.
There are two high-level types of Tuplizers, represented by the
org.hibernate.tuple.EntityTuplizer and org.hibernate.tuple.ComponentTuplizer
interfaces. EntityTuplizers are responsible for managing the above mentioned
contracts in regards to entities, while ComponentTuplizers do the same for
components.
Users may also plug in their own tuplizers. Perhaps you require that a java.util.Map
implementation other than java.util.HashMap be used while in the
dynamic-map entity-mode; or perhaps you need to define a different proxy generation strategy
than the one used by default. Both would be achieved by defining a custom tuplizer
implementation. Tuplizers definitions are attached to the entity or component mapping they
are meant to manage. Going back to the example of our customer entity:
...
public class CustomMapTuplizerImpl
extends org.hibernate.tuple.DynamicMapEntityTuplizer {
// override the buildInstantiator() method to plug in our custom map...
protected final Instantiator buildInstantiator(
org.hibernate.mapping.PersistentClass mappingInfo) {
return new CustomMapInstantiator( mappingInfo );
}
private static final class CustomMapInstantiator
extends org.hibernate.tuple.DynamicMapInstantitor {
// override the generateMap() method to return our custom map...
protected final Map generateMap() {
return new CustomMap();
}
}
}]]>
TODO: Document user-extension framework in the property and proxy packages