Oracle9iAS Containers for J2EE - Release 2

Date: 07/29/02

How-To: Use EJB QL for Finder Methods

After completing this How To you should be able to:

Software Requirements

Notation

Introduction

Entity EJBs in the EJB 1.1 specification were fairly simple representations of business objects in a storage mechanism, usually a database. There was no defined standard for defining relationships, portable query definitions for finder methods or local beans (as opposed to assuming every bean is a potentially remote object). Container providers therefore created these features in their containers. Each container implemented these features in different ways, so making the process of moving applications from one vendor to another more difficult.

Recognizing this, the EJB 2.0 specification more clearly defines the roles of entity beans. This includes standard query definition (EJB QL), local interfaces, and abstract persistence schemas. This How To will focus on the use of EJB QL

EJB QL

The Enterprise JavaBeans query language, EJB QL, was introduced in the EJB 2.0 specification. It is used to define queries for entity beans which operate with container-managed persistence. EJB QL enables you to specify the semantics of query methods in a portable way.

To use EJB QL, you provide a string which contains a SELECT clause and a FROM clause, and optionally a WHERE clause. Sounds familiar to all you SQL savvy developers? Well you'd be correct. EJB QL is in fact very similar to a subset of SQL, with a marginally different syntax. Some might say, just enough to be annoying at first.

For an Enterprise application, EJB QL can be used to express queries for two different styles of operations:

EJB QL uses the abstract persistence schemas of entity beans, including their relationships, for its data model. It defines operators and expressions based on this data model. EJB QL depends on navigation and selection based on the CMP-fields and CMR-fields of the related entity beans. You can navigate from an entity bean to other entity beans by using the names of CMR-fields in EJB QL queries.

This How-To focusses on demonstrating the simple use of EJB QL to support the expression of the queries for finder methods. It does not demonstrate the use of the ejbSelect methods nor the traversal of entity relationships to form joins between objects. These topics will be covered in additional How-To's.

As an example, consider the following Home interface. It defines a number of finder methods for the Emp bean which will return an instance of, or a collection of instances of Emp objects.

A Home Interface:

public interface EmpHome extends EJBLocalHome
{
  ...
  Emp findByPrimaryKey(EmpPK primaryKey) throws FinderException;
  public Collection findBySalaryRange(double low, double high) throws FinderException;
  public Collection findByDeptno(double deptno) throws FinderException; 
}

The findByPrimaryKey method is a special method finder method when using CMP. It's semantics and operation are managed entirely by the EJB container and there is no need for the specification of the query that it will use. In fact, the EJB specification explicitly prevents you from trying to subvert it's behavior by specifying a query.

The findBySalaryRange and findByDetpno are ideal candidates for having their queries be explicitly defined. Each of the finder methods is parameterized so they naturally fit to performing a constrained query utilizing the where clause. What we want the container to do for us is to look into it's persistent store, and pass back a collection of bean instances that match the query.

EJB QL for Finder Methods

Let's take a look at the query for the findBySalaryRange and then we'll discuss it further. The query definition for the bean is defined within the standard EJB ejb-jar.xml file and is encapsulated within the definition of the bean instance.

<entity>
  <ejb-name>Emp</emp>
  <local-home>howto.ejbql.ejb.EmpHome</local-home>
<local>howto.ejbql.ejb.Emp</local>
<ejb-class>howto.ejbql.ejb.EmpBean</ejb-class>
<persistence-type>Container</persistence-type>
<abstract-schema-name>Emp</abstract-schema-name>
<query>
<description></description>
<query-method>
<method-name>findBySalaryRange</method-name>
<method-params>
<method-param>double</method-param>
<method-param>double</method-param>
</method-params>
</query-method>
<ejb-ql>select distinct object(e) from Emp e where e.sal between ?1 and ?2</ejb-ql>
</query> ...
</entity>

The XML tags themselves are pretty informative, but let's dissect this query element piece by piece.

The query element itself is associated with a specific entity bean definition. An entity bean can have zero of more query definitions.

The method name matches matches one of the methods defined on the home interface. If there is more than one method of the same name, the method parameters will be used to identify which specific method is being described.

The method params enable you to provide information about the parameters that will accompany the method call. The number of parameters and their types are required information and are used to associate logical values to be used in the query and to help disambiguate overloaded method names. It's important that you get the order and type of the parameters correct since these will be mapped onto the parameters passed into the query.

The EJB-QL element defines the actual query that will be used. The most striking difference from standard SQL is the requirements to specify the OBJECT keyword when returning a single entity type. The entities to be used within the query are specified through the use of the <abstract-schema-name> that is supplied as part of the definition of the entity bean. To support parameterized queries, you use logical placeholders of the form ?n, where n represents the location of the value in the method params list, starting from an index of 1. EJB QL supports standard SQL operations of between and like, but it does not yet support aggregate and sorting functions (these are due to be supported in EJB 2.1).

So what we have here is a fully expressed query for the findBySalaryRange using EJB QL. It will take in 2 parameters, both of type double, and will then query the persistent store for all Emp entities which have a salary which is between the parameters. If there are two entities with the same results, then it will return only one of the matching entities in the collection.

An Example

This example file contains one EJB, a servlet and a two HTML pages. The EJB is a CMP based entity bean howto.ejbql.ejb.Emp and it is a local EJB object. The home interface for the bean defines a number of finder methods to enable inquisitive clients to look for specific types of employees. The servlet howto.ejbql.web.TestServlet acts as the client to the EJB and is responsible for interacting directly with the bean. Since the servlet is deployed in the same ear files as the EJB, it can access the local interface of the bean. The servlet calls the various finder methods on the bean depending on what data is sent to it in the request. An index.html page is provided that lets you call the servlet to exercise all of the finder methods, or an alternative page with an HTML form is provided to allow you to provide a salary range which then becomes the parameters for the findBySalary finder method.

Configuring your environment

  1. It is assumed that you have the demo user SCOTT created in your Oracle database and the demo tables are created in the SCOTT schema. If you do not have the demo tables install or want to install the demo tables in another schema then execute the demobld.sql located in %ORACLE_HOME%/sqlplus/demo directory of your Oracle database.

  2. Create a data-source in your %J2EE_HOME%/config/data-sources.xml file to point to the SCOTT/TIGER schema or any other schema where the demo tables are installed. You can find the data-sources.xml file used to build the example in the etc directory.

  3. This application uses the default data-source configured for OC4J. If you want to use a data-source other than your default data-source then modify the orion-ejb-jar.xml file to point the data-source you have created by adding the item in red in the tag shown below.
  4. <entity-deployment name="Emp" data-source="jdbc/OracleDS" table="EMP">

  5. Make sure that you have turned off autocreate-tables as the EMP and DEPT tables already exist. This is done using the orion-application.xml. Make sure the the <orion-application... tag have an attribute autocreate-tables="false".

  6. Examine ejb-jar.xml and verify that the Emp declaration has been specified as a local bean instance using the <local-home> and <local> tags respectively.

  7. Also in ejb-jar.xml, review the query definitions for each of the finder methods.

  8. Examine web.xml, review that it is using the local Emp bean by specifying that the reference is an <ejb-local-ref> tag pointing to the Emp bean.

Compiling and deploying the application

We have provided a build script for building the application using Ant. Alternatively you can use the pre-bulit enterprise archive file ejbql.ear located in the /lib directory.

Using Ant and admin.jar

  1. Ensure Ant 1.4.x or above is installed on your machine and configured correctly.

  2. Set JAVA_HOME and OC4J_HOME environment variables.  Note: On some operating systems Ant does not currently support the use of environment variables,  if this is the case for your operating system,  please modify the common.xml file located in the root example directory.

  3. From the example application root directory [the directory where the build.xml file is located] execute the following command:
  4. ant

  5. Your should now have a newly created ejbql.ear file in your <example_home >/lib directory.

  6. Deploy the J2EE application ejbql.ear to your OC4J instance by executing the following command from the <example_home > directory. Befor running the command check that the values deploy.username and deploy.password in the <example_home >/build.xml script are correct for your OC4J installation.
  7. ant deploy-usingadmin.jar

You should be now ready to run this demo application.

Running the application

Summary

In this document you should have:

References