We'll look into EJB 3.0 persistence and address the concerns of enterprise application developers and architects who are currently using EJB 2.1 CMP. The process of moving from EJB 2.1 persistence to EJB 3.0 can range from coexistence to a full migration and adoption of the new model. Although much has already been written about the new persistence model, specific strategies and development and design patterns have not been detailed. We'll attempt to fill these gaps with concrete solution options while visiting the key benefits that application developers will gain through this migration. We assume that you are familiar with the basic elements of the new persistence API. (Note: Look for an upcoming article on migrating J2EE applications to EJB 3.0 in Java Pro magazine by Debu Panda, a member of the Oracle Application Server Development team, that will post on FTPOnline in July, 2005.)
The real benefits in migrating from an EJB 2 persistence solution using container-managed persistence (CMP) include reduction in the number of artifacts and complexity surrounding beans, interfaces, and home interfaces that are no longer required in the 3.0 approach; the ability to test entities and the application code that uses them outside of the container—the EJB 2.1 persistence solutions have led most applications to avoid strong unit testing of these components; and reduction in unnecessary code that works around the limitations of EJB 2.1 entity beans. These patterns include data-transfer objects (DTOs) and service locator implementations.
These benefits are significant to the developer productivity and overall application quality that comes with improved testing. The challenge is that if you are to see these benefits, the application’s persistence model and the client code that uses the model must be migrated. Unlike the session- and message-driven bean components, the entity bean migration is potentially much more intrusive and should be undertaken with a better understanding of the associated costs.
Migrating from EJB 2.1 to EJB 3.0 CMP involves migrating the model and its mappings, the queries, and the applications’ client code to the persistence layer. The migration can be divided into three basic approaches: 1) migrate the entity DTOs to EJB 3.0 entities; migrate the EJB 2.1 entity beans to EJB 3.0 entities; and 3) create a new EJB 3.0 entity model.
The first two approaches are intended to minimize code changes in your application. The third involves creating a new model, possibly using forward-generation tools to reduce coding and configuration work. Although this may be the most efficient way to create the EJB 3.0 persistence model, it will most likely incur the most migration costs within the application code.
Before we discuss which approach is best for a given application, the issue of DTOs and their role in EJB 3.0 persistence should be clarified. In EJB 2.1, DTOs were required for getting persistent data outside of the container for use in another tier of the application. In EJB 3.0, entities can be serialized and the need to use DTOs is removed. This factor doesn't mean that DTOs cannot be used if they provide necessary decoupling, but it does address the common practice of having DTOs that mirror entity beans and replicate business logic. These will be referred to as "entity DTOs," and they are no longer required.
Another flavor of DTO is the "view DTO," which is an object that does not mirror the CMP entities but instead provides a more coarse-grained view of data that may come from one or more entity beans. View DTOs are used to optimize interaction between tiers. EJB 3.0 does not remove the need for this style of DTO but instead provides facilities to simplify their use. Whereas in traditional applications, the underlying entities are retrieved and populated programmatically into view DTOs, EJB 3.0 query results may now be directly projected onto view DTOs without any additional binding code.
Nonpersistent Classes
In EJB 3.0 an EJB QL query can return results by using nonpersistent classes. In this case, the query statement defines the attributes needed and the selection criteria relative to the persistence model, but instead of returning one of these types, a nonpersistent class is specified. The NEW operator is used for this specification and defines the class and constructor to be used when building these result objects.
SELECT NEW example.EmpView(
e.id, e.firstName, e.lastName,
d.name)
FROM Employee e JOIN
e.department d
WHERE e.salary > 50000
Deciding to migrate your entity DTOs to become your EJB 3.0 entities is a good choice for minimizing changes on the client tier that use these serialized objects. It is also a good choice if your application logic in the EJB tier is minimal or also focused on the entity DTOs.
This approach requires that the entity DTO is a valid JavaBean and maintains a one-to-one correspondence with the data elements in the associated entity bean. It is also highly recommended that a DAO layer be in place, so that the persistent logic can be updated in isolation once the DTOs have been converted. If your application logic makes direct use of EJB 2.1 entities, the techniques described for migrating entity beans may be more suitable.
The first step in the migration process is to mark the entity DTO as a persistent object by adding the @Entity annotation to the class definition. Without any further changes, the entity DTO is now a persistent object that maps to the database, using the EJB 3.0 defaults. The table name is assumed to be the class name, and each public JavaBean getter method is assumed to correspond to a column in the table with the same name as the property. You can customize these default mappings by using @Table, @Column, and other EJB 3.0 annotations.
After each entity DTO is marked as a persistent object and the database mappings are set up correctly, the next step is to define the relationships between the new entities. EJB 3.0 includes a full set of object-relational annotations to identify entity relationships and control their behavior during persistent operations. The <ejb-relation> descriptor in the EJB 2.1 ejb-jar.xml file can be directly mapped onto each pair of entities involved in the relationship. Consider the relationship descriptor shown in Listing 1. This fragment from an Employee entity demonstrates how the Emps-have-Dept role would be mapped:
@Entity
public class Employee {
private Department department;
// ...
@ManyToOne
public Department
getDepartment() {
return department;
}
public void setDepartment(
Department dept) {
this. department = dept;
}
}
Much as how the table and field names are assumed by default and can be overridden, so can the foreign key column names. For these the relationship mapping annotations make use of @JoinColumn to provide column names that are different from the defaults.
It is important to note that at this point, there are no behavioral changes to the existing application. We have merely added metadata to the original entity DTOs that makes it possible to use them as persistent EJB 3.0 entities. Assuming there is a DAO layer in place that manages the entity DTOs, this layer can now be updated to use the EntityManager to retrieve, store, and update the newly annotated persistent objects.
Migrate EJB 2.1 Entity Beans
The other alternative for migrating to the new EJB 3.0 persistence model is to leverage the existing EJB 2.1 entity model. For the purposes of this discussion, we'll assume that only local interfaces to the entity beans are used because these are the most prevalent in today's applications and because entities as remote objects are not supported in EJB 3.0. For those applications using entity beans as remote objects, the migration will involve more-fundamental design changes.
The goal in this type of migration is to minimize the changes to client code, which is bound to the local and home interfaces of an entity:
- Remove the abstract designation on the bean class, and provide concrete implementations of the property accessor (get/set) methods.
- Annotate the bean class with the object-relational annotations that map the entity to the database, and describe its relationships.
- Rename the bean class to be that of the local interface, and remove the local interface to minimize changes to application code that already uses the bean through this interface.
- Convert finders and selects associated with the bean to @NamedQuery annotations on the bean class.
If your entity bean acts as a client to other beans, this code must be migrated as application client code, as discussed later. Consider this EJB 2.1 Department bean:
public abstract class
DepartmentBean implements
EntityBean {
public abstract Long getId();
public abstract void setId(
Long id);
public abstract String
getName();
public abstract void setName(
String name);
public abstract Collection
getEmployees();
public abstract void
setEmployees(
Collection employees);
public void ejbCreate(Long id)
throws CreateException {}
public void ejbPostCreate(
Long id) throws
CreateException {}
public void ejbStore() {}
public void ejbLoad() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setEntityContext(
EntityContext ctx) {}
public void unsetEntityContext()
{}
}
It contains the following finder definitions:
<query&th;
<description></description>
<query-method>
<method-name>findAll
</method-name>
<method-params/>
</query-method>
<result-type-mapping>Local
</result-type-mapping>
<ejb-ql>Select OBJECT(d) From
Department d</ejb-ql>
</query>
<query>
<description></description>
<query-method>
<method-name>findByDeptName
</method-name>
<method-params>
<method-param>
java.lang.String
</method-param>
</method-params>
</query-method>
<result-type-mapping>Local
</result-type-mapping>
<ejb-ql>SELECT DISTINCT
OBJECT(d) FROM Department d
WHERE d.name = ?1</ejb-ql>
</query>
By following the steps listed previously, we end up with the code you see in Listing 2.
Of special interest is the use of EJB 2.1 container-managed relationships (CMR). In EJB 3.0, relationship management is performed with standard Java coding practices. It is the responsibility of the developer to ensure that both sides of a relationship are updated correctly when relationships are formed. Generally speaking, one additional line of code is required for every relationship operation to ensure that the model is consistent. With respect to collections, we recommend the use of helper methods such as add and remove instead of direct manipulation of the collection in client code:
@Entity
public class Department {
private Collection<Employee> employees;
// ...
public void addEmployee(
Employee emp) {
employees.add(emp);
emp.setDepartment(this);
}
}
Besides these more common migration changes, there is additional EJB 2.1 entity bean implementation that may require work. These include the use of the EntityContext within the bean itself and ejbHome methods. An EJB 3.0 entity will not have an EntityContext. The entity can simply use "this" to resolve to itself and pass itself into other beans’ methods. Any ejbHome methods implemented can simply remain as is or be converted into static methods to make their use more obvious.
Migrate EJB 2.1 Entity Homes
The home interface is the primary mechanism that enables application code to locate and manage entity beans. Because EJB 3.0 has nothing equivalent to home interfaces, it also presents the greatest challenge in migration.
To minimize changes to client code, the easiest approach to migrating homes is to create a helper class that holds onto an EntityManager and implement the methods called on the home by the application code. The example shown in Listing 3 demonstrates a helper class that functions as a replacement home.
This helper class leverages the named queries we defined on the entity class to implement finders, although dynamic queries could also have been used. The remove method on this helper replaces the EJBLocalObject.remove() previously on the entity bean. It is important that all calls to the bean’s remove method be migrated to call this one provided on the helper or to directly make the call with the EntityManager.
The application code that makes use of EJB 2.1 entity beans is coupled tightly to the Home and Local or Remote interfaces of the entity beans. The migration of this code is least likely to be automated; however:
- Home interface lookup and query execution must be modified to either directly use the EntityManager or a Home-helper class that is not managed by the container.
- Code using the entity beans interfaces must be changed to use the new EJB 3.0 entity. The entity class can be the interface, the entity DTO, or some new entity bean class. The selection and intrusiveness of these changes will depend on the chosen model-migration approach.
- Entity DTO detachment and attachment code can be removed, in favor of detachment/attachment of the entities.
- View DTO creation code can be replaced with EJB QL queries that dynamically create the nonpersistent view objects.
Although it is not possible to dive into all of the details of a migration from EJB 2.1 CMP to the new EJB 3.0 persistence model, we have addressed the major issues. Understanding the differences between the models and migration options available is key to planning such an effort. As the persistence vendors continue to make early previews of the technology available, more detailed technical information and tooling to assist in this migration will become available.
Those wanting to try these migration steps today require an EJB 3.0 preview and an IDE with good refactoring and J2SE 5.0 support. Oracle's EJB 3.0 preview and technical information are available at http://otn.oracle.com/ejb3. For those wanting to get more involved in the EJB 3.0 persistence specification, the drafts are available at http://java.sun.com/products/ejb, and you can provide feedback at ejb3-feedback@sun.com.
About the Authors
Merrick Schincariol is a senior engineer for Oracle Application Server and one of the lead developers of the Oracle Application Server EJB 3.0 Preview. Doug Clarke is a senior principal product manager for the Oracle TopLink product, with extensive development, consulting, and educational field experience focusing on persistence. Contact Merrick at merrick.schincariol@oracle.com, and contact Doug at douglas.clarke@oracle.com.