Declaring finders

The finders to access your beans must all be declared in the home interface, according to the EJB specification.

Standard finders, automatically generated

JAWS automatically generates the following finders for you:

  • findAll() will return a Collection of all the beans available

  • findByPrimaryKey(YourPK pk) will return a single bean with the corresponding primary key (the primary key class is defined in ejb-jar.xml)

  • for each of the cmp-fields of your bean, findByXX(YY fieldValue), where XX is the name of the cmp-field (NOT case-sensitive) and YY its class, will return a Collection of all the beans with the right value in this field.

Note that these finders are only generated if you declare them in your home interface. “Automatically generated” only means that JAWS will guess the meaning of your finder from its name, you don't have to add anything.

Custom finders

JAWS then allows you to define customized finders. These finders must also be declared in the home interface of your bean. You must then provide additional information in jaws.xml about the query to be used for this finder. This is done in a <finder> section in the section for your entity bean. You have to specify the name of the finder (the same as in the home interface), the WHERE part of the query, and the ORDER part.

Example: you want to select classes with a mininum number of students. Your Class bean has the following structure:

<ejb-jar>
  <display-name>Class</display-name>
  <enterprise-beans>

    <entity>
      <description>Models a Class</description>
      <ejb-name>ClassBean</ejb-name>
      <home>org.jboss.docs.cmp.jaws.interfaces.ClassHome</home>
      <remote>org.jboss.docs.cmp.jaws.interfaces.Class</remote>
      <ejb-class>org.jboss.docs.cmp.jaws.bean.ClassBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>False</reentrant>
      <cmp-field><field-name>classId</field-name></cmp-field>
      <cmp-field><field-name>teacherName</field-name></cmp-field>
      <cmp-field><field-name>studentCount</field-name></cmp-field>
      <primkey-field>classId</primkey-field>
    </entity>

  </enterprise-beans>
  ...
</ejb-jar>
 
 

You want to define a method in ClassHome:

public Collection findBigClasses(Integer minStudentCount, String teacher)
throws FinderException, RemoteException;
  

Your jaws.xml file will contain the following:

<jaws>
	<enterprise-beans>
		<entity>
			<ejb-name>ClassBean</ejb-name>
			...
       		<finder>
         		<name>findBigClasses</name>
         		<query>studentCount > {0} AND teacherName = {1}</query>
         		<order>studentCount DESC</order>
       		</finder>
		</entity>
	</enterprise-beans>
</jaws>

Then a call to findBigClasses(100, "Jones") will generate

SELECT classId FROM ClassBean
  WHERE studentCount > 100 AND teacherName = 'Jones'
  ORDER BY studentCount DESC;

Custom finders with join queries

Author: Michel de Groot <[email protected]> or <[email protected]>

By default, JAWS will use only the table for your bean to perform select queries. You can also use inner join queries on multiple tables with JAWS CMP for finder methods with EJB 1.1. This allows you to deliver powerful queries with minimal effort.

Standards compliance note: this feature is not standard or required in EJB 1.1. Therefore, EJBs that use this feature, might not be compile-free portable to other EJB containers.

You must use a database which has support for SQL92 (most databases do) or T-SQL join queries.

Example:

  • List your finder method in the EJB's home interface as usual.

    public interface PopulationHome extends EJBHome {
        /**
         * Finds all populations of a specific type as defined in 
         * CreatureTypes of a Province.
         * @param provinceId the primary key of the province
         * @param type the type of creatures to find
         */
         public Collection findByProvinceAndType(String provinceId, int type) 
                           throws RemoteException, FinderException;
    }
    
  • Declare your finder in jaws.xml, using the <query> tag. There are two options. The first presented here conforms to SQL92 and is the more generic and portable one. The second presented here is defined by T-SQL and is useful for MS Access 97 for example.

    Generic SQL92 example:

    <finder>
        <name>findByProvinceAndType</name>
        <query>,CreatureEJBTable 
        WHERE CreatureEJBTable.id = PopulationEJBTable.creatureId AND
        provinceId={0} AND type={1}</query>
        <order></order>
    </finder>
    

    T-SQL example:

    <finder>
        <name>findByProvinceAndType</name>
        <query>inner join CreatureEJBTable ON CreatureEJBTable.id =
        PopulationEJBTable.creatureId WHERE provinceId={0} AND
        type={1}</query>
        <order></order>
    </finder>
    

You must be aware of the following issues:

  • The query must start with ',<table name to join with>' (for SQL92 query) or with 'inner join <table name to join with>' (for T-SQL). JAWS will append the proper select statement.

  • If SQL92 is used, the WHERE statement must contain the identity statement which joins the tables. If T-SQL is used, the ON statement must contain the identity statement which joins the tables.

  • The query must contain the WHERE statement followed by the conditions of the query.

  • The query always delivers instances of the Entity Bean on which the finder is defined.

  • Field names must be fully qualified with table name if ambiguity can arise.

We strongly suggest you use SQL92 as default query language. T-SQL is provided for ease-of-use if you have existing T-SQL queries.

Advanced options for declared finders [since JBoss 2.4]

Author: Dan Christopherson <[email protected]> or <[email protected]>

As of JBoss version 2.4, it is possible to request that JAWS preload data for all entities selected by a declared finder. This avoids a performance problem where the database will be queried separately to load data for each bean returned by the finder. To activate this optimization, simply add <read-ahead>true</read-ahead> to your finder declaration. Note that as of the same version, you can override this option for the autogenerated findAll method as well.

Note that the data preloaded by this feature is tied to the transaction it is loaded in. In other words, data preloaded by one transaction is not seen by another transaction. One practical implication of this is that calling a finder from client code will not take advantage of this feature. It may be feasable in some circumstances to declare the finder and an entity's getter methods as 'Supports' in which case the data preloaded by the finder will not be tied to any transaction and may be available when the getter methods are called.

This is an example jaws.xml showing use of this option

 <jaws>
   <enterprise-beans>
     <entity>
       <ejb-name>ClassBean</ejb-name>
       <finder>
         <name>findBigClasses</name>
         <query>studentCount > {0} AND teacherName = {1}</query>
         <order>studentCount DESC</order>
         <read-ahead>true</read-ahead>
       </finder>
       <finder>
         <name>findAll</name>
         <query />
         <order />
         <read-ahead>true</read-ahead>
       </finder>
     </entity>
   </enterprise-beans>
   ...
 </jaws>

Custom finders coded in your beans

Author: Michel de Groot <[email protected]> or <[email protected]>

Sometimes, the finders automatically generated by JAWS or the finders defined in jaws.xml using SQL92 are not sufficient for your application. You want to implement a custom finder by coding the query in your bean.

You can also have Entity Beans with finders which have been developed against a less advanced persistence manager. This forced you to develop custom finders at that time. You want to be able to leverage this work to JBoss.

To do this, all you have to do is

  • Declare the finder in the EJB's home interface as usual. This name must begin with “findBy”. Example:

    public interface PopulationHome extends EJBHome {
       /**
        * Finds all populations of a specific type as defined in 
        * CreatureTypes of a Province.
        * @param provinceId the primary key of the province
        * @param type the type of creatures to find
        */
     
        public Collection findByProvinceAndType(String provinceId, int type) 
                          throws RemoteException, FinderException;
    }
    
  • Implement the finder method in the EJB's implementation class. The name of the method must begin with “ejbFindBy” and match the name declared in the home interface. Example:

    public class PopulationEJB implements EJBObject {
        public Collection ejbFindByProvinceAndType(String provinceId, int type) {
            // .. fill Collection with primary keys of result objects
            return aResultCollection;
        }
    }
    

This feature is standard and required in EJB 1.1.

Custom finders defined this way will ALWAYS be used if present. They override automatic or defined finders. This is according to the EJB 1.1 specification.