Migrating JDBC Data Access Objects to use EJB3
In this article, we'll discuss what you need to do to migrate your DAO-based application to the EJB3 Java Persistence API.
Persistence is one of the greatest challenges for building applications. There are many options for building persistence layers. Data Access Objects (DAO) is a popular design pattern for building the persistence layer of a J2EE application. Developers use this design pattern primarily to separate their JDBC code from business logic. The EJB3 Java Persistence API, which defines the persistence API for the Java platform based on O-R solutions such as Oracle TopLink and JBoss Hibernate, lets developers skip the mundane task of building DAO and JDBC code. Most developers will be using the EJB3 Java Persistence API instead of writing JDBC code with DAO.
A few months back I migrated the J2EE blueprint application Java Adventure Builder 1.0.1 consumer Web site module to use the EJB3 Java Persistence API. In this article, I’ll discuss what you need to do to migrate your DAO-based application to the EJB3 Java Persistence API.
Why Migrate?
If your application is running fine with JDBC DAOs, why should you migrate it to the EJB3 Java Persistence API? Here are a few reasons.
- You can reduce the number of artifacts and complexities involved in maintaining DAO code. For example, after I migrated the Adventure Builder Consumer Web site from DAO to the EJB3 Java Persistence API, I had 16% less code and 36% fewer numbers of Java classes (files) to manage.
The table below compares the lines of code before and after migration. I didn’t focus on redesigning the object or data model; instead, I concentrated on migrating existing code to restore the same functionalities.
|
DAOs (Classes/Lines of Java code) |
EJB3 (Classes/Lines of Java code) |
EJB3 (Entities) Module | 0/0 |
9 / 680 |
Web Module | 64 / 3062 |
34 / 2097 |
DAO Factory | 3 / 220 |
0/0 |
Total | 67/3284 |
43/2777 |
- Using JDBC API and native SQL code in your application can make it less portable among database vendors. Conversely, EJB3 containers such as Oracle Application Server 10g that use Oracle TopLink as the persistence provider are certified with many databases.
- EJB3-compliant persistence engines such as Oracle TopLink and JBoss Hibernate offer benefits including improved caching, performance and scalability.
The EJB3 Java Persistence API: An Overview
Much has been said about EJB 3.0 recently. EJB 3.0 is standardizing the persistence API for the Java platform. The persistence objects, called entities, are nothing but POJOs (Plain Old Java Objects) and are annotated with @Entity. An entity can embed one or more persistence objects inside; such an object is called an embedded object. The EJB3 Java Persistence API supports basic object-oriented concepts like inheritance and polymorphism with EJB3 entities.
You can define a relationship between different entities. The EJB3 Java Persistence API defines O-R mapping using Java metadata annotations or XML descriptors. Here’s a simple example of an entity with property-based access using metadata annotations for O-R mapping.
@Entity @Table(name = "SIGNON") public class User implements java.io.Serializable { protected String userId; protected String password; public User () {} public User (String userId, String password) { this.userId = userId; this.password = password; } @Id @Column(name="USERNAME") public String getUserId() { return userId; } public String getPassword() { return password; } public void setUserId(String userid) { this.userId = userid; } public void setPassword(String password) { this.password = password; } }
The EJB3 Java Persistence API introduces the EntityManager API, which is used for CRUD operations of entities. The EntityManager API is invoked in a transaction context. You can invoke it outside of the EJB container — for instance, from a Web application — without needing a session bean façade.
Queries can be created dynamically or can be saved as named query in the entities. You can either use EJBQL or Native SQL to query entities. Also, EJB 3.0 addresses many limitations of EJBQL that it had earlier.
Now that I’ve provided an overview of the EJB3 Java Persistence API, I’ll show how you can migrate an application using DAO to the EJB3 Java Persistence API. I’ll use the consumer Web site module of Java Adventure Builder as an example.
Migrating your applications to use EJB3 Persistence
If you decide to use the EJB3 Java Persistence API in your DAO-based application, you have two options: You can either completely replace DAOs with the EJB3 Java Persistence API, or you can keep using DAOs and migrate only the database-specific DAO code that you want to use the EJB3 Java Persistence API. In this article, I’ll discuss both approaches.
To complete the migration, you’ll need to take the following steps:
- Convert your persistence (value or transfer) objects to entities
- Migrate your queries to use EJBQL
- Update your applications to use the EJB3 EntityManager API
I’ll discuss each of these steps in the following section.
Convert your Java Objects to EJB3 entities
First, you must convert your value objects to EJB3 entities, and then map those to your database schema. Doing so involves the following steps:
- Identify the objects and the relationships between them. This most important step in migrating your application requires that you understand your applications and object and data model. The EJB3 Java Persistence API lets you map one or more Java objects into the same table as entities or embedded objects. You must understand the object model and identify the appropriate relationship between these objects. Examine your objects carefully; you might decide to remodel your objects while migrating to use EJB3 entities.
- Create persistent attributes and/or missing Setter and Getter methods for your entities. The EJB3 Java Persistence API lets you define two access types: field or property type. The access type is determined depending whether metadata annotations are used on the fields or properties. Field based access is easier to use but property based access allows data hiding. If you decide to use access type as property, you must define persistence properties (setter/getter methods) attributes for entity and use the mapping metadata annotations with the properties.
The EJB3 Java Persistence API lets you create the O-R mapping metadata using either metadata annotations or XML descriptors. I’ve used metadata annotations in my examples.
- Create entities and O-R Mapping metadata. The final step is to convert the persistence objects to entities. Some of your objects will become entities while others may become embeddable objects. An embeddable object is a persistent object with no identity of its own; it’s part of another entity. For example, Java objects such as Activity, AdventurePackage, Lodging, Transportation, Account and User become entities. Both Account and ContactInformation are mapped to the same database table and hence ContactInformation becomes an Embeddable.
Following is the example code of an entity Account that uses an Embeddable type ContactInformation. In this example, I’ve used metadata annotations, although some people may prefer to use XML descriptors to define O-R mapping.
@Entity @Table(name = "ACT") public class Account implements java.io.Serializable { protected String userId; protected ContactInformation info; public Account () {} public Account (String userId, ContactInformation info) { this.userId = userId; this.info = info; } @Id @Column(name="USERID") public String getUserId() { return userId; } @Embedded public ContactInformation getContactInformation() { return info; } ... ... }
Here’s the code example of an embeddable object used by Account entity:
@Embeddable @Table(name = "ACT") public class ContactInformation implements java.io.Serializable { protected String telephone; protected String email; protected String familyName; protected String givenName; public ContactInformation() {} public ContactInformation(String familyName, String givenName, String telephone, String email){ this.givenName = givenName; this.familyName = familyName; this.email = email; this.telephone = telephone; } @Column(name="FIRSTNAME") public String getGivenName(){ return givenName; } ... ... }
After you convert your objects to entities, you must create a persistence unit to package these entities. The entities can be packaged in any type of J2EE module such as WAR, EJB-JAR or a normal jar that is packaged or referenced by a J2EE module.
Migrating queries from DAO to EJB3 Named Query
The basic CRUD operation for persistence is handled by the EJB3 Java Persistence API; you don’t have to perform the mundane task of writing JDBC code to create or query objects. However, DAOs might be using some queries that you must migrate in order to use a named query with your entities. When you used EJB3 persistence, queries are expressed using EJBQL.
For example, the AdventureBuilder application has a method PointbaseCatalogDAO.getTransportations that finds all available flights using a SQL as follows:
SELECT transportationid, name, description, imageuri, price, origin, destination,carrier,departuretime, arrivaltime, class FROM Transportation WHERE origin = ? AND destination = ? AND locale = ? This must be migrated to use a named query on the entity, as follows: @NamedQuery( name="getTransportations", queryString="SELECT t FROM Transportation t WHERE t.origin = :origin and t.destination=:destination and t.locale=:locale" )
Updating the Application Code to use EntityManager API
Finally, you must update the application to use the EntityManager API. You have two options: either update the DAO code to use EntityManager API in place of JDBC code or replace the DAO code completely with EntityManager API.
The DAOs are used either to persist database changes or query the database. You’ll need to modify your application to use the appropriate EntityManager API.
DAOs are transactional objects and EntityManager API is invoked in a transactional manner so you probably won’t have to change the transactional semantics of your application.
Modifying DAO to use EntityManager API
DAO enables you to keep your application code separate from your persistence logic. If you use this approach there will be no impact on the application code using the DAOs. You can use either a container-managed or application-managed entity manager and you can use either JNDI lookup or dependency injection to grab an instance of EntityManager. Details discussion of EntityManager and persistent units is not within the scope of this article.
Here are the steps to take to use EntityManager API from the DAO:
- Grab an instance of EntityManager. Replace the JDBC code for getting a DataSource connection to grab an instance of EntityManager API. EntityManager is responsible for performing CRUD operations for your persistent data.
- Persisting data. Replace the JDBC code for insert, update, delete, etc to use EntityManager API.
- Retrieving data. To retrieve data when using JDBC, you always write SQL. When migrating the code to use the EntityManager API, you have two ways to retrieve data:
- Find by primary key. You can use the find( Class entityClass, Object primaryKey ) method of EntityManager to retrieve entity instances. For example, if you have a getAccount() method in the DAO, the migrated code for the getAccount() will look like follows.
- By any arbitrary query. You can use either a dynamic or a named query. For example, we identified a named query earlier for retrieving Transportation that matched particular criteria. You can modify the code to use the named query or a dynamic query instead of the JDBC methods.
account = (Account) em.find(Account.class,userId);
If you are using a container-managed entity manager you can grab an instance of entity manager by using dependency injection as follows:
@PersistentContext(unitName="adventureBuilder") EntityManager em; The following is an example code for grabbing an instance of Application Managed EntityManager API: @PersistenceUnit(unitName="adventureBuilder") private EntityManagerFactory emf; private EntityManager em = emf.createEntityManager();
You should close an instance of application-managed entity manager by invoking em.close() method after you are done.
Note that you can use dependency injection only in managed classes such as servlets, ejbs, etc. and not in regular Java classes and you have to use JNDI to look up an instance of entity manager as follows:
context = new InitialContext(); em = (EntityManager)context.lookup(JNDINames.AB_PM);
Note that I’ve ignored the code required for exception handling.
For example, one of DAO code has as an insert statement as follows to create an instance of Account in the database in the createAccount method:
private final static String INSERT_ACCOUNT_QUERY_STR = "INSERT INTO " + DatabaseNames.ACCOUNT_TABLE + "(userid,email,firstname,lastname," + "addr1,addr2,city,state,zip,country," + "phone)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; private void insertAccount(Account details) { ... stmt = dbConnection.prepareStatement(INSERT_ACCOUNT_QUERY_STR); stmt.setString(1, details.getUserId().trim() ); stmt.setString(2, info.getEMail().trim() ); stmt.setString(3, info.getGivenName().trim() ); stmt.setString(4, info.getFamilyName().trim() ); stmt.setString(5, info.getAddress().getStreetName1().trim() ); }
The migrated code to use EntityManager API will look like:
private void insertAccount(Account details) { ... em.persist(details); }
Similarly you can use remove and flush or merge methods to migrate the JDBC code for remove or update operations. You can use the remove(Object entity) method to delete a entity from the database and use flush(Object entity) method to causes updates to be sent to the database before the transaction completes and use merge(Object entity) to update the entity instance acquired in a different transaction.
Query transQuery = em.createNamedQuery("getTransportations"); transQuery.setParameter("origin",origin); transQuery.setParameter("destination",destination); transQuery.setParameter("locale",locale.toString().trim()); List trans = transQuery.getResultList();
Replacing DAO with EntityManager API
The steps are quite similar to updating the DAO to use EntityManager API but it may require more work and will make your persistence code closely tied with your applications. Many developers use the façade pattern so they can use DAOs without cluttering their applications with DAO code. These facades use the DAO APIs to create or query persistent data in an application. If your applications use these DAO façades, it will be very easy to migrate to the EJB3 Java Persistence API.
Here are the steps to take to use EntityManager API instead of DAO
- Grab an instance of EntityManager. Instead of creating an instance of your DAO, you must grab an instance of EntityManager. For example, the constructor method of AdventureBuilder CustomerFacade used the following code to create an instance of AccountDAO:
- Persisting data. You must change the code to use the EntityManager API to persist data instead of using the DAO.
- Retrieving data. As we discussed earlier, you always write JDBC code to retrieve data when using DAO and EntityManager API provides two ways to retrieve data:
- Find by primary key. You can use the Find method of EntityManager to retrieve entity instances. You must change the code using the EntityManager API. For example, if you have a getAccount() method in the DAO, this is how you’d get the account by its primary key:
- By any arbitrary query. As we discussed earlier, you can use either a dynamic or a named query. You must modify the code to use named queries or dynamic queries instead of the DAO methods.
accountDao = (AccountDAO) DAOFactory.getDAO(JNDINames.ACCOUNT_DAO_CLASS);
If the façade is a managed class then you can use dependency injection as we discussed earlier. If a regular class then we can use JNDI and the migrated code will look like:
context = new InitialContext(); em = (EntityManager)context.lookup(JNDINames.AB_PM);
For example, if your code uses DAO to create an account in the database, your code will look like this:
accountDao.create(accountDetails);
You can change that code to use EntityManager API, like this:
em.persist(accountDetails);
account = accountDao.getAccount(userId);
The migrated code will look like this:
account = (Account) em.find(Account.class,userId);
After you update the application code to remove the DAO methods and application, use the EntityManager API; the DAOs are candidates for removal.
Summary
In this article, I’ve discussed the steps required to migrate DAO applications to the EJB3 Java Persistence API. Since each J2EE application is unique, however, it’s difficult to outline all aspects of the migration process. Understanding EJB3 persistence and your applications, as well as following the steps in this article, should help make your migration project run smoother.
Acknowledgements
The author acknowledges help provided by Douglas Clarke during the migration project and Mike Keith for reviewing the article.
Resources and References
EJB3 Proposed Final Draft Specification
GlassFish Project – Entity Persitence Support
An Adventure with J2EE 1.4 Blueprints
About the Author
Debu Panda is a principal product manager of Oracle Application Server development team, where he focuses his efforts on the EJB container and Transaction Manager. He has more than 13 years of experience in the IT industry and has published articles in several magazines and has presented at many technology conferences. His J2EE-focused weblog can be found at http://radio.weblogs.com/0135826/.