Migrating legacy Hibernate applications to OpenJPA and EJB 3.0

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. This content is part of the IBM WebSphere Developer Technical Journal.

Donald Vines (dhvines@us.ibm.com), Executive IT Architect, IBM

Donald Vines is currently an Executive IT Architect at IBM responsible for the WebSphere migration practice within North America.


developerWorks Contributing author
        level

Kevin Sutter, Senior Software Engineer, IBM

Kevin Sutter is a Senior Software Engineer in the WebSphere Application Server development group. He is currently the lead architect for the Java Persistence API solution for WebSphere Application Server. Kevin is also a committer and a member of the PMC for the Apache OpenJPA project. Kevin's past roles include leading and architecting the WebSphere solutions for J2EE Connector Architecture (JCA) and WebSphere eXtremeScale (caching framework).



22 August 2007

Also available in Chinese Russian Japanese

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 Resources 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 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.hibernatejavax.persistenceDescription
cfg.ConfigurationPersistenceA 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.
SessionFactoryEntityManagerFactoryProvides 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.
SessionEntityManagerProvides APIs to store and load entities to and from the database. It also provides APIs to get a transaction and create a query.
TransactionEntityTransactionProvides APIs to manage transactions.
QueryQueryProvides 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.

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

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

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:

  1. Single table inheritance
  2. 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 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.
    • 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
    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.
    • 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 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:

  1. One-to-one
  2. Many-to-one
  3. One-to-many
  4. 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.
    • 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.

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

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

    1. Use a join table mapping and add a join table to the database, or
    2. 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 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 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.
    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>

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

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

    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 Resources 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 Resources).

  • Hibernate conventions

    In Hibernate, configuring JDBC connection maps as follows:

    • 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 
                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 Resources) 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 { 
   ...
}

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

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

Resources

Learn

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Open source
ArticleID=248401
ArticleTitle=Migrating legacy Hibernate applications to OpenJPA and EJB 3.0
publish-date=08222007