ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

EJB Inheritance, Part 1
Pages: 1, 2, 3, 4, 5

5. Finders & ejbSelects

Working out inheritance in finders and ejbSelect methods is not so simple. Finders are written by the container, using EJB-QL provided in the deployment descriptor. There's no hook for us to exploit.



In reality, a finder exists to find objects of a single type. This is highlighted in our example by the fact that each finder bares the name of its returned object; for example, findAllGoldCustomers(). Finders cannot find objects from the base type and all of its sub-types; they were not made for that. Queries are not polymorphic.

A solution is to use what I call locate methods -- essentially "smart finders," which know how to handle the base class' and subclasses' finders in order to collect the results properly. Locate methods have to know about subclasses, special finders that may be used depending on the table mapping, etc. Some other things about locate methods:

  • They are home methods.
  • They start with the prefix "locate".
  • The corresponding bean implementation method name would have the prefix ejbHomeLocate.
  • One would call a finder method to do non-polymorphic finds and a locate method to do polymorphic finds.

Locate methods are not natural in the world of inheritance and polymorphism, but they are necessary. I am working on ways to make the task of authoring locate methods easier. I plan to report my findings in future articles.

Let's see some examples of finders and locate methods.

First, the home interfaces:

Example 8. BaseCustomerHome

//...
public interface BaseCustomerHome extends EJBLocalHome {
//...
  public BaseCustomer findByPrimaryKey(String customerID)
    throws FinderException;

  public Collection findAllBaseCustomers()
    throws FinderException;

  public BaseCustomer locate(String customerID)
    throws FinderException;

  public Collection locateAll()
    throws FinderException;
}

Example 9. PlatinumCustomerHome

//...
public interface PlatinumCustomerHome extends EJBLocalHome {

//...
  public PlatinumCustomer findByPrimaryKey(String customerID)
    throws FinderException;

  public Collection findAllPlatinumCustomers()
    throws FinderException;
}

Note that the finders are implemented by the container based on these query definitions:

Example 10. Query definitions for BaseCustomer

<query>
  <query-method>
    <method-name>findAllBaseCustomers</method-name>
    <method-params>
    </method-params>
  </query-method>
  <ejb-ql>
    <![CDATA[SELECT OBJECT(c) FROM BaseCustomerEJB AS c</XMLCDATA>
  </ejb-ql>
</query>

Example 11. Query definitions for PlatinumCustomer

<query>
  <query-method>
    <method-name>findAllPlatinumCustomers</method-name>
    <method-params>
    </method-params>
  </query-method>
  <ejb-ql>
    <![CDATA[SELECT OBJECT(c) FROM PlatinumCustomerEJB AS c</XMLCDATA>
  </ejb-ql>
</query>

The GoldCustomer bean has a similar implementation.

Now the tricky part: how to implement the locate methods in the base class:

Example 12. Implementing the locate method for a single customer

  public BaseCustomer ejbHomeLocate(String customerID) throws
FinderException {
    //At this point, all base EJB classes must know their EJB subclasses...
    Context jndiCtx;
    BaseCustomerHome bHome;
    GoldCustomerHome gHome;
    PlatinumCustomerHome pHome;
    try {
      jndiCtx = new InitialContext();
      bHome = (BaseCustomerHome)ctx.getEJBLocalHome();
      gHome = (GoldCustomerHome)jndiCtx.lookup("GoldCustomerEJB");
      pHome = (PlatinumCustomerHome)jndiCtx.lookup("PlatinumCustomerEJB");
    } catch (NamingException e) {
      throw new FinderException("Cannot obtain JNDI initial context.");
    }
    BaseCustomer b=null;
    try {
      b = bHome.findByPrimaryKey(customerID);
      return b;
    } catch (FinderException e) {
    }
    try {
      b = gHome.findByPrimaryKey(customerID);
      return b;
    } catch (FinderException e) {
    }
    try {
      b = pHome.findByPrimaryKey(customerID);
      return b;
    } catch (FinderException e) {
    }
    throw new FinderException("Cannot find any bean with that key.");
  }

Example 13. Implementing the locate method for all customers

  public Collection ejbHomeLocateAll() throws FinderException
{
    Context jndiCtx;
    BaseCustomerHome bHome;
    GoldCustomerHome gHome;
    PlatinumCustomerHome pHome;
    try {
      jndiCtx = new InitialContext();
      bHome = (BaseCustomerHome)ctx.getEJBLocalHome();
      gHome = (GoldCustomerHome)jndiCtx.lookup("GoldCustomerEJB");
      pHome = (PlatinumCustomerHome)jndiCtx.lookup("PlatinumCustomerEJB");
    } catch (NamingException e) {
      throw new FinderException("Cannot obtain JNDI initial context.");
    }
    //At this point, all base EJB classes must know their EJB subclasses...
    ArrayList l = new ArrayList();
    l.addAll(bHome.findAllBaseCustomers());
    l.addAll(gHome.findAllGoldCustomers());
    l.addAll(pHome.findAllPlatinumCustomers());
    return l;
  }

The logic here is as follows:

  • For a locate method returning a single object, find the object in the current bean using its finder. If no object was found, try in each subclass until it is found.
  • For a locate method returning multiple objects, create a list to accumulate the results. Find the objects in the current bean and add them to the list. Find the objects in each subclass and add them to the list.

Note that, depending on your table mapping, the code for finders can be quite different. My next article will be dedicated to the various techniques for table mapping.

I know this code is ugly, but it is the only way I have found so far to emulate inheritance in finder methods. If you find a better solution, let me know.

But what about ejbSelect methods? The same principle exists with ejbSelect methods; they must be wrapped with other methods in the bean implementation. I decided to give them the prefix ejbLocate and call them ejbLocate methods. Note that they are not exposed through the home interfaces, just like regular ejbSelect methods.

Pages: 1, 2, 3, 4, 5

Next Pagearrow