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
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.
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.
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.
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.
<entity-deployment name="Emp" data-source="jdbc/OracleDS" table="EMP">
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.
ant
ant deploy-usingadmin.jar
You should be now ready to run this demo application.
In this document you should have: