Hibernate.orgCommunity Documentation
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). The term "persistent" here means that the classes are able to be persisted, not that they are in the persistent state (see Section 11.1, “Hibernate object states” for discussion).
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, Hibernate assumes very little
about the nature of your persistent objects. You can express a domain model in other ways (using trees of
java.util.Map
instances, for example).
The four main rules of persistent classes are explored in more detail in the following sections.
Cat
has a property named id
. This property maps to the
primary key column(s) of the underlying database table. The type of the identifier property can
be any "basic" type (see ???). See Section 9.4, “Components as composite identifiers”
for information on mapping composite (multi-column) identifiers.
Identifiers do not necessarily need to identify column(s) in the database physically defined as a primary key. They should just identify columns that can be used to uniquely identify rows in the underlying table.
We recommend that you declare consistently-named identifier properties on persistent classes and that you use a nullable (i.e., non-primitive) type.
A central feature of Hibernate, proxies (lazy loading), 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; you will not, however, be able to use proxies for lazy association fetching which will
ultimately limit your options for performance tuning. To persist a final
class which does not implement a "full" interface you must disable proxy generation. See
Example 4.2, “Disabling proxies in hbm.xml” and
Example 4.3, “Disabling proxies in annotations”.
If the final
class does implement a proper interface, you could alternatively tell
Hibernate to use the interface instead when generating the proxies. See
Example 4.4, “Proxying an interface in hbm.xml” and
Example 4.5, “Proxying an interface in annotations”.
Example 4.5. Proxying an interface in annotations
@Entity @Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
You should also avoid declaring public final
methods as this will again limit
the ability to generate proxies from this class. If you want to use a
class with public final
methods, you must explicitly disable proxying. Again, see
Example 4.2, “Disabling proxies in hbm.xml” and
Example 4.3, “Disabling proxies in annotations”.
You have to override the equals()
and
hashCode()
methods if you:
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;
}
}
A business key does not have to be as solid as a database primary key candidate (see Section 13.1.3, “Considering object identity”). Immutable or unique properties are usually good candidates for a business key.
By default, Hibernate works in normal POJO mode. You can set a
default entity representation mode for a particular
SessionFactory
using the
default_entity_mode
configuration option (see Table 3.3, “Hibernate Configuration Properties”).
The following examples demonstrate the representation using
Map
s. First, in the mapping file an
entity-name
has to be declared instead of, or in
addition to, a class name:
<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>
Even though associations are declared using target class names, the target type of associations can also be a dynamic entity instead of a POJO.
After setting the default entity mode to
dynamic-map
for the SessionFactory
,
you can, at runtime, work with Map
s of
Map
s:
Session s = openSession();
Transaction tx = s.beginTransaction();
// 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();
One of the main advantages of dynamic mapping is quick turnaround time for prototyping, without the need for entity class implementation. However, you lose compile-time type checking and will likely deal with many exceptions at runtime. As a result of 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:
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
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 do not have to
call 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 Chapter 20, XML Mapping.
There are two (high-level) types of Tuplizers:
Users can also plug in their own tuplizers. Perhaps you require that
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. Tuplizer definitions are attached to the entity or component
mapping they are meant to manage. Going back to the example of our Customer
entity,
Example 4.6, “Specify custom tuplizers in annotations” shows how to specify a custom
org.hibernate.tuple.entity.EntityTuplizer
using annotations while
Example 4.7, “Specify custom tuplizers in hbm.xml” shows how to do the same in hbm.xml
Example 4.7. Specify custom tuplizers in hbm.xml
<hibernate-mapping>
<class entity-name="Customer">
<!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<!-- other properties -->
...
</class>
</hibernate-mapping>
/**
* A very trivial JDK Proxy InvocationHandler implementation where we proxy an
* interface as the domain model and simply store persistent state in an internal
* Map. This is an extremely trivial example meant only for illustration.
*/
public final class DataProxyHandler implements InvocationHandler {
private String entityName;
private HashMap data = new HashMap();
public DataProxyHandler(String entityName, Serializable id) {
this.entityName = entityName;
data.put( "Id", id );
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ( methodName.startsWith( "set" ) ) {
String propertyName = methodName.substring( 3 );
data.put( propertyName, args[0] );
}
else if ( methodName.startsWith( "get" ) ) {
String propertyName = methodName.substring( 3 );
return data.get( propertyName );
}
else if ( "toString".equals( methodName ) ) {
return entityName + "#" + data.get( "Id" );
}
else if ( "hashCode".equals( methodName ) ) {
return new Integer( this.hashCode() );
}
return null;
}
public String getEntityName() {
return entityName;
}
public HashMap getData() {
return data;
}
}
public class ProxyHelper {
public static String extractEntityName(Object object) {
// Our custom java.lang.reflect.Proxy instances actually bundle
// their appropriate entity name, so we simply extract it from there
// if this represents one of our proxies; otherwise, we return null
if ( Proxy.isProxyClass( object.getClass() ) ) {
InvocationHandler handler = Proxy.getInvocationHandler( object );
if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
DataProxyHandler myHandler = ( DataProxyHandler ) handler;
return myHandler.getEntityName();
}
}
return null;
}
// various other utility methods ....
}
/**
* The EntityNameResolver implementation.
*
* IMPL NOTE : An EntityNameResolver really defines a strategy for how entity names
* should be resolved. Since this particular impl can handle resolution for all of our
* entities we want to take advantage of the fact that SessionFactoryImpl keeps these
* in a Set so that we only ever have one instance registered. Why? Well, when it
* comes time to resolve an entity name, Hibernate must iterate over all the registered
* resolvers. So keeping that number down helps that process be as speedy as possible.
* Hence the equals and hashCode implementations as is
*/
public class MyEntityNameResolver implements EntityNameResolver {
public static final MyEntityNameResolver INSTANCE = new MyEntityNameResolver();
public String resolveEntityName(Object entity) {
return ProxyHelper.extractEntityName( entity );
}
public boolean equals(Object obj) {
return getClass().equals( obj.getClass() );
}
public int hashCode() {
return getClass().hashCode();
}
}
public class MyEntityTuplizer extends PojoEntityTuplizer {
public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super( entityMetamodel, mappedEntity );
}
public EntityNameResolver[] getEntityNameResolvers() {
return new EntityNameResolver[] { MyEntityNameResolver.INSTANCE };
}
public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
String entityName = ProxyHelper.extractEntityName( entityInstance );
if ( entityName == null ) {
entityName = super.determineConcreteSubclassEntityName( entityInstance, factory );
}
return entityName;
}
...
In order to register an org.hibernate.EntityNameResolver
users must either:
Implement a custom tuplizer (see Section 4.5, “Tuplizers”), implementing
the getEntityNameResolvers
method
Register it with the org.hibernate.impl.SessionFactoryImpl
(which is the
implementation class for org.hibernate.SessionFactory
) using the
registerEntityNameResolver
method.
Copyright © 2004 Red Hat, Inc.