As enterprise applications become more complex, it becomes more challenging
to map their object models to relational database tables. In typical J2EE
applications, developers use EJB entity beans as the abstraction for the
underlying object store. Unfortunately, entity beans, as of EJB 2.0, have
limited mapping capabilities. For example, custom data conversions (such as
from Currency objects to Strings) and component inheritance (Manager is an
Employee) are not directly supported. When the object models require these
features, developers have to write and maintain extra code. Even so, the
results are often not satisfactory.
Fortunately, there are alternatives. These persistence frameworks allow
developers to map normal Java objects to relational tables with minimal effort.
The Java standard for such a framework is called JDO (Java Data Objects), and it
is maturing. Unfortunately, there is no complete, free implementation of JDO
today. This makes evaluating this emerging technology more difficult. However,
with some planning and the use of best practices, it is not difficult to
develop your application prototype with a non-standard persistence framework,
later switching to an industrial-strength JDO implementation if it suits your
business.
Three of the most popular persistence frameworks in the open source
community are Hibernate, Castor, and OJB. In this article, we will focus
on OJB. OJB integrates smoothly into J2EE containers with full support of JTA
and JCA, and is a viable alternative to EJB entity beans. Since OJB does not
modify your source code or byte code in any way, you can make an object
persistent even without access to the original source code. This article
introduces OJB and focuses on its component-inheritance capabilities. The
Appendix presents some best practices to show you how to decouple your
application from the specifics of a persistence layer.
1. Obtaining OJB
OJB can be downloaded from the Apache Jakarta Project home page. We
will use version 0.9.7, the latest as of this writing, throughout this article.
As a member of the Jakarta family, OJB makes extensive use of the Jakarta Commons Library. This is
good news to everyone who is already familiar with the Commons library through
other Jakarta projects like Struts.
Installation is a snap. You only need to unzip the downloaded file into any
directory and you are ready to go.
2. Component Inheritance
Component inheritance represents an "IS-A" relationship between two objects
in the object store. EJB entity beans do not directly support component
inheritance. You can mimic the relationship in entity beans by creating a
one-to-one relationship, delegating all base class getters and setters to the
base class entity bean. Unfortunately, this is only a halfway solution, because
EJB finders cannot return the proper object type.
To showcase the component inheritance feature of OJB, we will use an example
throughout this section. We will walk you through the entire process of
creating and running this example to help you understand the steps involved.
The example attempts to persist two objects: Manager and Employee. They are
related by a "IS-A" relationship. See Figure 1 below.
Figure 1: The Employee and Manager classes
2.1 Interfaces
Example 1: Component Inheritance Example Interfaces
public interface Employee
{
public String getName();
public void setName(String name);
}
public interface Manager extends Employee
{
public Integer getProjectNumber();
public void setProjectNumber(Integer projectNumber);
}
The first step in our example is to create interfaces for our object model.
It is always a good idea to have interfaces for your object model because
interfaces allow your implementations to evolve without causing breakage in
other areas of your system. This is especially true for objects in the
persistence layer. For example, a version timestamp is used to support object
versioning in the persistence layer. It is important in the persistence layer,
but not so in the business layer. Using interfaces allow you to change the
object versioning scheme without breaking the business layer.
Using interfaces also allows you to switch the underlying persistence
framework with mininal effort.
2.2 Base Class Implementation
After the interfaces are done, we are ready for the implementation. Since
different mapping techniques require slightly different implementations (see next page), a base class implementation will be helpful to house the common functionalities.
Example 2: Base Class Implementation
public class BasePersistenceObjectImpl
{
private Integer id;
public BasePersistenceObjectImpl()
{
super();
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
else if (o != null && o instanceof BasePersistenceObjectImpl)
{
return ((BasePersistenceObjectImpl) o).id.equals(this.id);
}
else
{
return false;
}
}
}