You are on page 1of 43

Migrating legacy Hibernate applications to OpenJPA

and EJB 3.0


Donald Vines August 22, 2007
Kevin Sutter

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

© Copyright IBM Corporation 2007 Trademarks


Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 1 of 43
developerWorks® ibm.com/developerWorks/

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.

Migrating Hibernate application source code


The Java Persistence API (JPA) was introduced as part of the EJB 3.0 specification (JSR220) to
get the entire Java community behind a single, standard persistence API. JPA draws upon the best
ideas from Hibernate, TopLink, Java Data Objects, and the Container Managed Persistence (EJB-
CMP) 2.1 specification.

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.

Details are provided below in these areas:

1. Classes and interfaces


2. Runtime configuration
3. Session management
4. Transaction management
5. Entity management
6. Detached entities

1. Classes and interfaces


This article focuses on the JPA javax.persistence package and not the OpenJPA
implementation packages, as you would generally be coding to the JPA standard APIs.

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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 2 of 43


ibm.com/developerWorks/ developerWorks®

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.

org.hibernate javax.persistence Description

cfg.Configuration Persistence A bootstrap class that configures the session


factory (in Hibernate) or the entity manager
factory (in OpenJPA). It is generally used to
create a single session (or entity manager)
factory for the JVM.

SessionFactory EntityManagerFactory Provides APIs to open Hibernate sessions


(or OpenJPA entity managers) to process a
user request. Generally, a session (or entity
manager) is opened per thread processing
client requests.

Session EntityManager Provides APIs to store and load entities to and


from the database. It also provides APIs to get
a transaction and create a query.

Transaction EntityTransaction Provides APIs to manage transactions.

Query Query Provides APIs to execute queries.

2. Runtime configuration
• Hibernate conventions
In Hibernate, runtime configuration maps as follows:
• Use static SessionFactory variable.
• Use Configuration#configure() method.
• Use Configuration#buildSessionFactory() method.

Listing 1. Hibernate runtime configuration


public class ORMHelper {

private static SessionFactory sf;

protected static synchronized


SessionFactory getSessionFactory(String name) {
if (sf == null) {
sf = new Configuration().configure(name).buildSessionFactory();
}
return sf;
}
...
}

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.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 3 of 43


developerWorks® ibm.com/developerWorks/

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()

Listing 2. OpenJPA runtime configuration


public class ORMHelper {

private static EntityManagerFactory sf;

protected static synchronized


EntityManagerFactory getSessionFactory(String name) {
if (sf == null) {
sf = Persistence.createEntityManagerFactory(name);
}
return sf;
}
...
}

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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 4 of 43


ibm.com/developerWorks/ developerWorks®

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.

Listing 3. Hibernate session management


public class ORMHelper {

private static final ThreadLocal tls = new ThreadLocal();

public static void openSession() {


Session s = (Session) tls.get();
if (s == null) {
s = getSessionFactory("test.cfg.xml").openSession();
tls.set(s);
}
}

public static Session getCurrentSession() {


return (Session) tls.get();
}

public static void closeSession() {


Session s = (Session)tls.get();
tls.set(null);
if (s != null && s.isOpen()) s.close();
}
...
}
• OpenJPA conventions

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 5 of 43


developerWorks® ibm.com/developerWorks/

In OpenJPA, equivalent EntityManager management maps as follows:


• Use ThreadLocal to get the current entity manager.
• Use EntityManagerFactory#createEntityManager() to open the session.
• Use EntityManager#isOpen() and EntityManager#close() to close the session.
Listing 4. OpenJPA session management
public class ORMHelper{

private static final ThreadLocal tls = new ThreadLocal();

public static void openSession() {


EntityManager s = (EntityManager) tls.get();
if (s == null) {
s = getSessionFactory("test").createEntityManager();
tls.set(s);
}
}

public static EntityManager getCurrentSession() {


return (EntityManager) tls.get();
}

public static void closeSession() {


EntityManager s = (EntityManager) tls.get();
tls.set(null);
if (s != null && s.isOpen()) s.close();
}
...
}

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 {

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 6 of 43


ibm.com/developerWorks/ developerWorks®

private static final ThreadLocal tltx = new ThreadLocal();

public static void beginTransaction() {


Transaction tx = (Transaction) tltx.get();
if (tx == null) {
tx = getCurrentSession().beginTransaction();
tltx.set(tx);
}
}

public static void commitTransaction() {


Transaction tx = (Transaction)tltx.get();
if (tx != null && tx.isActive()) tx.commit();
tltx.set(null);
}

public static void rollbackTransaction() {


Transaction tx = (Transaction)tltx.get();
tltx.set(null);
if (tx != null && tx.isActive()) tx.rollback();
}
...
}
• OpenJPA conventions
In OpenJPA, equivalent transaction management maps as follows:
• Use EntityManager#getTransaction() and EntityTransaction#begin().
• Use EntityTransaction#commit().
• Use EntityTransaction#isActive() and EntityTransaction#rollback().

Listing 6. OpenJPA transaction management


public class ORMHelper {

private static final ThreadLocal tltx = new ThreadLocal();

public static void beginTransaction() {


EntityTransaction tx = (EntityTransaction) tltx.get();
if (tx == null) {
tx = getCurrentSession().getTransaction();
tx.begin();
tltx.set(tx);
}
}

public static void commitTransaction() {


EntityTransaction tx = (EntityTransaction)tltx.get();
if (tx != null && tx.isActive()) tx.commit();
tltx.set(null);
}

public static void rollbackTransaction() {


EntityTransaction tx = (EntityTransaction)tltx.get();
tltx.set(null);
if (tx != null && tx.isActive()) tx.rollback();
}
...
}

Although the OpenJPA example stores the transaction in ThreadLocal, it is


common to just call getCurrentSession().getTransaction().begin() and then later call

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 7 of 43


developerWorks® ibm.com/developerWorks/

getCurrentSession().getTransaction().commit(). Thus, with OpenJPA, you don't really need to


store the transaction in ThreadLocal.

5. Entity management
Common scenarios for entity management include:

• Creating a persistent object.


• Retrieving a persistent object using the primary key.
• Updating a persistent object.
• Deleting a persistent object.

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.

Listing 7. Hibernate entity management


public class ORMHelper {
...
public static void create(Serializable obj) {
getCurrentSession().save(obj);
}

public static Object retrieve(Class clz, Serializable key) {


return getCurrentSession().load(clz, key);
}

public static void update(Serializable obj ) {


getCurrentSession().saveOrUpdate(obj);
}

public static void delete(Serializable obj ) {


getCurrentSession().delete(obj);
}
}
• OpenJPA conventions
In OpenJPA, equivalent entity management maps as follows:
• Use EntityManager#persist to create a persistent entity.
• Use EntityManager#find to retrieve a persistent entity.
• Use EntityManager#merge to update a detached entity. (If you are working with
persistent (connected) entities, then there's no need to call merge; OpenJPA propagates
the update to the database at the end of the transaction.)
• Use EntityManager#remove to delete a detached or persistent entity.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 8 of 43


ibm.com/developerWorks/ developerWorks®

Listing 8. OpenJPA entity management


public class ORMHelper {
...
public static void create( Serializable obj ) {
getCurrentSession().persist((Object) obj);
}

public static Object retrieve(Class clz, Serializable key) {


return getCurrentSession().find( clz, (Object) key );
}

public static Object update( Serializable obj ) {


return getCurrentSession().merge((Object) obj);
}

public static void delete( Serializable obj ) {


getCurrentSession().remove( (Object) obj);
}
}

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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 9 of 43


developerWorks® ibm.com/developerWorks/

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.

Listing 9. Hibernate detached entities with EJB2.1


public class CustomerFacadeBean implements SessionBean, CustomerFacade{

public Customer createCustomer( Customer customerEntity ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
ORMHelper.create(customerEntity);
ORMHelper.commitTransaction();
return customerEntity;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}

public Customer updateCustomer( Customer customerEntity ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
ORMHelper.update(customerEntity);
ORMHelper.commitTransaction();
return customerEntity;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}

public Customer getCustomer( Long customerId ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
Customer customerEntity;
customerEntity = ORMHelper.retrieve(Customer.class,customerId);
ORMHelper.commitTransaction();
return customerEntity;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}

public void deleteCustomer( Customer customerEntity ) {

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 10 of 43


ibm.com/developerWorks/ developerWorks®

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.

Listing 10. OpenJPA detached entities with EJB 3.0


@Stateless
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class CustomerFacadeBean implements CustomerFacade {

public Customer createCustomer( Customer customerEntity ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
ORMHelper.create(customerEntity);
ORMHelper.commitTransaction();
return customerEntity;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}

public Customer updateCustomer( Customer customerEntity ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
Customer returnValue;
returnValue = ORMHelper.update(customerEntity);
ORMHelper.commitTransaction();
return returnValue;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}

public Customer getCustomer( Long customerId ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
Customer customerEntity;
customerEntity = ORMHelper.retrieve(Customer.class,customerId);
ORMHelper.commitTransaction();

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 11 of 43


developerWorks® ibm.com/developerWorks/

return customerEntity;
} catch (RuntimeException ex) {
ORMHelper.rollbackTransaction();
throw ex;
} finally {
ORMHelper.closeSession();
}
}

public void deleteCustomer( Customer customerEntity ) {


ORMHelper.openSession();
try {
ORMHelper.beginTransaction();
ORMHelper.delete(customerEntity);
ORMHelper.commitTransaction();
} 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.

Migrating Hibernate object-relational mappings


Hibernate object-relational mappings can be defined in a set of XML mapping files that are
loaded at startup time. These mapping files can be directly used or generated from javadoc style
annotations embedded in your source code. In more recent versions of Hibernate, you can also
define the object-relational mappings via Java 5 annotations.

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.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 12 of 43


ibm.com/developerWorks/ developerWorks®

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:

a. Single table inheritance


b. Joined inheritance
The third option (table per concrete class), although less often used, is provided by Hibernate and
is an optional implementation for the JPA persistence providers, like OpenJPA.

a. Single table inheritance

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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 13 of 43


developerWorks® ibm.com/developerWorks/

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;
...
}

// SalesRep (sub) class


public class SalesRep extends Participant { ... }

// Administrator (sub) class


public class Administrator extends Participant { ... }
• Hibernate conventions
In Hibernate, single table inheritance maps as follows:
• Use class with discriminator column in base class, mapped to table; you would also
define the mapping for the primary key and other properties (which will be covered later
and not shown in the example).
• Use subclass with distinct discriminator value in subclass; you would also define
mapping for properties that are unique to the subclass. You would not define an ID
element in the subclasses; they don't have their own table, so they use the ID of the
(base) class.

Mapping 2. Single table inheritance (Hibernate XML mapping)


<!-- Participant (base) class -->
<class name="Participant" table="T_PRTCPNT" >
<id name="participantId" column="PRTCPNT_TID"/>
<discriminator column="PRTCPNT_TYPE" type="string"/>
...
</class>

<!-- SalesRep subclass -->


<subclass
name="SalesRep"
extends="Participant"
discriminator-value="SALES_REP">
...
</subclass>

<!-- Administrator subclass -->


<subclass name="Administrator"
extends="Participant"
discriminator-value="ADMIN">
...
</subclass>
• OpenJPA conventions
In OpenJPA, single table inheritance maps as follows:
• Use SINGLE_TABLE inheritance strategy and a discriminator column in the base class;
you would also define the persistent properties of the base class and its unique ID.
Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 14 of 43
ibm.com/developerWorks/ developerWorks®

• 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.

Mapping 3. Single table inheritance (OpenJPA XML mapping)


<entity class="Participant">
<table name="T_PRTCPNT"/>
<inheritance strategy="SINGLE_TABLE"/>
<discriminator-column name="PRTCPNT_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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 15 of 43


developerWorks® ibm.com/developerWorks/

Mapping 4. Joined inheritance (POJO)


// Participant (base) class
public class Participant implements Serializable {
private Long participantId;
...
}

// SalesRep (sub) class


public class SalesRep extends Participant {
...
}

// Administrator (sub) class


public class Administrator extends Participant {
...
}
• Hibernate conventions
In Hibernate, joined inheritance maps as follows:
• In the base class, use the class element with a primary key (id); you would also define
the mapping for those properties that make up the base class.
• In the subclasses, use the joined-subclass with a foreign key column that contains the
primary key of the base class; you would also define the mapping of other properties in
the joined subclass (not shown in the example).

Mapping 5. Joined inheritance (Hibernate XML mapping)


<!-- Participant (base) class -->
<class name="Participant" table="T_PRTCPNT" >
<id name="participantId" column="PRTCPNT_TID"/>
...
</class>

<!-- SalesRep joined-subclass -->


<joined-subclass
name="SalesRep"
extends="Participant"
table="T_SALESREP">
<key column="PRTCPNT_TID"/>
...
</joined-subclass>

<!-- Administrator joined-subclass -->


<joined-subclass
name="Administrator"
extends="Participant"
table="T_ADMIN">
<key column="PRTCPNT_TID"/>
...
</joined-subclass>
• OpenJPA conventions
In OpenJPA, joined inheritance maps as follows:
• In the base class, use JOINED inheritance strategy. The base class also defines the
primary key to be used by all joined subclasses and may optionally define a version
column. You would also define the mapping of the base class properties.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 16 of 43


ibm.com/developerWorks/ developerWorks®

• 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.

Mapping 6. Joined inheritance (OpenJPA XML mapping)


<!-- Participant (base) class -->
<entity class="Participant">
<table name="T_PRTCPNT"/>
<inheritance strategy="JOINED"/>
<attributes>
<id name="participantId">
<column name="PRTCPNT_TID"/>
</id>
...
</attributes
</entity>

<!-- SalesRep subclass -->


<entity class="SalesRep">
<table name="T_SALESREP"/>
<primary-key-join-column name="PRTCPNT_TID"/>
...
</entity>

<! Administrator subclass -->


<entity class="Administrator">
<table name="T_ADMIN"/>
<primary-key-join-column name="PRTCPNT_TID"/>
...
</entity>

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.

The mapping of relationships falls into four categories:

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.

A one-to-one relationship is an exception case. If it occurs, it is a common practice in Hibernate to


model it as a component object so that all properties of the child will be flattened into the parent's
table, thereby removing the need to join parent and child to access the child's properties.

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;
...
}

// EmployeeRecord (child) class


public class EmployeeRecord implements Serializable { ... }
• Hibernate conventions
In Hibernate, one-to-one relationship using component objects maps as follows:
• Use a class to model the parent class; inside the parent you would also define the
primary key (id) and other properties of the parent.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 18 of 43


ibm.com/developerWorks/ developerWorks®

• Use a nested component element to model the child class; you can also define additional
properties in the nested component, if you so desire.

Mapping 8. One-to-one relationship with component objects (Hibernate


XML mapping)
<! Employee (parent) class
<class name="Employee" table="T_EMPLOYEE">
...
<id name="employeeId" column="EMP_TID"/>

<!-- EmployeeRecord (child) class


<component name="employeeRec" class="EmployeeRecord">
...
</component>
</class>
• OpenJPA conventions
In OpenJPA, one-to-one relationship using embeddable objects maps as follows:
• Declare an embedded field in the parent entity (for example, Employee). Embedded
fields are mapped as part of the database record of the parent entity and would be
embedded in the parent entity, rather than forming a relation to child entity.
• Define the child entity (for example, EmployeeRecord) as embeddable.

Mapping 9. One-to-one relationship with embeddable objects (OpenJPA


XML mapping)
<!-- Employee (parent) class -->
<entity class="Employee">
<table name="T_EMPLOYEE"/>
<attributes>
<id name="employeeId">
<column name="EMP_TID"/>
</id>
<embedded name="employeeRec"/>
...
</attributes>
</entity>

<!-- EmployeeRecord (child) class -->


<embeddable class="EmployeeRecord">
...
</embeddable>

b. Many-to-one relationship

A many-to-one relationship defines a reference to a single persistent object. Although many-to-one


relationships can be unidirectional; they are more often defined as the inverse of a one-to-many
bidirectional 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.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 19 of 43


developerWorks® ibm.com/developerWorks/

• Object model
Mapping 10. Many-to-one relationship (POJO)
// Address (parent) class

public class Address implements Serializable {


private Long addressId;
...
}

// Phone (child) class


public class Phone implements Serializable {
private Address address;
...
}
• Hibernate conventions
In Hibernate, many-to-one relationship maps as follows:
• Use many-to-one element in the child class.
• Define the primary key in the parent class.

Mapping 11. Many-to-one relationship (Hibernate XML mapping)


<!-- Phone (child) class -->
<class name="Phone" table="T_PHONE">
<many-to-one
name="address"
class="Address"
column="ADDR_TID">
</many-to-one>
...
</class>

<!-- Address (parent) class -->


<class name="Address" table="T_ADDRESS">
<id name="addressId" column="ADDR_TID"/>
...
</class>
• OpenJPA conventions
In OpenJPA, many-to-one relationship maps as follows:
• In the parent entity, define the primary key (id).
• In the child entity, use the many-to-one element to define the relationship and use the
nested join-column element to define the foreign key. The join-column specifies how to
find the parent entity for this child by way of a join.

Mapping 12. Many-to-one relationship (OpenJPA XML mapping)


<!-- Address (parent) class -->
<entity class="Address">
<table name="T_ADDRESS"/>
<attributes>
<id name="addressId">
<column name="ADDR_TID"/>
</id>
...
</attributes>
</entity>

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 20 of 43


ibm.com/developerWorks/ developerWorks®

<!-- Phone (child) class -->


<entity class="Child">
<table name="T_PHONE"/>
<attributes>
<many-to-one name="address">
<join-column name="ADDR_TID"/>
</many-to-one>
...
</attributes>
</entity>

c. One-to-many relationship

A one-to-many relationship defines a reference to a collection of objects. It is the most common


kind of relationship that you will find in object models due to the fact that use cases typically
require traversal from the parent object to its children, but may or may not require traversal from
the child back to its parent; which means a unidirectional one-to-many relationship will suffice in
most cases.

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 Hibernate, the mapping of one-to-many relationships is generally done by adding a column to


the child table for the foreign key, but the details of the mapping differs depending on whether it is
a unidirectional or a bidirectional one-to-many relationship.

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.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 21 of 43


developerWorks® ibm.com/developerWorks/

• 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();
...
}

// Phone (child) entity


public class Phone implements Serializable {
...
}
• Hibernate conventions
In Hibernate, one-to-many (unidirectional) relationships map as follows:
• Use the set, bag, or list with one-to-many sub-element in parent class.
• Create a foreign key in the table representing the child class if the relationship is
unidirectional; otherwise, use the many-to-one relationship for a bidirectional relationship.

Mapping 14. One-to-many relationship (Hibernate XML mapping)


<!-- Address (parent) class -->
<class name="Address" table="T_ADDRESS">
<id name="addressId" column="ADDR_TID"/>

<set
name="phones"
table="T_PHONE"
cascade="all-delete-orphan">
<key column="ADDR_TID"/>
<one-to-many class="Phone">
</set>

</class>

<!-- Phone (child) class -->


<class name="Phone" table="T_PHONE">
...
</class>

It is important to notice the proprietary cascade="all-delete-orphan" feature in Hibernate (see


Mapping 14). Usage of this attribute enables you to delete a child from the database simply
by removing it from the parent's collection and then saving the parent to the database. With
this proprietary feature, there is no explicit deletion of the child in the code. Although there is
no equivalent feature in standard JPA, there is a proprietary @ElementDependent annotation
in OpenJPA that provides for automatic orphan cleanup, but this feature is not portable to
other persistence providers and can lead to confusion when reading the code. The all-delete-
orphan feature is discussed more next.
• OpenJPA conventions
In OpenJPA, you cannot map a one-to-many unidirectional relationship using a foreign
key; you must map it using a join table. That said, you can use a foreign key mapping if the
relationship is bidirectional. Thus, there are two options to map a one-to-many unidirectional
relationship from Hibernate:
Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 22 of 43
ibm.com/developerWorks/ developerWorks®

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.

Converting them to bidirectional relationships is recommended since it is generally easier to


modify the legacy code than it is to alter the existing database schema and associated rows.
In OpenJPA, one-to-many (bidirectional) relationship maps as follows:
• In the parent, use the one-to-many element to define the collection of child objects, and
use the id element to define the primary key for the parent object.
• In the child class, use the many-to-one element to define the parent object, and use the
nested join-column element to define the foreign key in the child and specify how to join
the parent and the child.

Mapping 15. One-to-many relationship (OpenJPA XML mapping)


<!-- Address (parent) class -->
<entity class="Address">
<table name="T_ADDRESS"/>
<attributes>
<id name="addressId">
<column name="ADDR_TID"/>
</id>
<one-to-many name="phones" mapped-by="address">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
...
</attributes>
</entity>

<!-- Phone (child) class -->


<entity class="Phone">
<table name="T_PHONE"/>
<attributes>
<many-to-one name="address">
<cascade>
<cascade-all/>
</cascade>
<join-column name="ADDR_TID"/>
</many-to-one>
...
</attributes>
</entity>

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
}
}

you would replace it with code that looks like this:


public class Address implements Serializable {
...
public void removePhone(Phone p) {
// Explicitly remove p from the set
this.phones.remove(p);

// Explicitly delete p from the database


ORMHelper.getCurrentSession().delete(p);
}
}
• OpenJPA
• cascade=CascadeType.ALL
Although the Hibernate example above only specified the cascade of operations from
the parent to the child, in testing this mapping with the OpenJPA persistence provider we
had to define cascade=CascadeType.ALL in both directions, meaning in the OneToMany
annotation and in the ManyToOne annotation. Generally, you don't want to cascade
operations from the child to its parent (in the ManyToOne annotation), but this was

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 24 of 43


ibm.com/developerWorks/ developerWorks®

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

A many-to-many relationship defines a reference to a collection of objects through a mapping


table. Many-to-many relationships are not all that common in an object model, but they will
generally be bidirectional.

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();
...
}

// User (owner/child) entity


public class User implements Serializable {
private Long userId;
private Set groups = new HashSet();
...
}
• Hibernate conventions
In Hibernate, many-to-many relationship maps as follows:
• The non-owner uses the collections (set, bag, or list) element with the inverse="true"
attribute, the table attribute, the key sub-element, and the many-to-many sub-element.
• The owner of the relationship uses the collections (set, bag, or list) element with the table
attribute, the key sub-element, and the many-to-many sub-element.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 25 of 43


developerWorks® ibm.com/developerWorks/

Mapping 17. Many-to-many relationship (Hibernate XML mapping)


<! Group (non-owner/parent) class -->
<class name="Group" table="T_GROUP">
<id name="groupId" column="GROUP_TID"/>
<set name="users" table="T_USER_GROUP" inverse="true">
<key column="GROUP_TID"/>
<many-to-many column="USER_TID" class="User"/>
</set>
</class>

<! User (owner/child) class -->


<class name="User" table="T_USER">
<id name="userId" column="USER_TID"/>
<set name="groups" table="T_USER_GROUP">
<key column="USER_TID"/>
<many-to-many column="GROUP_TID" class="Group"/>
</set>
</class>
• OpenJPA conventions
In OpenJPA, many-to-many relationship maps as follows:
• In the owner/child, use the many-to-many element and the nested join-table element
to specify how to create the relationship. You'll also use the id with the nested column
element to specify the name of the primary key that is referred to by the join-table.
• In the non-owner/parent, use the many-to-many element with the mapped-by attribute to
reference the property in the class where the join table is declared. (The join table has all
the information to create the relationship. You will also need to create a primary key via
the id element.)

Mapping 18. Many-to-many relationship (OpenJPA XML mapping)


<!-- User (owner/child) class -->
<entity class="User">
<table name="T_USER"/>
<attributes>
<id name="userId">
<column name="USER_TID"/>
</id>
<many-to-many name="groups">
<join-table name="T_USER_GROUP">
<join-column name="USER_TID"/>
<inverse-join-column name="GROUP_TID"/>
</join-table>
</many-to-many>
...
</attributes>
</entity>

<!-- Group (non-owner/parent) class -->


<entity class="Group">
<table name="T_GROUP"/>
<attributes>
<id name="groupId">
<column name="GROUP_TID"/>
</id>
<many-to-many name="users" mapped-by="groups"/>
...
</attributes>
</entity>

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 26 of 43


ibm.com/developerWorks/ developerWorks®

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.

To further explain this concept, consider a one-to-many or a many-to-many relationship from A to


B. If fetch is set to LAZY on that relationship, then reading A from the database will not read B from
the database until the code attempts to traverse the relationship from A to B. On the other hand,
if fetch is set to EAGER, then reading A from the database will also read the dependent B objects
from the database as well.

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

public class Address implements Serializable {


private Long addressId;
private Set phones = new HashSet();
...
}

// Phone (child) entity

public class Phone implements Serializable {


private Address address;
...
}
• Hibernate conventions
In Hibernate, the default is lazy initialization for a collection that implements a one-to-many
or a many-to-many relationship. To disable lazy (or to enable eager) initialization, map as
follows:
• In parent, use collections (set, bag, or list) with lazy=false attribute.
• In child, use class with the lazy=false attribute to enable fetching.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 27 of 43


developerWorks® ibm.com/developerWorks/

Mapping 20. Lazy initialization (Hibernate XML mapping)


<!-- Address (parent) class -->
<class name="Address" table="T_ADDRESS">
...
<id name="addressId" column="ADDR_TID"/>

<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>

<!-- Phone (child) class -->


<class name="Phone" table="T_PHONE" lazy="false">
...
<many-to-one
name="address"
class="Address"
column="ADDR_TID">
</many-to-one>
</class>
• OpenJPA conventions
In OpenJPA, lazy initialization of one-to-many and many-to-many relationships is also default.
To disable lazy initialization (and enable eager initialization), map as follows:
• Use fetch=FetchType.EAGER attribute on the collection in the parent entity.

Mapping 21. Lazy initialization (OpenJPA XML mapping)


<!-- Address (parent) class -->
<entity class="Address">
<table name="T_ADDRESS"/>
<attributes>
<id name="addressId">
<column name="ADDR_TID"/>
</id>
<one-to-many name="phones" mapped-by="address" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
...
</attributes>
</entity>

<!-- Phone (child) class -->


<entity class="Phone">
<table name="T_PHONE"/>
<attributes>
<many-to-one name="address">
<cascade>
<cascade-all/>
</cascade>
<join-column name="ADDR_TID"/>
</many-to-one>

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 28 of 43


ibm.com/developerWorks/ developerWorks®

...
</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:

Mapping 22. openjpa.DetachState configuration property


<persistence ...>
<persistence-unit name="JPATEST">
<properties>
<property
name="openjpa.DetachState"
value="fetch-groups(DetachedStateField=true,
DetachedStateManager=true,
AccessUnloaded=false)" >
</property>
</properties>
</persistence-unit>
</persistence>

There are three solutions to working with detached objects:

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.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 29 of 43


developerWorks® ibm.com/developerWorks/

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:

1. Trigger loading of the collection by invoking size() on the collection.


2. Use JP-QL with the Fetch Join feature to temporarily override the Lazy fetch type.
3. Use the OpenJPA FetchGroups annotation for loading child objects.

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.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 30 of 43


ibm.com/developerWorks/ developerWorks®

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

public class Address implements Serializable {


private Long addressId;
...
}
• Hibernate conventions
In Hibernate, the object identity maps as follows:
• Use id with nested generator sub-element.

Mapping 24. Object identity (Hibernate XML mapping)


<class name="Address">
...
<id name="addressId" column="ADDR_TID">
<generator class="sequence">
<param name="sequence">T_ADDRESS_SEQ</param>
</generator>
</id>
...
</class>
• OpenJPA conventions
In OpenJPA, that same object identity maps as follows:
• Use Id, SequenceGenerator, and GeneratedValue annotations.

Mapping 25. Object identity (OpenJPA XML mapping)


/<!-- Address entity -->
<entity class="Address">
<sequence-generator name="AddressSeq" sequence-name="T_ADDRESS_SEQ"/>
<attributes>
<id name="addressId">
<column name="ADDR_TID"/>
<generated-value strategy="SEQUENCE" generator="AddressSeq"/>
</id>
...
</attributes>

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

public class Address implements Serializable {


private Long version;
...
}
• Hibernate conventions
In Hibernate, optimistic locking maps as follows:
• Use the version element to define the version property.

Mapping 27. Optimistic locking (Hibernate XML mapping)


<!-- Address class -->
<class name="Address" table="T_ADDRESS">
...
<version name="version" column="VERSION"/>
...
</class>
• OpenJPA conventions
In OpenJPA, optimistic locking maps as follows:
• Use the version element to define the version property.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 32 of 43


ibm.com/developerWorks/ developerWorks®

Mapping 28. Optimistic locking (OpenJPA XML mapping)


<!-- Address class -->
<entity class="Address">
<table name="T_PHONE"/>
<attributes>
...
<version name="version">
<column name="VERSION"/>
</version>
...
</attributes>
</class>

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:

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 33 of 43


developerWorks® ibm.com/developerWorks/

Mapping 30. Pessimistic locking (OpenJPA configuration properties set


programmatically)
import org.apache.openjpa.persistence.*;
...
EntityManager em = ...;
em.getTransaction ().begin ();
...
// load an object with a pessimistic read lock mode
fetch = ( OpenJPAPersistence.cast( em ) ).getFetchPlan();
fetch.setReadLockMode( LockModeType.WRITE );
Address address = em.find( Address.class, addressId );
...
em.getTransaction ().commit ();

Migrating Hibernate configuration parameters


In Hibernate, the most common way to configure the SessionFactory is to include <property>
elements in a hibernate.cfg.xml file and place that file in the root folder on the classpath. Another
less used but equivalent approach is to include properties in a hibernate.properties file on the
classpath.

In OpenJPA, the EntityManagerFactory is configured by including a named <persistence-unit>


element in a persistence.xml file and placing that file on the META-INF folder on the classpath.
The <persistence-unit> defines the persistence provider, the mapping files, and other properties,
such as database connections and logging. The persistence provider identifies the vendor that
implements the JPA specification, and the named properties in the <persistence-unit> will be
specific to that persistence provider, which in this case is OpenJPA.

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:

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 34 of 43


ibm.com/developerWorks/ developerWorks®

• Use dialect configuration parameter.


• Use connection.driver_class configuration parameter.
• Use connection.url configuration parameter.
• Use connection.username configuration parameter.
• Use connection.password configuration parameter.

Configuring 1. Hibernate database connections


...
<hibernate-configuration>
<session-factory>
<property
name="dialect">
org.hibernate.dialect.DB2Dialect
</property>
<property
name="connection.driver_class">
com.ibm.db2.jcc.DB2Driver
</property>
<property
name="connection.url">
jdbc:db2://localhost:50000/HIBTEST
</property>
<property
name="connection.username">
db2admin
</property>
<property
name="connection.password">
db2admin
</property>
</session-factory>
</hibernate-configuration>
• OpenJPA conventions
In OpenJPA, configuring equivalent JDBC connection maps as follows:
• Use openjpa.jdbc.DBDictionary configuration parameter. (This one could be optional
since OpenJPA can normally determine the proper dictionary via the URL and
DriverName properties.)
• Use openjpa.ConnectionDriverName configuration parameter.
• Use openjpa.ConnectionURL configuration parameter.
• Use openjpa.ConnectionUserName configuration parameter.
• Use openjpa.ConnectionPassword configuration parameter.

Configuring 2. OpenJPA database connections


<persistence ...>
<persistence-unit name="JPATEST">
<properties>
<property
name="openjpa.jdbc.DBDictionary"
value="db2(DriverVendor=db2)" >
</property>
<property
name="openjpa.ConnectionDriverName"
value="com.ibm.db2.jcc.DB2Driver">
</property>
<property

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 35 of 43


developerWorks® ibm.com/developerWorks/

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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 36 of 43


ibm.com/developerWorks/ developerWorks®

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

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 37 of 43


developerWorks® ibm.com/developerWorks/

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.

Appendix: OpenJPA Java 5 annotations


Annotation A. Single table inheritance
//Participant (base) class
@Entity
@Table(name="T_PRTCPNT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="PRTCPNT_CLASS")
public class Participant implements java.io.Serializable {
@Column(name="PRTCPNT_TID")
@Id private Long participantId;
...
}

// SalesRep subclass
@Entity
@DiscriminatorValue(value="SALES_REP")
public class SalesRep extends Participant {
...

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 38 of 43


ibm.com/developerWorks/ developerWorks®

// Administrator subclass
@Entity
@DiscriminatorValue(value="ADMIN")
public class Administrator extends Participant {
...
}

Annotation B. Joined inheritance


// Participant (base) class
@Entity
@Table(name="T_PRTCPNT")
@Inheritance(strategy=InheritanceType.JOINED)
public class Participant implements Serializable {
@Column(name="PRTCPNT_TID")
@Id private Long participantId;
...
}

// 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 {
...
}

Annotation C. One-to-one relationship with embeddable objects


// Employee (parent) class
@Entity
@Table(name="T_EMPLOYEE")
public class Employee implements Serializable {
@Embedded private EmployeeRecord employeeRec;
...
}

// EmployeeRecord (child) class


@Embeddable
public class EmployeeRecord implements Serializable {
...
}

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 39 of 43


developerWorks® ibm.com/developerWorks/

Annotation D. Many-to-one relationship


// Address (parent) class

@Entity
@Table(name="T_ADDRESS")
public class Address implements Serializable {
...
@Column(name="ADDR_TID")
@Id private Long addressId;
}

// Phone (child) class


@Entity
@Table(name="T_PHONE")
public class Phone implements Serializable {
...
@ManyToOne
@JoinColumn(name="ADDR_TID")
private Address address;
...
}

Annotation E. One-to-many relationship


// Address (parent) entity

@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;
}

// Phone (child) entity

@Entity
@Table(name="T_PHONE")
public class Phone implements Serializable {
...
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="ADDR_TID")
private Address address;
...
}

Annotation F. Many-to-many relationship


// User (owner/child) entity
@Entity
@Table(name="T_USER")
public class User implements Serializable {
@Column(name="USER_TID")
@Id private Long userId;

@ManyToMany
@JoinTable(
name="T_USER_GROUP",
joinColumns=@JoinColumn(name="USER_TID"),
inverseJoinColumns=@JoinColumn(name="GROUP_TID"))
private Set<Group> groups = new HashSet<Group>();
...

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 40 of 43


ibm.com/developerWorks/ developerWorks®

// Group (non-owner/parent) entity


@Entity
@Table(name="T_GROUP")
public class Group implements Serializable {
@Column(name="GROUP_TID")
@Id private Long groupId;

@ManyToMany(mapppedBy="groups")
private Set<User> users = new HashSet<User>();
...
}

Annotation G. Lazy initialization


// Address (parent) entity

@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;
}

// Phone (child) entity

@Entity
@Table(name="T_PHONE")
public class Phone implements Serializable {
...
@ManyToOne
@JoinColumn(name="ADDR_TID")
private Address address;
...
}

Annotation H. Object identity


// Address entity

@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;
...
}

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 41 of 43


developerWorks® ibm.com/developerWorks/

Annotation I. Optimistic locking


@Entity
@Table(name="T_ADDRESS")
public class Address {
...
@Column(name="VERSION")
@Version private long version;
...
}

Acknowledgements
The authors thank Roland Barcia, Reddy Sripathi, David Wisnesk, and David Zhang for their help
with this article.

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 42 of 43


ibm.com/developerWorks/ developerWorks®

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

© Copyright IBM Corporation 2007


(www.ibm.com/legal/copytrade.shtml)
Trademarks
(www.ibm.com/developerworks/ibm/trademarks/)

Migrating legacy Hibernate applications to OpenJPA and EJB 3.0 Page 43 of 43

You might also like