Using EJB QL with Find and Select Methods


Using EJB QL with Find and Select Methods

EJB QL is used for defining queries for accessing entity beans with container-managed persistence in a portable way. The queries created using EJB QL are specified in the ejb-jar.xml deployment descriptor using the entity:query element. The query element is specified for all ejbFind<METHOD>(s) (with the exception of ejbFindByPrimaryKey) and ejbSelect<METHOD>(s). The suffix <METHOD> is a stand-in for the name of the method. Only the finder methods are exposed to the entity bean clients through the beans home interface. ejbSelect<METHOD>(s) are used internally by the bean class, and declared as abstract method on an entity bean class. For container-managed persistence, the implementation for the finder and select methods are generated by the container provider's tools at deployment time.

One important distinction between finder and select methods is that the finder methods can only return a type that represents the entity bean's local or remote interface (depending on local or remote usage), or a type representing a collection of objects that implement the entity bean's local or remote interface, whereas select methods can return objects of any cmp-field or cmr-field type. Another important distinction is that the select methods execute in the transaction context determined by the transaction attribute of the invoking business method. The container is responsible for ensuring that changes to the states of all entity beans in the same transaction context as the select method are visible in the results of the select method.

Single-object finder methods and select methods should always return a single entity object, otherwise the container will throw the FinderException. Multi-object finder methods specify a result type as a java.util.Collection type. For remote interface types, the client must use the PortableRemoteObject.narrow method to convert the objects contained in a collection. Multi-object select methods specify a result type as a java.util.Collection type or java.util.Set type. For Collection type, the objects returned in the collection may contain duplicates if DISTINCT is not specified in the SELECT clause of the query. For Set type, SELECT DISTINCT is default when DISTINCT is not specified in the SELECT clause.

Specifying the Deployment Descriptors

We begin discussing deployment descriptors with an emphasis on collection-valued cmr-fields because of the one-to-many relationship between PortalAlliance and Campaign EJBs. Figure 6-1 shows the PortalAlliance-Campaign relationship, which is a one-to-many unidirectional relationship. We represent this relationship using the following deployment descriptor declarations. The ejb-relationship-role element is defined in the context of the role name associated with the relationship-role-source element. For the deployment descriptor shown in the following code, the source is identified by the logical name assigned to the PortalAlliance entity bean, which is PortalAllianceEntityEJB, and the corresponding role name identified by the ejb-relationship-role-name is alliance. The relationship-role-source CampaignEntityEJB does not have a cmr-field indicating that association between PortalAlliance and Campaign does not have directivity from Campaign to PortalAlliance.

<ejb-relation>
    <ejb-relation-name>PortalAlliance-Campaign</ejb-relation-name>
    <ejb-relationship-role>
        <ejb-relationship-role-name>alliance</ejb-relationship-role-name>
        <multiplicity>One</multiplicity>
        <relationship-role-source>
            <ejb-name>PortalAllianceEntityEJB</ejb-name>
        </relationship-role-source>
        <cmr-field>
            <cmr-field-name>campaigns</cmr-field-name>
            <cmr-field-type>java.util.Collection</cmr-field-type>
        </cmr-field>
    </ejb-relationship-role>
    <ejb-relationship-role>
        <ejb-relationship-role-name>campaigns</ejb-relationship-role-name>
        <multiplicity>Many</multiplicity>
        <relationship-role-source>
            <ejb-name>CampaignEntityEJB</ejb-name>
        </relationship-role-source>
    </ejb-relationship-role>
</ejb-relation>
 

In this snippet, observe that the cmr-field-name has the value campaigns. This value corresponds to the getCampaigns and setCampaigns accessor methods, and follows the JavaBean convention for naming accessor methods. The cmr-field-type specifies that the get and set methods will use a collection-valued object in their method signatures.

The following snippet from ejb-jar.xml depicts the ejbSelectRegionalCampaign method and query configurations..

<entity>
    ... other declarations appear here ...
    <abstract-schema-name>PortalAlliance</abstract-schema-name>
    <cmp-field><field-name>portalID</field-name></cmp-field>
    <cmp-field><field-name>portalName</field-name></cmp-field>
    ... rest of cmp-fields ...
    <primkey-field>portalID</primkey-field>
    <query>
        <query-method>
            <method-name>ejbSelectRegionalCampaigns</method-name>
            <method-params>
                <method-param>java.lang.String</method-param>
                <method-param>java.lang.String</method-param>
            </method-params>
        </query-method>
        <ejb-ql>
           SELECT OBJECT(c)FROM PortalAlliance AS p,
               IN (p.campaigns) c
               WHERE (p.portalID = ?1 AND c.regionCode = ?2)
        </ejb-ql>
    </query>
</entity>
 

The getCampaigns method on the PortalAlliance returns a collection as a result of one-to-many relationships existing between the PortalAlliance entity bean and Campaign entity beans. This is shown in Figure 6-2. The EJB 2.0 specification mandates that the iterator obtained over a collection in a container-managed relationship must be used within the transaction context in which the iterator was obtained. Therefore the getCampaigns method of the PortalAlliance entity bean is associated with the transaction attribute value of Mandatory. This constraint automatically enforces a requirement on the client to call the getCampaigns method of the PortalAlliance entity bean with a transaction attribute Required; this is because the client is going to iterate over the collection. Transactions are discussed in Chapter 7 in the section "Transaction Semantics for Enterprise Beans." The following snippet shows the transaction attribute declaration for the getCampaigns method in the ejb-jar.xml file.

<container-transaction>
    <method>
        <ejb-name>PortalAllianceEntityEJB</ejb-name>
        <method-name>getCampaigns</method-name>
    </method>
    <trans-attribute>Mandatory</trans-attribute>
</container-transaction>
 

The deployment descriptor files are included in their entirety in the accompanying source distribution. This concludes the discussion for implementing and configuring the PortalAlliance entity bean.

Defining the Campaign Interface

Figure 6-1 depicts the Campaign-NPO relationship between Campaign entity bean and the NPO domain-object interfaces entity bean. The relationship is unidirectional implying that only the Campaign bean has cmr-field accessor methods defined. The following code segment represents the methods required on the campaign interface:

public interface Campaign {
    public Integer getCampaignID();
    /* setCampaignID is specified only in the bean class */
    ... Other cmp-fields accessors ...
    /* Accessors for cmr-field npo */
    public NPOLocal getNpo();
    public void setNpo(NPOLocal npo);
}
 

Observe that the getCampaignID method returns an integer. This is because the CAMPAIGN_ID of the CAMPAIGN table, as shown in Figure 6-2, uses a database-generated key. For developing the sample application, we have used the Oracle database server, which provides a sequence generation facility. The vendor-specific implementation wraps the sequence number in an Integer object; this is discussed in the following section.

Specifying the Deployment Descriptors

The following declarations in the vendor-specific weblogic-ejb-jar.xml deployment descriptor are for configuring a primary key that employs automatic key generation:

<weblogic-rdbms-bean>
    <ejb-name>CampaignEntityEJB</ejb-name>
    <data-source-name>jdbc/gcOracleTxPool</data-source-name>
    <table-map>
        <table-name>CAMPAIGN</table-name>
        <field-map>
            <cmp-field>campaignID</cmp-field>
            <dbms-column>CAMPAIGN_ID</dbms-column>
        </field-map>
        ... Other field-map declarations ...
    </table-map>
    <automatic-key-generation>
        <generator-type>ORACLE</generator-type>
        <generator-name>CAMPAIGN_ID_SEQUENCE</generator-name>
        <key-cache-size>10</key-cache-size>
    </automatic-key-generation>
</weblogic-rdbms-bean>
 

The sequence CAMPAIGN_ID_SEQUENCE specified for the generator-name element is created using the following DDL:

CREATE SEQUENCE CAMPAIGN_ID_SEQUENCE
START WITH 10 INCREMENT BY 10 CACHE 20;
 

Providing key-cache-size optimizes access to the database because the container caches the sequence number and increments the sequence without requesting the next value from database for each entity creation. When using WebLogic with Oracle's sequence generator, the WebLogic document recommends using the same value for the key-cache-size element and INCREMENT; if these values differ, you will most likely experience duplicate key problems.