Professional Documents
Culture Documents
Learn how to migrate Hibernate application source code, object-relational mappings, and
configuration parameters to OpenJPA by comparing the features and functions in Hibernate
applications using EJB 2.1 with equivalent capabilities in OpenJPA and EJB 3.0.
Introduction
Hibernate is an open source persistence and query framework that provides object-relational
mapping (ORM) of plain old Java™ objects (POJOs) to relational database tables, as well as
data query and retrieval capabilities. The Apache OpenJPA project provides a similar open
source persistence and query framework for POJO entities as defined by the EJB 3.0 Java
Persistence API specification. This article examines common Hibernate scenarios with Enterprise
JavaBeans™ (EJB) 2.1 and compares them to an equivalent scenario implemented in OpenJPA
with EJB 3.0. Specifically, you will be able to view side by side Hibernate application source code,
object-relational mappings, and configuration parameters, and compare them to the equivalent
OpenJPA source code, mappings, and configurations. The comparisons shown here will not only
enable you to see how to make these changes, but also show you how relatively straightforward it
is to migrate legacy Hibernate applications that use these common scenarios to OpenJPA.
Although the focus of this article is on migrating legacy Hibernate applications to OpenJPA, you will
also find value here if you are knowledgeable with Hibernate and want to come up to speed with
the new JPA specification quickly, and use the OpenJPA persistence provider for new application
development.
This article assumes that you're familiar with the basic concepts of Hibernate and focuses
specifically on the Hibernate 3.0 implementation. All of the examples in this article have been run
with Hibernate 3 with EJB 2.1, and with OpenJPA 0.9.7 using IBM® WebSphere® Application
Server V6.1 Feature Pack for EJB 3.0.
There are many reasons to migrate legacy Hibernate applications to OpenJPA. For instance,
Hibernate is a non-standard, object-relational mapping and persistence management solution.
Hibernate 3 requires JDK 1.3.1 or higher. By contrast, OpenJPA implements the JPA specification,
which is a core part of the Java 5 specification, and on top of which the WebSphere Application
Server V6.1 Feature Pack for EJB 3.0 implementation is based. See Related topics for more
information on these products.
For the purpose of this article, JPA refers to the specification, and OpenJPA refers to an
implementation of the JPA specification.
This article does not cover all Hibernate features and functions, but it does cover best practices
that are frequently encountered in the field.
The JPA is usable in both Java Platform, Standard Edition (Java SE) and Enterprise Edition
(Java EE) environments because it represents entities as POJOs that can be managed by a JPA
persistence provider; such as OpenJPA. Metadata about the entity's object-relational mapping is
specified using Java 5 annotations or in XML descriptors. Entities are used to persist Java objects
to the database.
There are many JPA persistence providers. IBM's implementation of the JPA specification is
based upon the Apache OpenJPA project. With the release of these JPA persistence providers,
customers can now code to a standard API and do not have to decide between incompatible non-
standard persistence providers.
To assist you in the migration of legacy Hibernate applications to OpenJPA, this section compares
the Hibernate non-standard APIs that are commonly used with the equivalent OpenJPA standard
APIs. This section compares the classes and interfaces that are used, and then compares the
APIs via common usage scenarios.
The table below compares the Hibernate classes that are generally used with their equivalent
classes in OpenJPA. All of the Hibernate classes are in the org.hibernate package. All of the
JPA interfaces (and the Persistence class) are in the javax.persistence package. The OpenJPA
implementations of the JPA interfaces are in the org.apache.openjpa.* packages.
2. Runtime configuration
• Hibernate conventions
In Hibernate, runtime configuration maps as follows:
• Use static SessionFactory variable.
• Use Configuration#configure() method.
• Use Configuration#buildSessionFactory() method.
With legacy Hibernate applications, you will generally find a single static SessionFactory
instance that is shared by all threads that are servicing client requests in the JVM. Hibernate
can also create multiple SessionFactory instances, but this is seldom done in practice.
There are several ways to configure the SessionFactory in Hibernate. The most common
scenario is to call the configure() method. If you don't pass in a name to configure(), then it will
look for hibernate.cfg.xml in the root directory on the classpath. If you pass in the name of the
XML configuration file, it will look for that on the classpath.
Once you've found the XML configuration file, the buildSessionFactory() method creates and
initializes the SessionFactory using the metadata from that configuration file.
Some things to keep in mind:
• Instead of using a static variable, some applications lookup the SessionFactory from
the JNDI registry, but you still have to call configure and buildSessionFactory on the
first lookup, so there is little to be gained and as such the static variable is the more
commonly used idiom.
• Instead of using the configure() method to read the Hibernate configuration
parameters from a file, you can also configure them programmatically using the
Configuration#setProperties() method, but the better and more frequently used approach
is to externalize the Hibernate properties.
Like Hibernate, OpenJPA can also pass in additional properties when it creates the entity
manager factory, but externalizing the properties into a named persistence unit in an XML file
is the more common idiom.
• OpenJPA conventions
In OpenJPA, equivalent runtime configuration maps as follows:
• Use static EntityManagerFactory variable.
• Use Persistence#createEntityManagerFactory()
Like Hibernate, the static EntityManagerFactory instance can be used by all threads that are
servicing client requests in the JVM. You can also define a static Map if multiple instances are
required.
The createEntityManagerFactory() looks for persistence.xml in the META-INF folder on the
classpath that has a persistence unit with the same name as the one that was specified in
the method call. If persistence.xml is found with a persistence unit that matches the given
name, then createEntityManagerFactory() configures an EntityManagerFactory instance
with the metadata from that file. If no persistence.xml is found with a matching name, then a
javax.persistence.PersistenceException is raised.
3. Session management
Generally, applications will obtain a session from the SessionFactory when it receives a client
request and will close the session at the end of the request, where the request can be an
HttpRequest, or a call on a stateless session bean, and so on. Sessions provide methods to work
with transactions and to load entities from (and store entities to) the database.
Hibernate applications generally manage the session. In doing so, they will often associate the
session with thread local storage so that the session doesn't need to be passed as a parameter
to all of the methods that require access to it; instead, they retrieve it from thread local storage.
Hibernate 3.0.1 also provides a getCurrentSession(), but you will often find explicit session
management.
In terms of exceptions, Hibernate 3.0 throws unchecked or runtime exceptions (the same is true
for OpenJPA exceptions), which means that most applications won't throw Hibernate exceptions in
their method signature; neither will they catch and handle Hibernate exceptions in their methods.
Of course, you can still catch and handle them if you so desire.
You will also generally find that most of the existing legacy Hibernate applications have been
implemented using Java SE 1.4 while OpenJPA applications are implemented using Java SE 5.
The examples below use the getSessionFactory() helper method to get the session factory (or
entity manager factory) that is needed to create/open a session (or entity manager). (See Runtime
Configuration for more on the getSessionFactory() method.)
• Hibernate conventions
In Hibernate, session management maps as follows:
• Use ThreadLocal to get the current session.
• Use SessionFactory#openSession() to open the session.
• Use Session#isOpen() and Session#close() to close the session.
4. Transaction management
Hibernate applications can run in different environments with different transaction strategies.
Applications can run in environments using local JDBC or global Java Transaction API (JTA)
transactions.
The use of local JDBC transactions is the most common scenario. JTA transactions are useful if
you have disparate data stores, such as a database and a message queue; JTA lets you to treat
them as a single transaction.
Hibernate applications manage their own transactions by calling the transaction APIs. The
transaction strategy that you use (either JDBC or JTA) is set in the Hibernate configuration file, so
it doesn't matter to the application.
• Hibernate conventions
In Hibernate, transaction management maps as follows:
• Use Session#beginTransaction() to begin the transaction.
• Use Transaction#commit() to commit the transaction.
• Use Transaction#isActive and Transaction#rollback() to rollback the transaction.
Listing 5. Hibernate transaction management
public class ORMHelper {
5. Entity management
Common scenarios for entity management include:
Generally, these scenarios map to individual use case operations and are executed in separate
transactions using detached entities, but they can also be used with connected entities.
• Hibernate conventions
In Hibernate, entity management maps as follows:
• Use Session#save to make a transient object persistent.
• Use Session#load to retrieve a persistent object.
• Use Session#update to update the persistent state of a detached object. (If you modify
the state of a persistent (connected) object, there's no need to call update; Hibernate
automatically propagates the update to the database when commit() is called.)
• Use Session#delete to make a detached or persistent object transient.
In this example, the signature of the ORMHelper#update() method was changed because
the Hibernate update() method copies the newly managed persistent state onto the detached
(or transient) object that was passed into the method, so it doesn't have a return value.
Conversely, in the OpenJPA merge() method, the original detached (transient) object is left
unchanged and the return value has the newly managed persistent state.
In addition to changing the ORMHelper#update signature, the legacy applications that were
calling it also had to be changed to explicitly assign the return value to the original transient
object.
6. Detached entities
Another common scenario in Hibernate applications that are based upon layered architectures is
using a stateless session EJB as a session facade to return "detached" entities to the Web tier.
In this scenario, you will find that transactions are started and stopped by the session EJBs in
response to invocations from the Web tier.
The benefits of this pattern for layered architectures is that since the EJB tier starts and stops
transactions per user interaction, transactions will never be kept open while a user performs some
work. Hence, all of the transactions will be short-lived and should complete within seconds.
Most existing Hibernate applications use EJB 2.1 to implement the session EJBs, while most
OpenJPA applications will use EJB 3.0. Initially you should migrate to EJB 3.0 session beans using
resource local entity manager, as that will not require changes to the transaction logic, but you
can also use OpenJPA from an EJB 2.1 session bean (see Leveraging OpenJPA with WebSphere
Application Server V6.1). Once the migration is complete, you should consider refactoring the
application to EJB 3.0 using a JTA entity manager.
One additional note on detached entities: if you retrieve an object in one transaction then modify
that detached object outside the transaction, you will then have to call update on that object to
save it back to the database. This is the common programming idiom in Hibernate as well as
OpenJPA. Similarly, if you retrieve an object and modify that object in the same transaction, there's
no need to call update on the object to save it to the database; once you commit the transaction,
that object will be written to the database automatically. This, too, is the common programming
idiom for Hibernate and OpenJPA.
• Hibernate conventions
In Hibernate, detached entities with EJB 2.1 maps as follows:
• Use the session facade pattern to wrap entities.
• Return detached entities (POJOs) to the Web tier.
ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
ORMHelper.delete(customerEntity);
ORMHelper.commitTransaction();
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
Throw ex;
} finally {
ORMHelper.closeSession();
}
}
...
}
• OpenJPA conventions
In OpenJPA, detached entities with EJB 3.0 maps as follows:
• Use the session facade pattern to wrap entities.
• Return detached entities (POJOs) to the Web tier.
return customerEntity;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}
The changes that had to be made for migrating are illustrated in Listing 10.
In comparison, the EJB 3.0 SessionFacade class does not implement the SessionBean class,
nor does it implement the SessionBean's callback methods (setSessionContext, ejbCreate,
ejbRemove, ejbActivate, and ejbPassivate). In addition, the component interfaces, home
interfaces, and deployment descriptors are also not required in EJB 3.0. The values specified
in the EJB 2.1 deployment descriptor are included in the EJB 3.0 SessionFacade class with
Java 5 annotations.
That said, there were no changes to the SessionFacade's business methods, which is where
you would expect to see the bulk of the calls to the Hibernate or OpenJPA APIs. This is
because we encapsulated most of the Hibernate/OpenJPA APIs inside a helper class and the
session bean used the helper class. Of course, your application may not have been structured
to encapsulate Hibernate/OpenJPA APIs inside a helper class, but if you compare the side-
by-side changes that were made to the helper class in the previous sections, then you should
be able to determine the necessary changes to the EJB session bean for session, transaction,
and entity management.
OpenJPA object-relational mappings can be defined in a set of XML mapping files, or they can be
defined via Java 5 annotations embedded directly in your code, which totally eliminates the need
for the mapping files.
Most of the legacy Hibernate applications use XML mapping files, while most OpenJPA
applications use Java 5 annotations in development, but are moved to XML in production so that
simple changes to the mappings do not require you to modify source code and rebuild.
Since XML is common for Hibernate and will generally be used for production in OpenJPA, XML
will be shown for the mappings in this section. To help with understanding what's required (or not
required) in the object model (POJOs), the corresponding base code (minus the annotations) is
also included.
If your legacy Hibernate application does not happen to use mapping files (for example, uses
javadoc-style annotations or Java 5 annotations), then you should still be able to derive the
changes to migrate your application to OpenJPA based on the information in this section.
Conversely, if you want to use Java 5 annotations with OpenJPA, examples of this are included in
the Appendix.
There are also different ways to map between Java objects and relational tables. This section
covers the common scenarios that you will find in an enterprise application, including:
1. Inheritance
2. Relationships
3. Lazy initialization
4. Object identity
5. Optimistic locking
1. Inheritance
The data model for an enterprise application generally has several places where generalization/
specialization amongst classes provides significant reuse opportunities. Hibernate and OpenJPA
both support three different ways inheritance can be modeled in relational tables. We discuss two
of those options which we believe to be the most common scenarios:
For the cases where the Java base class contains most of the attributes for all of the subclasses,
inheritance can be mapped using a single table, with a discriminator column whose value identifies
the specific subclass to which the instance that is represented by the row belongs. If there are any
columns not mapped to a particular subclass, then those columns must be declared to be nullable,
as they will be empty in the database row for that subclass.
A disadvantage of this inheritance strategy is that if your subclasses define several properties
that should be non-null for that instance, then the loss of the non null constraint could pose a
data integrity problem. A major advantage of this approach is that it provides the best support for
polymorphic relationships between entities and queries that range over the class hierarchy, as
there are no complex joins.
• Object model
Mapping 1. Single table inheritance (POJO)
// Participant (base) class
public class Participant implements Serializable{
private Long participantId;
...
}
• Use a discriminator value in the subclass(es) to represent its instances; you'd also
define the persistent properties of the subclass, but no ID. There will be no table for
the subclass(es); their properties will be promoted to the table that represents the base
class.
<attributes>
<id name="participantId">
<column name="PRTCPNT_TID"/>
</id>
...
</attributes>
</entity>
// SalesRep subclass
<entity class="SalesRep">
<discriminator-value>SALES_REP</discriminator-value>
...
</entity>
// Administrator subclass
<entity class="Administrator">
<discriminator-value>ADMIN</discriminator-value>
...
</entity>
b. Joined inheritance
For cases where the base class does not contain most of the attributes for all of the subclasses,
use one table that contains the base class attributes with a separate joined table for each of
the subclasses. The table contains columns only for non-inherited properties. Thus, reading a
subclass instance requires a join across the base class table and the subclass table.
An advantage of the joined inheritance strategy is that you can define non null properties in the
subclass(es), but the corresponding disadvantage is that multiple joins are required to construct an
instance. This approach is also the most flexible in that you can define new subclasses and add
properties to existing subclasses without having to modify the base class table.
• Object model
• In the subclass(s), define the persistent properties of the subclass; its table will contain
those properties and will have a primary key column that is used as a foreign key to join
to the primary key of the base class. The sub-classes don't define version columns.
2. Relationships
There are many kinds of relationships needed between the objects in the object model (and
between the tables in the data model). While data models contain unspecified relationships
between any columns of similar data class, the object model must be explicit about relationships
between objects to support traversal. In addition, relationships in the data model have no intrinsic
direction (although searching in one direction may be more efficient than another). Object model
relationships, on the other hand, inherently have a direction from one object to the other.
Object model relationships are implemented in the data model by way of a foreign key in one
table that references a primary key in another. The table that has the foreign key is called the child
object. Its rows represent objects whose lifetime is dependent on the object they reference. Thus,
its table will have the foreign key to the parent. Since the child object has the foreign key, it must
always point to a valid parent object; it can't be orphaned, which means that to delete a parent
object, you must first delete its child object(s) or cascade the delete from the parent to the children.
For maintaining relationships, the child object is also known as the owner of the relationship, while
the parent object is the non-owner. This concept of an "owner" is important because although the
Java programmer has to set both sides of a bi-directional relationship, the database just has to
update one value to reflect those changes; and that update is to the foreign key in the child object
(or the owner). Thus, changes to the property that represents the foreign key in the child object
Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 17 of 43
developerWorks® ibm.com/developerWorks/
are propagated to the database, while changes to the inverse property in the parent object are not
propagated to the database.
a. One-to-one
b. Many-to-one
c. One-to-many
d. Many-to-many
a. One-to-one relationship
One-to-one relationship defines a reference to another persistent object, where the child object's
lifetime is totally dependent on the parent object's lifetime.
Modeling one-to-one relationships with component objects will still result in two separate
classes in the object model, but there will not be two separate tables in the data model.
There are two other approaches that can be used for modeling one-to-one relationships in
Hibernate:
• Use the many-to-one element with a foreign key association between the tables; follow the
many-to-one relationship guidelines.
• Use the one-to-one element with a primary key association between the tables; map using the
one-to-one element in OpenJPA.
The remainder of this section covers the migration of one-to-one relationships using component
objects in Hibernate, and shows you how to migrate them to embeddable objects in OpenJPA.
• Object model
Mapping 7. One-to-one relationship (POJO)
// Employee (parent) class
public class Employee implements Serializable {
private Long employeeId;
private EmployeeRecord employeeRec;
...
}
• Use a nested component element to model the child class; you can also define additional
properties in the nested component, if you so desire.
b. Many-to-one relationship
The entity declaring the many-to-one relationship is the child object (or owner of the relationship),
as its table has the foreign key, while the object that is referenced by the entity declaring the many-
to-one relationship is the parent object. Since its table doesn't have the foreign key, it is the non-
owner, or inverse of the relationship.
• Object model
Mapping 10. Many-to-one relationship (POJO)
// Address (parent) class
c. One-to-many relationship
That said, if there are use cases that require traversal from the child to its parent, then you can
easily add the many-to-one relationship in the child class to make it a bidirectional relationship.
The entity declaring the one-to-many relationship is the parent object (and is the non-owner). The
table for this entity defines the primary key, but it does not have the foreign key -- that is in the
child.
The objects that are referenced by this one-to-many relationship are the child objects and the
owner of the relationship. The child objects have the foreign key and reference the parent's
primary key.
In the unidirectional case, the foreign key column in the child table doesn't map to a property in the
child object; it is in the data model, but not the object model. Since it is unidirectional there is just a
property in the parent object; not the child. In addition, the foreign key column has to be defined as
nullable because Hibernate will first insert the child row (with a NULL foreign key) and then update
it later.
In the bidirectional case, the object-relational mapping is better because there is a property in
the child object for the foreign key column, and that column doesn't have to be nullable in the
database. But the resulting object model has cyclic dependencies and tighter coupling between the
objects, and requires additional programming to set both sides of the relationship.
As you can see, there are several tradeoffs to consider with regard to the definition of one-to-many
relationships, but using unidirectional relationships is generally recommended unless there are use
cases that indicate the need for navigation in both directions.
• Object model
Mapping 13. One-to-many relationship (POJO)
// Address (parent) entity
public class Address implements Serializable {
private Long addressId;
private Set phones = new HashSet();
...
}
<set
name="phones"
table="T_PHONE"
cascade="all-delete-orphan">
<key column="ADDR_TID"/>
<one-to-many class="Phone">
</set>
</class>
a. Use a join table mapping and add a join table to the database, or
b. Use a foreign key mapping and convert the relationship from unidirectional to
bidirectional, add the inverse property to the object model, and change the code to set
the relationship in both directions.
There are some additional features that are often used in the definition of one-to-many
relationships, which you may need to know how to migrate:
• Hibernate
• inverse="true"
In Hibernate, you might encounter the inverse="true" attribute being used in the definition
of a bidirectional relationship. If so, don't worry, because this feature is equivalent to
what OpenJPA refers to as the non-owner of the relationship whose table doesn't have
Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 23 of 43
developerWorks® ibm.com/developerWorks/
the foreign key. Similarly, the Hibernate inverse="false" attribute is equivalent to the
OpenJPA owner of the relationship whose table has the foreign key.
In general, these notions align, except for the case where someone defines a Hibernate
mapping with inverse="true" set on the many-to-one side of a bidirectional relationship. If
you find such a mapping, you should change it prior to performing the migration, as it is
not a best practice in Hibernate and does not generate optimal SQL. For instance, if the
many-to-one side is set to inverse="true" then every time you create a child, Hibernate
will execute two SQL statements, one to create the child and one to update the child
with the foreign key of the parent. Changing it to inverse="false" on the many-to-one side
and setting inverse="true" on the one-to-many side will fix that oversight and align it with
OpenJPA. Of course, you should re-test the application after making that change.
• cascade="all-delete-orphan"
There is no equivalent of this Hibernate feature in the JPA specification, but there is a
proprietary @ElementDependent annotation in the OpenJPA persistence provider that
does exactly what the all-delete-orphan feature does for Hibernate. If you want to use
that feature then see the OpenJPA Users Guide. Although it's a proprietary feature, if you
used it to migrate the all-delete-orphan feature, then there would be little or no changes
to the application source code.
To migrate the all-delete-orphan feature in a standards-compliant fashion, you can use
the cascade=CascadeType.ALL attribute in the OneToMany annotation and change the
source code to not only remove the child from the parent's collection, but also explicitly
delete the child from the database; for example, instead of having code that looks like
this:
public class Address implements Serializable {
...
public void removePhone(Phone p) {
this.phones.remove(p); // Explicitly remove p from the set; which
// Implicitly deletes p from the database
}
}
necessary to get cascade of the merge() operation from the parent to the children to
work.
Without defining cascade in both directions, the cascade of the remove() and persist()
operations worked from the parent Address object to its dependent Phone objects, but
the cascade of merge() raised an exception indicating that the Phone.address field in the
dependent object does not allow cascade.
(This is a known problem in OpenJPA v0.9.7 that is currently being worked on and
should be fixed in later releases. In the meantime, the workaround is to enable cascade
in both directions.)
d. Many-to-many relationship
Like the other bidirectional relationships, there is an owning and a non-owning side. In this case,
the owning side has the mapping table, instead of the foreign key. Either side can be designated
as the owning side; it doesn't matter which side you pick.
• Object model
Mapping 16. Many-to-many relationship (POJO)
// Group (non-owner/parent) entity
public class Group implements Serializable {
private Long groupId;
private Set users = new HashSet();
...
}
3. Lazy initialization
Lazy initialization can be applied to any field, but it is most often used with one-to-many or many-
to-many relationships where you can control whether you want the database to return all of the
child objects as well when you read the parent object.
You have to be careful in the definition of relationships with LAZY or EAGER fetching, particularly
when following a typical pattern where the EJB tier returns detached entities to the Web tier,
and the Web tier accesses that data to render the view. With Lazy loading, the Web tier may not
have dependent objects necessary to render the view. On the other hand, you can't simply make
all relationships EAGER loaded, because you would return more information every time than is
necessary.
The bottom line is that you have to tailor your domain model with appropriate fetch strategies that
match your business requirements. If a use case needs the parent object and the child objects,
then use EAGER loading. If the use case does not require the child objects, then use LAZY
loading.
• Object model
Mapping 19. Lazy initialization (POJO)
// Address (parent) entity
<set
name="phones"
table="T_PHONE"
cascade="all-delete-orphan"
inverse="true"
lazy="false">
<key column="ADDR_TID"/>
<one-to-many class="Phone"/>
</set>
</class>
...
</attributes>
</entity>
It is also worth noting that Hibernate and OpenJPA differ in accessing a lazy loaded collection
from a detached object. In Hibernate, an exception will be thrown if the programmer attempts to
access a lazy loaded collection on a detached object; while OpenJPA will return a null value, not
an exception.
The reason for this difference is because the JPA specification does not specify how accessing
lazy loaded collections on a detached object are processed. Each JPA vendor can decide how to
process this condition. They could throw an exception, or they could leave it un-initialized, or they
could even return a collection with zero elements.
Thus, if the legacy Hibernate application is using exceptions to detect access to a lazy loaded
collection of a detached object, you can do the same with OpenJPA by testing for a null collection.
However, it is important to remember that the JPA specification doesn't say whether to raise an
exception or to return a null, so relying on this behavior is not portable and is subject to change --
perhaps even breaking your application in later releases.
In addition, it is also important to note that whether you get an exception (as you will with
Hibernate) or not (as is the case in OpenJPA), it is an indication of an application error that needs
to be detected and fixed during testing. That is, it is a problem whether you get an exception or
not, so mandating whether or not exceptions are thrown in the JPA specification doesn't solve the
problem: your application needs some architectural guidelines for working with detached objects.
The paragraphs above define standard JPA processing as defined by the specification, but
OpenJPA provides additional capabilities via the openjpa.DetachState configuration property.
Specifically, one of the plug-in properties for this DetachState configuration property is
AccessUnloaded; if you set the AccessUnloaded property to false, then OpenJPA will throw an
exception whenever an unloaded field is accessed. This behavior is consistent with Hibernate.
Mapping 22 shows an example of the openjpa.DetachState configuration property:
1. Explicitly initialize all collections required by the view before returning them; that is, you
determine whether lazy loading is correct for these particular relationships.
2. Keep the entity manager open until you are done rendering the view (that is, you open and
close the entity manager in the view), which avoids working with detaching altogether.
3. Similar to the second solution, the third possible solution with EJB 3.0 uses extended
persistence context to keep the entity manager alive between transactions.
If you are passing entities from the EJB component to the Web tier, then you should follow the
first solution, since it enables you to keep the transaction logic in the EJB (session facade), rather
than requiring the Web tier to manage the transaction. In addition, it is difficult for the Web tier
to manage the transaction because it requires committing the transaction and closing the entity
manager (perhaps in a ServletFilter) after rendering the view.
The final point to make with regard to detached objects is that you should align your object model
with your business use cases. If some use cases require lazy loading of the collection while others
require eager loading of that same collection, then use lazy loading of the collection in your object
model and force eager loading in the session facade, as required. That is, define different methods
in the session facade for the different use cases: one would simply return the detached object,
while the other would load the child objects, then return the detached object. There are three ways
to load the child objects with OpenJPA; the first two of which are JPA standard and the last is an
OpenJPA extension:
4. Object identity
All tables in the data model will include an object identity column, called an OID, as the primary
key for the table. A best practice for the OID is to use an integer column whose value is assigned
by the system. In this case, the OID maps to the primary key of the object model using a
java.lang.Long. As well, all relationships between tables (foreign keys) are based on the OID
column of the related table.
The ID will be assigned by Hibernate upon inserting any new rows by commonly using the
Hibernate Sequence generator class, which uses the underlying database support for sequences
that has a single row for each table containing the table name and the current OID number. In this
approach, OIDs are unique to each table (OIDs may not be unique across tables).
Using OIDs provides a key to all data that has no business meaning. These system generated
keys do not have any business meaning and are referred to as synthetic keys, as opposed to
natural keys which do have business meaning. Using synthetic keys enables any business-
meaningful data in the database to be changed without concern for causing constraint violations.
In existing Hibernate models, you will also often find that most tables will have a business-
meaningful identity that may include many columns. You might also find that a unique constraint is
defined on any such candidate primary keys in the data model. This database constraint ensures
the uniqueness and also provides an index for finding such entities based on business meaning.
You might encounter some special situations where data had to be pre-populated into some tables
to represent data rows that have no business meaning (for code tables and the like). To do that,
these rows must have been inserted into the database before the application was started and the
OIDs for those rows must be lower than the starting value for the SEQUENCE table for that table.
In this case, you might find sequence tables with initial values larger than 1.
• Object model
Mapping 23. Object identity (POJO)
/ Address entity
For Hibernate and OpenJPA, the DDL for the OID's existing sequence table is:
create sequence T_ADDRESS_SEQ;
One final note on object identifiers: all foreign keys in the data model will generally be based on
the OID primary key of the related table. All foreign keys will have a constraint in the data model
Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 31 of 43
developerWorks® ibm.com/developerWorks/
that references the primary key and has ON DELETE RESTRICT semantics defined, so that the
database can prevent you from deleting a row from the database (for example, you can't delete the
parent object) if there still exists a row for the child object of that parent.
5. Optimistic locking
Hibernate provides optimistic and pessimistic locking modes for concurrency control. For short-
lived transactions and better performance, most legacy Hibernate applications use optimistic
locking. Short-lived transactions release the locks while a user edits the data on the screen.
Optimistic locking checks the timestamp or version of the object to make sure it hasn't been
changed by someone while the user was editing the data. If so, an optimistic lock exception is
thrown, the user will be notified that the exception has occurred, and the user can refresh the
data that was causing the exception and retry the transaction, which should work the next time.
Notifying the user of the optimistic lock exception generally occurs through the normal exception
handling of the application.
• Object model
Where concurrency could be an issue, all entities will contain a version property, which is
used by the persistence provider to detect concurrent modifications to the same record. The
legacy application simply treats the version property as immutable and is responsible for
keeping it with the entity from the time the entity is read from the database until the time the
entity is written back to the database.
Mapping 26. Object identity (POJO)
// Address entity
Hibernate supports both optimistic and pessimistic locking, but the JPA specification only
defines the concept of optimistic locking. However, OpenJPA supports optimistic locking and
provides extensions for pessimistic locking. Therefore, if you are migrating a legacy Hibernate
application that uses pessmistic locking, consider setting the following OpenJPA configuration
properties in the persistence.xml file:
Mapping 29. Pessimistic locking (OpenJPA configuration properties)
<property
name="openjpa.LockManager"
value="pessimistic">
</property>
<property
name="openjpa.ReadLockLevel"
value="read">
</property>
<property
name="openjpa.WriteLockLevel"
value="write">
</property>
<property
name="openjpa.jdbc.TransactionIsolation"
value="repeatable-read">
</property>
Using these properties, all reads will acquire share (read) locks and hold them until end
of a transaction. If you experience concurrency problems (deadlocks) when there are
multiple concurrent updaters against the same record, then you might need to specify
a ReadLockLevel of write to generate FOR UPDATE when retreiving data and force
serialization of the updaters.
If you specify these pessimistic lock levels using configuration parameters in
the persistence.xml file, then they will apply to all transactions. Alternatively, you
might want to set them programmatically for individual transactions using the
org.apache.openjpa.persistence.FetchPlan class, as illustrated in this code fragment:
There are at least three common configuration scenarios that you will encounter in the migration of
a Hibernate application to OpenJPA:
1. Database connections -- the configuration properties that tell the SessionFactory how to
connect to the database.
2. Mapping locations -- the properties that govern the mapping of objects to rows in the
database.
3. Logging categories -- the properties that enable you to diagnose problems, such as setting
the logging/tracing levels.
There are many configuration properties that you might encounter that cannot all be covered here,
so be sure to see Related topics for information on mapping other configuration properties. Of
specific interest will be the Hibernate API Documentation for the org.hibernate.cfg.Environment
class and all Hibernate configuration properties, and the OpenJPA Users's Guide.
1. Database connections
There are two ways to configure database connections: using local JDBC connections (covered
here) or using J2EE data sources (see Related topics).
• Hibernate conventions
In Hibernate, configuring JDBC connection maps as follows:
name="openjpa.ConnectionURL"
value="jdbc:db2://localhost:50000/JPATEST">
</property>
<property
name="openjpa.ConnectionUserName"
value="db2admin">
</property>
<property
name="openjpa.ConnectionPassword"
value="db2admin">
</property>
</properties>
</persistence-unit>
</persistence>
(Although not required by this example, another parameter that is quite useful is the
openjpa.ConnectionProperties, which enables additional properties to be passed in when
doing the connect() call.)
2. Mapping locations
If you use the XML-based configuration approach in Hibernate, then you can specify not only
configuration parameters, but also the location of mapping files. This is a common scenario
because it enables you to configure the SessionFactory without having to programmatically specify
the location of the mapping files.
• Hibernate conventions
In Hibernate, configuring the location of the mapping files maps as follows:
• Use <mapping> with resource attribute.
Configuring 3. Hibernate mapping locations
...
<hibernate-configuration>
<session-factory>
...
<!-- Mapping files -->
<mapping resource="domainmodel.hbm.xml"/>
</session-factory>
</hibernate-configuration>
• OpenJPA conventions
In OpenJPA, configuration the location of the mapping files maps as follows:
• Use <mapping-file> element.
Configuring 4. OpenJPA mapping locations
<persistence ...>
<persistence-unit name="TEST">
...
<!-- Mapping files -->
<mapping-file>META-INF/orm.xml</mapping-file>
</persistence-unit>
3. Logging categories
Troubleshooting can be difficult with Hibernate applications, because Hibernate generates the SQL
commands for you based on the metadata that is specified in the mapping files. Therefore, it is
often useful to configure the logging categories to see the exact SQL that Hibernate issues to the
database.
• Hibernate conventions
In Hibernate, configuration of logging categories maps as follows:
• Use show_sql configuration parameter to print the SQL commands.
Configuring 5. Hibernate logging categories
...
<hibernate-configuration>
<session-factory>
...
<property
name="show_sql">
true
</property>
</session-factory>
</hibernate-configuration>
If investigating the SQL is not enough to diagnose the problem, there are other logging
categories available in Hibernate, but you will find them configured in the log4j.properties
file on the classpath. (Configuration of that file is not shown here; refer to the Hibernate
documentation for information on configuring other Hibernate logging categories.)
• OpenJPA conventions
In OpenJPA, configuration of logging maps as follows:
• Use openjpa.Log configuration property to print the SQL.
• Use openjpa.ConnectionFactoryProperties to pretty print the SQL.
Configuring 6. OpenJPA logging categories
<persistence ...>
<persistence-unit name="TEST">
...
<properties>
<property
name="openjpa.Log"
value="SQL=TRACE">
</property>
<property
name="openjpa.ConnectionFactoryProperties"
value="PrettyPrint=true, PrettyPrintLineLength=72">
</property>
</properties>
</persistence-unit>
You can also configure OpenJPA to output other logging information using the openjpa.Log
configuration property. (See the OpenJPA User's Guide.)
Conclusion
This article took you through a case study that migrated a proprietary Hibernate 3 application using
EJB 2.1 to an industry standard JPA application using the OpenJPA 0.9.7 persistence provider
with EJB 3.0. The migration was described through a series of common scenarios, and in each
scenario, the Hibernate and OpenJPA implementations were compared side by side. This side
by side comparison will help those who are migrating existing Hibernate/EJB 2.1 applications to
OpenJPA/EJB 3.0, as well as those who might be using OpenJPA/EJB 3.0 on their next project.
The article showed how to migrate the three primary building blocks that make up a legacy
Hibernate application (the application source code, the object-relational mappings, and the
configuration parameters) and drew these conclusions:
• If the Hibernate application source code is well structured and encapsulates the Hibernate
calls, then the migration of the source code is straightforward for the common scenarios. For
programs that do not encapsulate Hibernate calls, the migration will be more difficult, but will
still be more of a syntactic change than a change to the business (session, transaction, and
entity management) logic.
• Since the object-relational mappings already exist, then you will need to use a meet-in-the-
middle migration approach to preserve the existing object and data models. You can't use
a bottom-up approach to generate the object model from the data model, nor can you use
a top-down approach to generate the data model from the object model; the migration must
preserve both models. This article manually implemented the meet-in-the-middle mapping,
but perhaps the Dali JPA Tools or the IBM Design Pattern Toolkit (see Related topics) can
automate much of the Hibernate XML to OpenJPA XML migration.
• The common configuration parameters can be migrated easily from Hibernate to OpenJPA,
but there are many other parameters that are used to tune the legacy Hibernate application
which may or may not be required for OpenJPA. Thus, do not perform a side-by-side
migration for tuning. Instead, migrate the common configuration parameters, then let the load
test dictate which OpenJPA tuning parameters to introduce.
This article did not cover the migration of Hibernate queries to OpenJPA, as it is more common
for Hibernate applications to have a well-defined domain model that matches the business
requirements. For those applications that do not have a well-defined domain model and require
lots of ad hoc queries, then perhaps a solution like iBatis is a better fit than an object-relational
mapping tool like Hibernate or OpenJPA. That said, if there is enough interest, then perhaps a
follow-on article will cover the migration of Hibernate queries to OpenJPA.
// SalesRep subclass
@Entity
@DiscriminatorValue(value="SALES_REP")
public class SalesRep extends Participant {
...
// Administrator subclass
@Entity
@DiscriminatorValue(value="ADMIN")
public class Administrator extends Participant {
...
}
// SalesRep subclass
@Entity
@Table(name="T_SALESREP")
@PrimaryKeyJoinColumn(name="PRTCPNT_TID")
public SalesRep extends Participant {
...
}
// Administrator subclass
@Entity
@Table(name="T_ADMIN")
@PrimaryKeyJoinColumn(name="PRTCPNT_TID")
public Administrator extends Participant {
...
}
@Entity
@Table(name="T_ADDRESS")
public class Address implements Serializable {
...
@Column(name="ADDR_TID")
@Id private Long addressId;
}
@Entity
@Table(name="T_ADDRESS")
public class Address implements Serializable {
...
@OneToMany(mappedBy="address", cascade=CascadeType.ALL)
private Set<Phone> phones = new HashSet<Phone>();
...
@Column(name="ADDR_TID")
@Id private Long addressId;
}
@Entity
@Table(name="T_PHONE")
public class Phone implements Serializable {
...
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="ADDR_TID")
private Address address;
...
}
@ManyToMany
@JoinTable(
name="T_USER_GROUP",
joinColumns=@JoinColumn(name="USER_TID"),
inverseJoinColumns=@JoinColumn(name="GROUP_TID"))
private Set<Group> groups = new HashSet<Group>();
...
@ManyToMany(mapppedBy="groups")
private Set<User> users = new HashSet<User>();
...
}
@Entity
@Table(name="T_ADDRESS")
public class Address implements Serializable {
...
@OneToMany(mappedBy="address", fetch=FetchType.EAGER)
private Set<Phone> phones = new HashSet<Phone>();;
...
@Column(name="ADDR_TID")
@Id private Long addressId;
}
@Entity
@Table(name="T_PHONE")
public class Phone implements Serializable {
...
@ManyToOne
@JoinColumn(name="ADDR_TID")
private Address address;
...
}
@Entity
@Table(name="T_ADDRESS")
public class Address implements Serializable {
...
@SequenceGenerator(name="AddressSeq", sequenceName="T_ADDRESS_SEQ")
@GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator="AddressSeq")
@Column(name="ADDR_TID")
@Id private Long addressId;
...
}
Acknowledgements
The authors thank Roland Barcia, Reddy Sripathi, David Wisnesk, and David Zhang for their help
with this article.
Related topics
• OpenJPA User's Guide
• OpenJPA javadoc
• Java Persistence APIs
• EJB 3.0 Specification
• Hibernate API Documentation
• Hibernate Reference Documentation
• Using Spring and Hibernate with WebSphere Application Server
• Leveraging OpenJPA with WebSphere Application V6.1
• Dali JPA Tools
• WebSphere Application Server V6.1 Feature Pack for EJB 3.0
• Get started with model-driven development using Design Pattern Toolkit