Inheritance & EJBs

Java Development News:

Inheritance & EJBs

By Daniel O'Connor

01 Jun 2002 | TheServerSide.com

Introduction

One of the most common criticisms of entity EJBs is that they don't support inheritance or polymorphism. If this were true, it would be a serious complaint for two reasons. First, entity beans are intended to provide an object-oriented view of information in a persistent store, and inheritance and polymorphism are key aspects of the object-oriented programming paradigm. Second, many bean developers are using relational database schemas that essentially implement a form of inheritance, and they need to be able to operate on these schemas with their EJBs. However, in practice, entity EJBs are capable of meeting these requirements.

In this article I'm going to show how to implement entity bean inheritance with three CMP EJBs: a "base class" PARTY EJB and two "derived class" EJBs: PERSON and CORPORATION. (This is a common design in relational database schemas.) Furthermore, I'll demonstrate a polymorphic finder query and a couple of polymorphic method calls. The beans I produce will be absolutely portable according to the EJB 2.0 specification.

I'll also show how these container-managed persistence beans can be mapped to a standard relational database schema for this type of data. The EJB specification does not require particular object/relational mapping capabilities from an EJB container's CMP engine. It does not even require that the container be able to use relational databases at all, so the ability to map to a particular schema is not portable. However, I am the author of a pluggable CMP engine that provides this capability (the MVCSoft Persistence Manager). We'll look at how this engine is able to make the sometimes-difficult translation from EJB objects and relationships to database tables, and what is involved from the user's point of view in using this particular mapping.


Some Distinctions

It's important to be clear exactly what we mean when we talk about inheritance. In this case there are three possibilities, and this article is mostly about a single one of these possibilities. First, there is the language inheritance with which every competent Java programmer is familiar. The Java language allows a class to extend a single immediate base class, which can in its turn extend another single base class, until the universal base class of java.lang.Object is reached. It also allows an interface to extend one or more interfaces. Finally, it allows a class to implement any number of interfaces. The EJB specification allows for any of the interfaces or implementation classes associated with a bean (remote home and component interfaces, local home and component interfaces, primary key class, and bean implementation class) to take advantage of these Java language features. (There are a few caveats, such as requirements for the return types from create methods.)

Second, there is an amorphous concept called "component inheritance." Although frequently used (even once in the EJB spec itself), there is no generally accepted definition of the term in this context. It would probably include (1) the ability to extend an existing entity without modifying the base component or even having the source code; (2) the ability to compare component interfaces of different types for equality; and (3) the ability to return different component interfaces from a single finder or ejbSelect method.

Third, there is "data model inheritance." This is the ability to model an inheritance hierarchy of business objects (and their polymorphic behavior) using entity EJBs. It is this last type of inheritance that this article addresses. Note that whereas language inheritance and component inheritance might be considered "implementation details," data model inheritance directly affects what can and cannot be accomplished using EJBs.


Why the Criticism?

If entity EJBs are capable of inheritance and polymorphism, why do people frequently say they are not? One source for this criticism can be found right in the EJB 2.0 specification. Section 11.2.11 says, "The data model for container-managed persistence does not currently support inheritance. Therefore, entity objects or value classes of different types cannot be compared. EJB QL queries that contain such comparisons are invalid." Furthermore, the list of features deferred to future releases includes "support for component-level inheritance."

In fact, there is no framework support in the EJB spec for inheritance and polymorphism. This has consequences for all three types of inheritance discussed above. It makes component inheritance--by any reasonable definition--impossible. It places limitations on how language inheritance is used. It requires the bean developer to implement data model inheritance on his or her own, while being mindful of the limitations of the EJB component model. This lack of framework support, and the consequences, are the issues that ultimately generate the criticism.

But that's a far cry from saying that entity EJBs are incapable of representing an inheritance hierarchy, or polymorphic behavior, in a data model. When enterprise software developers hear about the lack of inheritance in EJBs, they most likely assume that EJBs cannot work with their existing schema. But they can, and this is the important point by far. In practice, it's not that significant that you don't have "black box" component-level inheritance for EJBs. What's important is that you can represent your complete existing enterprise schema using EJBs.


How to Do It

It's not complicated to implement a schema incorporating inheritance. The fundamental technique is called "containment." This just means that your base EJB component will have a reference to its derived class or classes, and will delegate calls as appropriate. The base class will need some way of determining how to delegate the call to a related "derived" class. Typically, there will be a field (called a discriminator) that establishes the type of the base class. In its simplest form, a business method would check this discriminator, retrieve the appropriate related entity component interface from a cmr field, and delegate the call to this interface. For example, a business method call on a PARTY component interface would be translated by the container to a call on the PARTY implementation class. The PARTY implementation class might check a discriminator CMP field, and depending on the result, delegate the business method call to the PERSON cmr field or the CORPORATION cmr field.

Polymorphic relationships between entities will actually be between base EJB components, and polymorphic EJB-QL queries will return references to the base EJB component. For instance, if there were an ORDER component, it might have a relationship to a PARTY bean, rather than either a PERSON or CORPORATION bean. Or if you called a finder that retrieved all the orders in Ohio, you would query on the PARTY bean rather than the PERSON or CORPORATION bean.

It is possible that there might be methods defined on a subclass component interface that aren't defined on the base component interface. You can't use Java language casting to downcast from the base interface to the subclass interface...but you could implement a business method that returned the desired derived component interface. An elegant signature might be EJBLocalObject dynamicCast( String type ).

The example code for this article implements four EJBs: a session bean fafacadeade and the three entity EJBs (PARTY, PERSON, and CORPORATION). A client will ask the session bean facade to create some sample data: three people and three corporations. It will then execute a findAll() query that returns all six of these EJBs in a single result set. It will call two polymorphic business methods, one getting a display name and one getting a credit rating. There is nothing special about any of the code, but I'll review the parts that are relevant to inheritance or polymorphism.

First, I have provided a business interface from which every local component interface in the inheritance hierarchy derives. (See PartyIntf.java.) This is not a requirement, but it allows us to treat any local component interface in the hierarchy as a single type with polymorhic methods. If I had not done this, the business methods of all three of the component interfaces would still function correctly, but the client would not be able to treat a party component interface, a person component interface, and a corporation component interface as the same type. Really, this is just syntactic sugar.

In this example, the polymorphic behavior is implemented entirely in the PARTY bean implementation class. (See PartyBean.java.) There is a cmp field "typeCode" that indicates the derived type of the entity. There are also two cmr fields to contain the derived instance: corporation and person. In this example, they are mutually exclusive and one of them will be null, but this is not a fundamental requirement. (In other words, you can implement multiple inheritance if your schema requires it.)

There are two business methods: getDisplayName() and getCreditLimitCode(). When either of the business methods is called, the typeCode cmp field is checked and the business method is delegated appropriately. For example:


public String getDisplayName()
{
  String code = getTypeCode();
  if (code.equals("I"))
    return getPerson().getDisplayName();
  else if (code.equals("C"))
    return getCorporation().getDisplayName();
  throw new EJBException( "Unknown type" );
}

You could make a case that the typeCode is redundant and it should be possible to check which relationship is not null. But if the cmr field is being lazily loaded by the EJB container, this could result in unnecessary database I/O.

The findAll() query (used by the session bean facade and called by the client) is defined on the PARTY local home interface. Of course, it is only returning component interfaces for the PARTY EJB, but these represent both PERSON instances and CORPORATION instances. It is defined in the deployment descriptor as a query returning PARTY instances, as follows:


<query>
  <query-method>
    <method-name>findAll</method-name>
    <method-params/>
  </query-method>
  <ejb-ql>select object(pb) from PartyBean pb</ejb-ql>
</query>

The last interesting pieces of code from this example are the ejbCreate and ejbPostCreate methods for the CORPORATION and PERSON beans. As well as creating the corporation or person instance, respectively, these methods need to create the corresponding PARTY instance and establish a one-one relationship between the two. You might wonder if any special technique is required, considering that the relationship between a PARTY instance and a CORPORATION or PERSON instance is likely implied by a migrated key, rather than being explicitly stored in a separate foreign key. But this is a detail of the object/relational mapping, and doesn't enter into consideration when you are coding your component. Let's take a look at CorporationBean.java's ejbCreate method:


public Integer ejbCreate( ViewCorporation view )
    throws CreateException
{
  getPartyLocalHome().create(new ViewParty(view.getId(), "C"));
  setViewCorporation( view );
  return null;
}

This is just one possible implementation, but the basic steps will usually be the same. In the ejbCreate method, we create an instance of the PARTY base component, and initialize the cmp fields for this component. Then in the ejbPostCreate method, we establish the relationship between the two:

public void ejbPostCreate( ViewCorporation view )
  throws CreateException
{
  try
  {
    PartyLocal party = getPartyLocalHome().findByPrimaryKey(view.getId());
    setParty( party );
  }
  catch (FinderException e)
  {
    throw new EJBException( "Party not created" );
  }
}

Object/Relational Mapping

For most enterprise developers, modeling inheritance and polymorphic behavior in EJBs is just half the battle. The other half is working with an existing schema. The code with this article should work with any EJB container, but there are no requirements in the EJB spec for any set of mapping capabilities. In practice, you'll find that there is substantial variation in capabilities between implementations, and there are three potential trouble spots here.

First, the most common database schema for this model will have a migrated primary key that establishes the relationship between the base and derived entities. In the EJB world, primary keys and relationships are completely unrelated. The persistence mechanism for your EJB container must be smart enough to realize that in this case, the primary key and relationship information in the persistent store are conflated.

Second, the typical database schema for this model will have foreign-key constraints on the migrated primary key. This requires that the database inserts take place in the correct order (to not violate these constraints). If the EJB container triggers the insert statements immediately on return from ejbCreate, you must make sure that your code is ordered correctly. The persistence manager I wrote (the MVCSoft Persistence Manager) can cache the data and write it out to the database at the end of the transaction, dynamically reordering the database i/o so as to not violate any constraints.

Finally, if your EJB container is lazily-loading relationships for large result sets, it can lead to unacceptable performance. In our example, each call from the PARTY bean to getCorporation() or getPerson() might result in a select query to the database to get the information. There are several possible solutions to this problem. For example, the MVCSoft Persistence Manager allows you to define a "fault group" for a query that defines the information you will need during the transaction, and this fault group can extend to related information. The runtime will eagerly load the required data using a minimum number of queries. If you issued a finder query that returned a hundred results and called a polymorphic method on each instance, a properly configured runtime will issue three queries, rather than 101 queries as in the lazy loading case.


Conclusion

Despite what you may have heard, entity EJBs can be used effectively to represent business objects with inheritance and polymorphic behavior. There is no support for this functionality in the EJB component model, but one simple technique--called containment--is to delegate polymorphic business method calls to appropriate "derived" component instances. The EJB specification does not mandate specific object/relational mapping functionality, but there are products that can work with your existing relational database schema.