2 Using ADF Model Data Binding in a Java EE Web Application

This chapter describes how to create ADF model data controls for EJB session beans and how to use the Data Controls panel to create databound UI components on JSF web pages.

This chapter includes the following sections:

For more comprehensive information about using ADF Model data binding, refer to the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

2.1 Introduction to ADF Model Data Binding

ADF Model implements two concepts that enable the decoupling of the user interface technology from the business service implementation: data controls and declarative bindings. Data controls abstract the implementation technology of a business service by using standard metadata interfaces to describe the bean's operations and data collections, including information about the properties, methods, and types involved. Using JDeveloper, you can view that information as icons which you can drag and drop onto a page. Using those icons, you can create databound HTML elements (for JSP pages), databound UI components (for JSF pages), and databound Swing UI components (for ADF Swing panels) by dragging and dropping them from the panel onto the visual editor for a page. JDeveloper automatically creates the metadata that describes the bindings from the page to the services. At runtime, the ADF Model layer reads the metadata information from the appropriate XML files for both the data controls and the bindings, and then implements the two-way connection between your user interface and your business services.

Declarative bindings abstract the details of accessing data from data collections in a data control and of invoking its operations. There are three basic kinds of declarative binding objects:

  • Executable bindings: Include iterator bindings, which simplify the building of user interfaces that allow scrolling and paging through collections of data and drilling-down from summary to detail information. Executable bindings also include bindings that allow searching and nesting a series of pages within another page.

  • Value bindings: Used by UI components that display data. Value bindings range from the most basic variety that work with a simple text field to more sophisticated list and tree bindings that support the additional needs of list, table, and tree UI controls.

  • Action bindings: Used by UI command components like hyperlinks or buttons to invoke built-in or custom operations on data collections or a data control without writing code.

Figure 2-1 shows how bindings connect UI components to data control collections and methods.

Figure 2-1 Bindings Connect UI Components to Data Controls

Bindings connect components to data

The group of bindings supporting the UI components on a page are described in a page-specific XML file called the page definition file. The ADF Model layer uses this file at runtime to instantiate the page's bindings. These bindings are held in a request-scoped map called the binding container. In a JSF application, the binding container is accessible during each page request using the EL expression #{bindings}.

Tip:

For more information about ADF EL expressions, see the "Creating ADF Data Binding EL Expressions" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

To use the ADF Model layer to data-bind, you need to create a data control for your services. The data controls will then appear as icons in the Data Controls panel, which you can use to declaratively create pages whose components will be automatically bound to those services.

2.2 Exposing Services with ADF Data Controls

Once you have your application's services in place, you can use JDeveloper to create data controls that provide the information needed to declaratively bind UI components to those services. In a Java EE application, you normally create entity beans that represent tables in a database and then create a session facade over all the EJBs. This facade provides a unified interface to the underlying entities. In an ADF application, you can create a data control for the session bean, and that data control will contain representation of all the EJBs under the session bean. The data control consists of a number of XML metadata files that define the capabilities of the service that the bindings can work with at runtime.

For example, the Suppliers module uses the FOD database schema, which contains a number of relational database tables. The module has a number of entity beans that represent the tables in the schema used by the Suppliers module. There is an Addresses bean, a Product bean, a Persons bean, and so on. The module also contains two session beans: the SupplierFacade bean, which is used to access the beans created from tables, and the GenericServiceFacade bean, which contains generic service methods used by all beans in the application. A data control exists for each of those session beans, which allows developers to declaratively create UI pages using the data and logic contained in those beans.

2.2.1 How to Create ADF Data Controls

You create data controls from within the Application Navigator of JDeveloper.

Before you begin:

  1. Create JPA/EJB 3.0 entities. For more information, see the "Building a Persistence Tier" section of the JDeveloper online help.

  2. Create one or more session beans for the entities. For more information see the "Implementing Business Processes in Session Facade Design Pattern" section of the JDeveloper online help.

    When creating your entities and session bean(s), keep the following in mind:

    • For a class to be a valid data control source, it has to meet the JavaBeans specification. It needs to have a public default constructor.

    • Because the metadata files that represent the beans for the data control are named based on the class names for the beans, you must ensure that if beans have the same name, they are in different packages. If two beans with the same name are in the same package, one metadata file will overwrite the other.

    • If you rename the bean used to create a data control, you must re-create the data control.

To create a data control:

  1. In the Application Navigator, right-click the session bean for which you want to create a data control.

  2. From the context menu, select Create Data Control.

  3. In the Choose EJB Interface dialog, choose Local.

2.2.2 What Happens in Your Project When You Create a Data Control

When you create a data control based on an EJB session bean, the data control contains a representation of all the methods exposed on the bean, as well as underlying entity beans, and the methods and properties exposed on those.

For the data control to work directly with the service and the bindings, JDeveloper creates the following metadata XML files:

  • Data control definition file (DataControls.dcx). This file defines the factory class and ID for each data control. It also contains settings that determine how the data control behaves. For example, you can use the .dcx file to set global properties, such as whether the service supports transactions. To change the settings, you select the data control in the overview editor and change the value of the property in the Property Inspector. Figure 2-2 shows the DataControls.dcx file in the overview editor and Property Inspector of JDeveloper.

    Figure 2-2 DataControls.dcx File in the Overview Editor and Property Inspector

    dcx file values set in Property Inspector

    Example 2-1 shows the code from the corresponding XML file (available by clicking the source tab).

    Example 2-1 DataControls.dcx File

    <?xml version="1.0" encoding="UTF-8" ?>
    <DataControlConfigs xmlns="http://xmlns.oracle.com/adfm/configuration"
                        version="11.1.1.54.7" id="DataControls"
                        Package="oracle.fodemo.supplier.model">
      <AdapterDataControl id="SupplierFacadeLocal"
                       FactoryClass="oracle.adf.model.adapter.DataControlFactoryImpl"
                       ImplDef="oracle.adfinternal.model.adapter.ejb.EjbDefinition"
                       SupportsTransactions="false" SupportsSortCollection="true"
                       SupportsResetState="false" SupportsRangesize="false"
                       SupportsFindMode="false" SupportsUpdates="true"
                       Definition="oracle.fodemo.supplier.service.SupplierFacadeLocal"
                       BeanClass="oracle.fodemo.supplier.service.SupplierFacadeLocal"
                       xmlns="http://xmlns.oracle.com/adfm/datacontrol">
        <CreatableTypes>
          <TypeInfo FullName="oracle.fodemo.supplier.model.CountryCode"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.ProductCategory"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.Addresses"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.AddressUsage"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.Person"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.Supplier"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.Product"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.ProductImage"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.ProductTranslation"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.WarehouseStockLevel"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.OrderItem"/>
          <TypeInfo FullName="oracle.fodemo.supplier.model.LookupCode"/>
        </CreatableTypes>
        <Source>
          <ejb-definition ejb-version="3.0" ejb-name="SupplierFacade"
                          ejb-type="Session"
           ejb-business-interface="oracle.fodemo.supplier.service.SupplierFacadeLocal"
                          ejb-interface-type="local"
                       initial-context-factory="weblogic.jndi.WLInitialContextFactory"
          DataControlHandler="oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler"
                          xmlns="http://xmlns.oracle.com/adfm/adapter/ejb"/>
        </Source>
      </AdapterDataControl>
      <AdapterDataControl id="GenericServiceFacadeLocal"
    . . .
      </AdapterDataControl>
    </DataControlConfigs>
    
  • Structure definition files for every entity object and structured object that this service exposes. These files define how attributes, accessors, and operations will display and behave. For example, you can set how the label for an attribute will display in a client. A structure definition file contains the following information:

    • Attributes: Describes the attributes available on the service. You can set UI hints that define how these attributes will display in the UI. You can also set other properties, such as whether the attribute value is required, whether it must be unique, and whether it is visible. For information about setting UI hints, see the "Defining Control Hints for View Objects" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

      Note:

      View objects are ADF Business Components used to encapsulate SQL queries and to simplify working with the results. When reading this section, simply substitute "bean" for "view object."

      You can also set validation for an attribute and create custom properties. For more information, see the "Using the Built-in Declarative Validation Rules" and the "How to Implement Generic Functionality Driven by Custom Properties" sections of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

    • Accessors: Describes the different accessor methods.

    • Operations: Describes custom methods on the service, along with any parameters.

    Figure 2-3 shows the structure definition file for the Addresses bean in the Suppliers module.

    Figure 2-3 Structure File in the Overview Editor

    Structure file

    Example 2-2 shows the code from the corresponding XML file (available by clicking the source tab).

    Example 2-2 Structure File

    <?xml version="1.0" encoding="UTF-8" ?>
    <JavaBean xmlns="http://xmlns.oracle.com/adfm/beanmodel" version="11.1.1.54.7"
              id="Addresses" Package="oracle.fodemo.supplier.model"
              BeanClass="oracle.fodemo.supplier.model.Addresses" isJavaBased="true">
      <Attribute Name="address1" Type="java.lang.String" Precision="40">
        <Properties>
          <SchemaBasedProperties>
            <LABEL ResId="oracle.fodemo.supplier.model.Addresses.address1_LABEL"/>
            <TOOLTIP ResId="oracle.fodemo.supplier.model.Addresses.address1_TOOLTIP"/>
            <DISPLAYWIDTH Value="40"/>
          </SchemaBasedProperties>
        </Properties>
      </Attribute>
      <Attribute Name="address2" Type="java.lang.String" Precision="40">
        <Properties>
          <SchemaBasedProperties>
            <LABEL ResId="oracle.fodemo.supplier.model.Addresses.address2_LABEL"/>
            <TOOLTIP ResId="oracle.fodemo.supplier.model.Addresses.address2_TOOLTIP"/>
            <DISPLAYWIDTH Value="40"/>
          </SchemaBasedProperties>
        </Properties>
      </Attribute>
    . . .
      <AccessorAttribute id="addressUsageList" IsCollection="true"
                         RemoveMethod="removeAddressUsage"
                         AddMethod="addAddressUsage"
                         BeanClass="oracle.fodemo.supplier.model.AddressUsage"
                         CollectionBeanClass="UpdateableCollection">
        <Properties>
          <Property Name="RemoveMethod" Value="removeAddressUsage"/>
          <Property Name="AddMethod" Value="addAddressUsage"/>
        </Properties>
      </AccessorAttribute>
    . . .
      <MethodAccessor IsCollection="false"
                      Type="oracle.fodemo.supplier.model.AddressUsage"
                      BeanClass="oracle.fodemo.supplier.model.AddressUsage"
                      id="addAddressUsage" ReturnNodeName="AddressUsage">
        <ParameterInfo id="addressUsage"
                       Type="oracle.fodemo.supplier.model.AddressUsage"
                       isStructured="true"/>
      </MethodAccessor>
      <MethodAccessor IsCollection="false"
                      Type="oracle.fodemo.supplier.model.AddressUsage"
                      BeanClass="oracle.fodemo.supplier.model.AddressUsage"
                      id="removeAddressUsage" ReturnNodeName="AddressUsage">
        <ParameterInfo id="addressUsage"
                       Type="oracle.fodemo.supplier.model.AddressUsage"
                       isStructured="true"/>
      </MethodAccessor>
    . . .
      <ConstructorMethod IsCollection="true"
                         Type="oracle.fodemo.supplier.model.Addresses"
                         BeanClass="oracle.fodemo.supplier.model.Addresses"
                         id="Addresses">
        <ParameterInfo id="address1" Type="java.lang.String" isStructured="false"/>
        <ParameterInfo id="address2" Type="java.lang.String" isStructured="false"/>
        <ParameterInfo id="addressId" Type="java.lang.Long" isStructured="false"/>
        <ParameterInfo id="city" Type="java.lang.String" isStructured="false"/>
        <ParameterInfo id="countryId" Type="java.lang.String" isStructured="false"/>
        <ParameterInfo id="createdBy" Type="java.lang.String" isStructured="false"/>
        <ParameterInfo id="creationDate" Type="java.sql.Timestamp"
                       isStructured="false"/>
        <ParameterInfo id="lastUpdateDate" Type="java.sql.Timestamp"
                       isStructured="false"/>
        <ParameterInfo id="lastUpdatedBy" Type="java.lang.String"
                       isStructured="false"/>
        <ParameterInfo id="latitude" Type="java.lang.Long" isStructured="false"/>
        <ParameterInfo id="longitude" Type="java.lang.Long" isStructured="false"/>
        <ParameterInfo id="objectVersionId" Type="java.lang.Long"
                       isStructured="false"/>
        <ParameterInfo id="postalCode" Type="java.lang.String"
                       isStructured="false"/>
        <ParameterInfo id="stateProvince" Type="java.lang.String"
                       isStructured="false"/>
      </ConstructorMethod>
      <ConstructorMethod IsCollection="true"
                         Type="oracle.fodemo.supplier.model.Addresses"
                         BeanClass="oracle.fodemo.supplier.model.Addresses"
                         id="Addresses"/>
      <ResourceBundle>
        <PropertiesBundle xmlns="http://xmlns.oracle.com/adfm/resourcebundle"
                          PropertiesFile="oracle.fodemo.supplier.model.ModelBundle"/>
      </ResourceBundle>
    </JavaBean>
    

JDeveloper also adds the icons to the Data Controls panel that you can use to create databound UI components. The Data Controls panel lists all the data controls that have been created for the application's business services and exposes all the collections, methods, and built-in operations that are available for binding to UI components.

Tip:

If the Data Controls panel is not visible, see the "How to Open the Data Controls Panel" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework for instructions on opening the panel.

When a data control is created for a session bean, and that session bean was configured to contain an accessor method for the underlying beans, those beans appear as an accessor returned collection whose name matches the bean instance name. The Data Controls panel reflects the master-detail hierarchies in your data model by displaying detail data collections nested under their master data collection. For example, Figure 2-4 shows the Data Controls panel for the Suppliers module of the Fusion Order Demo application. Note that the Addresses, Person, and Product beans are all represented by accessor returned collections in the Data Controls panel.

The Data Controls panel also displays each service method on the session bean as a method icon whose name matches the method name. If a method accepts arguments, those arguments appear in a Parameters node as parameters nested inside the method's node. Objects that are returned by the methods appear as well, as shown in Figure 2-4.

Figure 2-4 Data Controls Panel

Sample Data Control Palette

Each returned collection or object displays any attributes and custom methods that were defined on the associated bean. Figure 2-5 shows the attributes and methods defined on the Supplier bean that is returned by the supplierFindAll accessor method.

Figure 2-5 Child Nodes to Returned Collections

Child nodes to the collections

By default, implicit view criteria are created for each attribute that is able to be queried on a bean. They appear as the All Queriable Attributes node under the Named Criteria node, as shown in Figure 2-5. This node is used to create quick search forms, as detailed in Chapter 7, "Creating Databound Search Forms."

As shown in Figure 2-5, the Operations node under a returned collection displays all its available built-in operations. If an operation accepts one or more parameters, then those parameters appear in a nested Parameters node. At runtime, when one of these data collection operations is invoked by name by the data binding layer, the data control delegates the call to an appropriate method on the bean interface to handle the built-in functionality. Most of the built-in operations affect the current row. Only the execute operation refreshes the data control itself. Following are the built-in operations:

  • Create: Creates a new row that becomes the current row, but does not insert it.

  • Delete: Deletes the current row.

  • Execute: Refreshes the data collection by executing or reexecuting the accessor method.

  • First: Sets the first row in the row set to be the current row.

  • Last: Sets the last row in the row set to be the current row.

  • Next: Sets the next row in the row set to be the current row.

  • Next Set: Navigates forward one full set of rows.

  • Previous: Sets the previous row in the row set to be the current row.

  • Previous Set: Navigates backward one full set of rows.

  • removeCurrentRowWithKey: Tries to find a row using the serialized string representation of the row key passed as a parameter. If found, the row is removed.

  • setCurrentRowWithKey: Tries to find a row using the serialized string representation of the row key passed as a parameter. If found, that row becomes the current row.

  • setCurrentRowWithKeyValue: Tries to find a row using the primary key attribute value passed as a parameter. If found, that row becomes the current row.

Note:

By default, JavaBeans assume the rowIndex as the key. If you do not explicitly define a key, the index will be used.

The Data Controls panel is a direct representation of the DataControls.dcx and structure definition files created when you created a data control. By editing the files, you can change the elements displayed in the panel.

Note:

Whenever changes are made to the underlying services, you need to manually refresh the data control in order to view the changes. To refresh the data control, click the Refresh icon in the header of the Data Controls panel.

2.3 Paginated Fetching of Data in EJB Data Controls

When you create an EJB or bean data control, you can determine how records are accessed from the database and whether to limit the number of records that are held in memory at a time.

There are the following possibilities for fetching and storing data in memory:

  • Scrollable access mode.

    If you accept the defaults when creating the data control, the data access mode is set to scrollable. This means that the data that your application needs to display is retrieved from the database as needed (in increments equal to the range size specified by the UI component's iterator) and stored in memory. Then, when the user scrolls forward through the application, additional rows are fetched as needed and stored in memory. All rows that have been fetched remain in memory.

    For example, if the running application contains a table that displays rows 1 through 20 on a web page and the table's iterator has a range size of 25 (the default), the data control will fetch the first 25 rows. If the user scrolls down to display rows 477 through 496 of the result set, the data will be fetched in sets of 25 as the user scrolls until rows 26 through 500 are fetched. At that point, a total of 500 rows will be stored in memory.

    This is the default mode for data controls using oracle.adf.model.adapter.bean.DataFilterHandler and oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler. However, for data controls using oracle.adf.model.adapter.bean.DataFilterHandler, you still need to add paging methods to your data control to implement the access mode. For more information, see Section 2.3.6, "How To Manually Implement Pagination Support in a Bean Data Control."

  • Range paging access mode

    To limit the amount of records that are fetched and stored in memory at a time, you can use the rangePaging access mode. As with scrollable mode, range paging mode allows your applications to fetch data in increments. The main difference in range paging mode is that only the most recently fetched increment is retained in memory. So, for example, if the accessor iterator's rangeSize attribute is set to 25, no more than 25 records will be held in memory at any given time.

    In a range paging version of the scrollable example above, the data control would fetch rows 1 through 25 and hold them in memory in order to display rows 1 through 20. If the user scrolled down, the data control would fetch data in increments of 25 as the user was scrolling but release the previous 25 records from memory as it fetched a new range. By the time the user reached rows 477 through 496 as in the example above, only rows 476 through 500 would be in memory.

    When scrolling to a position that displays data from multiple increments, only the data from the increment last fetched is held in memory.

    Note:

    When you use range paging in a data control, the built-in navigation operation Last does not work on databound UI components created from that data control.
  • No pagination. When there is no pagination, all available data for a UI component is fetched. No pagination is implemented when the data control does not implement a data control handler, such as oracle.adf.model.adapter.bean.DataFilterHandler or oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler. You can also use annotations to turn off paging for specific collections. For more information, see Section 2.3.4, "How to Specify Access Mode for Individual Objects in the Data Control."

  • Custom pagination. If the built-in pagination options do not suit your needs, you can implement your own pagination by implementing a custom handler class. For more information, see Section 2.3.7, "How to Implement a Custom Handler for Querying and Pagination."

For more information about access mode and data control handlers, see Section 2.3.3, "What You May Need to Know About the Scrollable and Range Paging Modes."

2.3.1 How to Change Paging Mode for a Data Control

If you want to change the paging mode for an EJB or bean data control, you can do so in the Data Controls panel.

Note:

For data controls using the oracle.adf.model.adapter.bean.DataFilterHandler or oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler handler, the default access mode is scrollable.

Before you begin:

It may be helpful to have a general understanding of access modes for EJB and bean data controls. For more information, see Section 2.3, "Paginated Fetching of Data in EJB Data Controls.".

You need to complete this task:


Create an EJB or bean data control. For more information, see Section 2.2.1, "How to Create ADF Data Controls.".

To change paging mode for a data control:

  1. In the Data Controls panel, right-click the data control's node and choose Edit Definition.

  2. In the ejb-definition Properties or the bean-definition Properties dialog, select rangePaging or scrollable from the AccessMode dropdown list.

  3. If you are changing the data control to use range paging, make sure that the data control's FactoryClass property is specified as oracle.adf.model.adapter.bean.BeanDCFactoryImpl.

    You can access the FactoryClass property in the source editor for the DataControls.dcx file or in the Property Inspector that appears when you open DataControls.dcx in the source editor or overview editor.

2.3.2 How to Set Range Size for a Data Control that Uses Range Paging

When you set a data control's access mode to rangePaging, the data control determines the range size by reading the rangeSize property of the accessor iterator of each component that is bound to a collection in the data control.

To set the range size for a component:

  1. In the Application Navigator, select the page containing the component that is bound to the data control.

  2. In the Structure window, select the component that is bound to the data control collection.

  3. In the Property Inspector, expand the Behavior node, and set the rangeSize property to the desired value.

For more information on iterator bindings, see "Creating and Using Iterator Bindings" in Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

2.3.3 What You May Need to Know About the Scrollable and Range Paging Modes

Data controls that support scrollable and range paging modes rely on methods in the bean class to implement that functionality. The method that the data control uses depends on the data control handler class that the data control uses.

For JPA-based data controls, typically the JPQLDataFilterHandler handler is specified. JPQLDataFilterHandler relies on the presence of JPA queries and a queryByRange() method in the bean.

For non-JPA bean data controls (and for JPA-based bean and EJB data controls that do not have a queryByRange() method), DataFilterHandler is specified. To implement range paging in data controls that use this handler, you need to add code to your bean class as shown in Section 2.3.6, "How To Manually Implement Pagination Support in a Bean Data Control."

For data controls that do not have either of these handler classes (such as data controls that were generated in an earlier version of the IDE), there is no built-in support for scrollable or range paging.

2.3.4 How to Specify Access Mode for Individual Objects in the Data Control

If your data control encompasses multiple collections of different sizes, you may wish to set different access modes for some of the collections. You can do so by placing annotations on the accessor methods in the bean that the data control represents.

For the methods on which the annotations are used, the annotations override the access mode set for the data control. If an accessor method does not have such an annotation, it inherits its access mode from the one that is defined for the data control.

To specify access mode for individual objects in a bean or EJB data control:

  1. Open the bean class on which the data control is based.

  2. Add annotations for the accessor methods for which you want a different access mode than that generally specified for the data control.

    Example 2-3 shows the necessary import statements and the available annotations and how they can be used on a collection.

Example 2-3 Access Mode Annotations

import oracle.adf.model.adapter.bean.annotation.AccessMode;
import oracle.adf.model.adapter.bean.annotation.AccessModeType;

...
   * List with scrollable access
   */  
  @AccessMode(type=AccessModeType.SCROLLABLE)
  public List<Employees> getEmployees() {
 ...
   * List with range paging.
   */  
  @AccessMode(type=AccessModeType.RANGE_PAGING)
  public List<Employees> getEmployees() {
 ...
   * List with no paging.
   */  
  @AccessMode(type=AccessModeType.NO_PAGING)
  public List<Countries> getCountries() {
...

Note:

For EJB data controls, you must place any @AccessMode annotations in the session bean interface that you are using (rather than the bean class).

2.3.5 What You May Need to Know About Sorting Tables Based on Range Paginated Collections

By default, if a user sorts a table that is bound to a JPA-based data control, the ADF Model runtime forces the iterator to return all rows into memory for sorting, even if the back-end JPQL queries have already done the sort at the database level, which can cause memory problems if collection is too large. If you are using range paging for a collection, you can disable the ADF Model runtime full in-memory sort and have the data control handle it instead, based on just the currently selected range.

To use the data control to handle the sort for range paginated collections:

  1. In the Application Navigator, double-click the DataControls.dcx file to open it in the overview editor.

  2. In the overview editor, select the node for the data control that you want to edit.

  3. In the Property Inspector, set the ImplementsSort property to true.

2.3.6 How To Manually Implement Pagination Support in a Bean Data Control

With non-JPA data controls (or any bean data control that uses the oracle.adf.model.adapter.bean.jpa.DataFilterHandler handler), you need to add three methods for each collection in the session or service facade in order for the ADF Model runtime to implement scrollable paging and range paging. The method signatures should take the following form:

List<EntityBeanName> getEntityBeanNameList()
List<EntityBeanName> getEntityBeanNameList(int firstResult, int maxResults)
long getEntityBeanNameListSize()

2.3.7 How to Implement a Custom Handler for Querying and Pagination

If the built-in querying and paging options are not sufficient for your application, you can implement your own custom paging and querying behavior by providing your own data handler class for your data control.

To implement a custom handler for querying and pagination:

  1. Write a custom data control handler class and add it to the data control's project.

    You can sub-classes an existing handler, such as oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler or oracle.adf.model.adapter.bean.DataFilterHandler. See Example 2-4 for an outline of a custom handler class.

  2. In the Source view of the DataControls.dcx file, type the fully-qualified class name of the handler as the value for the DataControlHandler attribute of each data control.

    DataControlHandler is an attribute of the ejb-definition element of EJB data controls and an attribute of the bean-definition element for bean data controls.

Example 2-4 Custom Data Control Handler

public class MyJPQLDataFilterHandler  extends JPQLDataFilterHandler
{
 public boolean invoke(Map bindingContext,
                                 OperationBinding action,
                                 DataFilter filter)
 {
    /** TODO: Developer provides custom criteria. */
 }
 
 public Object invoke(RowContext rowCtx, String name,
                               DataFilter filter)
 {
    /** TODO: Developer provides custom criteria. */
 } 
 
}

2.4 Using the Data Controls Panel

You can design a databound user interface by dragging an item from the Data Controls panel and dropping it on a page as a specific UI component. When you use data controls to create a UI component, JDeveloper automatically creates the various code and objects needed to bind the component to the data control you selected.

In the Data Controls panel, each object is represented by a specific icon. Table 2-1 describes what each icon represents, where it appears in the Data Controls panel hierarchy, and what components it can be used to create.

Table 2-1 The Data Controls Panel Icons and Object Hierarchy

Icon Name Description Used to Create...

Data control icon.

Data Control

Represents a data control. You cannot use the data control itself to create UI components, but you can use any of the child objects listed under it. Depending on how your business services were defined, there may be more than one data control.

Serves as a container for the other objects, and is not used to create anything.

Accessor return icon.

Accessor Returned Collection

Represents an object returned by a bean-style accessor method on the business service. For example, if when you created a session bean, you chose to also create accessor methods for each of the Java entities under the session bean, then an accessor returned collection is displayed for each of those entities.

If an entity contains a relationship to another entity (for example, a foreign key), then a child accessor returned collection is shown for that entity In ADF, the relationship between parent and child entities is called a master-detail relationship.

The children under a collection may be attributes of the elements that make up the collection, operations on the entire collection, or operations on the row for each element in the collection.

For collections: forms, tables, trees, range navigation components, and master-detail widgets.

For single objects: forms, master-detail widgets, and selection lists.

For more information about creating forms, and navigation components, see Chapter 3, "Creating a Basic Databound Page."

For more information about creating tables, see Chapter 4, "Creating ADF Databound Tables."

For information about creating trees and other master-detail UI components, see Chapter 5, "Displaying Master-Detail Data."

For information about creating lists, see Chapter 6, "Creating Databound Selection Lists."

Attribute icon.

Attribute

Represents a discrete data element in an object (for example, an attribute in a row). Attributes appear as children under the collections or method returns to which they belong.

Label, text field, date and selection list components.

For information about creating text fields, see Section 3.2, "Using Attributes to Create Text Fields."

Method icon

Method

Represents an operation in the data control or one of its exposed structures that may accept parameters, perform some business logic, and optionally return a single value, a structure, or a collection of a single value and a structure.

Command components.

For methods that accept parameters: command components and parameterized forms.

For information about creating command components from methods, see Section 3.6, "Creating a Form to Edit an Existing Record."

For information about creating parameterized forms, see Section 3.5, "Creating a Form Using a Method That Takes Parameters."

Method return icon.

Method Return

Represents an object that is returned by a custom method. The returned object can be a single value or a collection.

A method return appears as a child under the method that returns it. The objects that appear as children under a method return can be attributes of the collection, other methods that perform actions related to the parent collection, or operations that can be performed on the parent collection.

For single values: text fields and selection lists.

For collections: forms, tables, trees, and range navigation components.

When a single-value method return is dropped, the method is not invoked automatically by the framework. A user either has to also create an invoke action as an executable, or drop the corresponding method as a button to invoke the method.

Data control operation icon.

Operation

Represents a built-in data control operation that performs actions on the parent object. Data control operations are located in an Operations node under collections or method returns. The operations that are children of a particular collection or method return operate on those objects only.

If an operation requires one or more parameters, they are listed in a Parameters node under the operation.

Command components such as buttons or links.

For information about creating command components from operations, see Section 3.4, "Incorporating Range Navigation into Forms."

Parameter icon.

Parameter

Represents a parameter value that is declared by the method or operation under which it appears. Parameters appear in the Parameters node under a method or operation.

Label, text, and selection list components.


2.4.1 How to Use the Data Controls Panel

JDeveloper provides you with a predefined set of UI components from which to choose for each data control item you drop.

To use the Data Controls panel to create UI components:

  1. Select an item in the Data Controls panel and drag it onto the visual editor for your page. For a definition of each item in the panel, see Table 2-1.

  2. From the ensuing context menu, select a UI component.

    When you drag an item from the Data Controls panel and drop it on a page, JDeveloper displays a context menu of all the default UI components available for the item you dropped.

    Figure 2-6 shows the context menu displayed when an accessor returned collection from the Data Controls panel is dropped on a page.

    Figure 2-6 Data Controls Panel Context Menu

    Data Control Palette Context Menu

    Depending on the component you select from the context menu, JDeveloper may display a dialog that enables you to define how you want the component to look.

    The resulting UI component appears in the JDeveloper visual editor, as shown in Figure 2-7.

    Figure 2-7 Databound UI Component: ADF Table

    Sample databound UI component

Tip:

Instead of creating automatically bound UI components using the Data Controls panel, you can create your UI first and then bind the components to the ADF Model layer. For more information, see the "Using Simple UI First Development" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

2.4.2 What Happens When You Use the Data Controls Panel to Create UI Components

When a web application is built using the Data Controls panel, JDeveloper does the following:

  • Creates a DataBindings.cpx file in the default package for the project (if one does not already exist), and adds an entry for the page.

    DataBindings.cpx files define the binding context (a container object that holds a list of available data controls and data binding objects) for the application. Each DataBindings.cpx file maps individual pages to the binding definitions in the page definition file and registers the data controls used by those pages. Figure 2-8 shows a DataBindings.cpx file in the overview editor of JDeveloper.

    Figure 2-8 DataBindings.cpx File in the Overview Editor

    cpx file

    Example 2-5 shows the code from the corresponding XML file.

    Example 2-5 DataBindings.cpx File

    <?xml version="1.0" encoding="UTF-8" ?>
    <Application xmlns="http://xmlns.oracle.com/adfm/application"
                 version="11.1.1.54.7" id="DataBindings" SeparateXMLFiles="false"
                 Package="oracle.fodemo.supplier" ClientType="Generic"
                 ErrorHandlerClass="oracle.fodemo.frmwkext.CustomErrorHandlerImpl">
      <definitionFactories>
        <factory nameSpace="http://xmlns.oracle.com/adf/controller/binding"
                 className="oracle.adf.controller.internal.binding.
                                                      TaskFlowBindingDefFactoryImpl"/>
        <dtfactory className="oracle.adf.controller.internal.dtrt.binding.
                                                             BindingDTObjectFactory"/>
      </definitionFactories>
      <pageMap>
        <page path="/templates/StoreFrontTemplate.jspx"
              usageId="oracle_fodemo_supplier_StoreFrontTemplatePageDef"/>
        <page path="/browse.jspx" usageId="oracle_fodemo_supplier_browsePageDef"/>
        <page path="/supplier/supplierDetails.jspx"
              usageId="oracle_fodemo_supplier_supplierdetailPageDef"/>
        <page path="/login_error.jspx"
              usageId="oracle_fodemo_supplier_login_errorPageDef"/>
    . . .
      </pageMap>
      <pageDefinitionUsages>
        <page id="oracle_fodemo_supplier_StoreFrontTemplatePageDef"
              path="templates.StoreFrontTemplatePageDef"/>
        <page id="oracle_fodemo_supplier_browsePageDef"
              path="oracle.fodemo.supplier.pageDefs.browsePageDef"/>
        <page id="oracle_fodemo_supplier_supplierdetailPageDef"
              path="oracle.fodemo.supplier.pageDefs.supplierdetailPageDef"/>
        <page id="oracle_fodemo_supplier_login_errorPageDef"
              path="oracle.fodemo.supplier.pageDefs.login_errorPageDef"/>
    . . .
      </pageDefinitionUsages>
      <dataControlUsages>
        <dc id="GenericServiceFacadeLocal"
            path="oracle.fodemo.supplier.model.GenericServiceFacadeLocal"/>
        <dc id="SupplierFacadeLocal"
            path="oracle.fodemo.supplier.model.SupplierFacadeLocal"/>
      </dataControlUsages>
    </Application>
    

    For more information about the .cpx file, see the "Working with the DataBindings.cpx File" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

  • Creates the adfm.xml file in the META-INF directory. This file creates a registry for the DataBindings.cpx file, and is used by the applications metadata layer to allow customization and personalization of the application. Example 2-6 shows an example of an adfm.xml file.

    Example 2-6 adfm.xml File

    <MetadataDirectory xmlns="http://xmlns.oracle.com/adfm/metainf"
                       version="11.1.1.0.0">
      <DataBindingRegistry path="oracle/fodemo/supplier/DataBindings.cpx"/>
    </MetadataDirectory>
    
  • For web applications, registers the ADF binding filter in the web.xml file.

    The ADF binding filter preprocesses any HTTP requests that may require access to the binding context. For more information about the filter, see the "Configuring the ADF Binding Filter" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

  • Adds the following libraries to the project:

    • ADF Model Runtime

    • ADF Model Generic Runtime

  • Adds a page definition file (if one does not already exist for the page) to the page definition subpackage. The default subpackage is view.pageDefs in the adfmsrc directory.

    The page definition file (pageNamePageDef.xml) defines the ADF binding container for each page in an application's view layer. The binding container provides runtime access to all the ADF binding objects. Figure 2-9 shows a page definition file in the overview editor of JDeveloper.

    Figure 2-9 Page Definition File

    Page definition file
  • Configures the page definition file, which includes adding definitions of the binding objects referenced by the page. Example 2-7 shows the corresponding XML for a page definition.

    Example 2-7 Page Definition File

    <pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                    version="11.1.1.54.43" id="browsePageDef"
                    Package="oracle.fodemo.supplier.pageDefs">
      <parameters/>
      <executables>
        <variableIterator id="variables"/>
        <page path="templates.StoreFrontTemplatePageDef" id="pageTemplateBinding"
              Refresh="ifNeeded"/>
        <iterator Binds="root" RangeSize="25" DataControl="SupplierFacadeLocal"
                  id="SupplierFacadeLocalIterator"/>
        <accessorIterator MasterBinding="SupplierFacadeLocalIterator"
                          Binds="productFindAll" RangeSize="25"
                          DataControl="SupplierFacadeLocal"
                          BeanClass="oracle.fodemo.supplier.model.Product"
                          id="productFindAllIterator" Refresh="ifNeeded"/>
        <searchRegion Criteria="__ImplicitViewCriteria__"
                      Customizer="oracle.jbo.uicli.binding.JUSearchBindingCustomizer"
                      Binds="productFindAllIterator"
                      id="ImplicitViewCriteriaQuery" />
      </executables>
      <bindings>
        <tree IterBinding="productFindAllIterator" id="productFindAll">
          <nodeDefinition DefName="oracle.fodemo.supplier.model.Product"
                          Name="productFindAll0">
            <AttrNames>
              <Item Value="productId"/>
              <Item Value="productName"/>
              <Item Value="costPrice"/>
              <Item Value="listPrice"/>
              <Item Value="minPrice"/>
              <Item Value="productStatus"/>
            </AttrNames>
          </nodeDefinition>
        </tree>
        <methodAction id="removeProduct" RequiresUpdateModel="true"
                      Action="invokeMethod" MethodName="removeProduct"
                      IsViewObjectMethod="false" DataControl="SupplierFacadeLocal"
                      InstanceName="SupplierFacadeLocal.dataProvider">
          <NamedData NDName="product"
                  NDValue="${bindings.productFindAllIterator.currentRow.dataProvider}"
                     NDType="oracle.fodemo.supplier.model.Product"/>
        </methodAction>
        <action IterBinding="productFindAllIterator" id="Delete"
                RequiresUpdateModel="false" Action="removeCurrentRow"/>
      </bindings>
    </pageDefinition>
    

    For more information about the page definition file, see the "Working with Page Definition Files" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

  • Adds prebuilt components to the view page.

    These prebuilt components include ADF data bindings that reference the binding objects in the page definition file. Example 2-8 shows a JSF page that contains components that have been bound using ADF Model data binding. Note that values of the output text components are bound to values of the productsFindAll binding object, as defined in the page definition file in Example 2-7.

    Example 2-8 JSF Page with ADF Model Data Binding

    .
    .
    .
    <af:column sortProperty="costPrice" sortable="false"
               headerText="#{bindings.productFindAll.hints.costPrice.label}"
               id="c6" align="right">
      <af:outputText value="#{row.costPrice}" id="ot1">
        <af:convertNumber groupingUsed="false"
                         pattern="#{bindings.productFindAll.hints.costPrice.format}"/>
      </af:outputText>
    </af:column>
    <af:column sortProperty="listPrice" sortable="false"
               headerText="#{bindings.productFindAll.hints.listPrice.label}"
               id="c1" align="right">
      <af:outputText value="#{row.listPrice}" id="ot6">
        <af:convertNumber groupingUsed="false"
                         pattern="#{bindings.productFindAll.hints.listPrice.format}"/>
      </af:outputText>
    </af:column>
    <af:column sortProperty="minPrice" sortable="false"
               headerText="#{bindings.productFindAll.hints.minPrice.label}"
               id="c3" align="right">
      <af:outputText value="#{row.minPrice}" id="ot3">
        <af:convertNumber groupingUsed="false"
                          pattern="#{bindings.productFindAll.hints.minPrice.format}"/>
      </af:outputText>
    </af:column>.
    .
    .
    .
    
  • For applications that use ADF Faces, adds all the files, and configuration elements required by ADF Faces components. For more information, see the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

2.4.3 What Happens at Runtime

When a page contains ADF bindings, at runtime the interaction with the business services initiated from the client or controller is managed by the application through the binding context. The binding context is a runtime map (named data and accessible through the EL expression #{data}) of all data controls and page definitions within the application.

The ADF lifecycle creates the ADF binding context from the DataControls.dcx, DataBindings.cpx, and page definition files, as shown in Figure 2-10. The DataControls.dcx file defines the data controls available to the application at design time, while the DataBindings.cpx files define what data controls are available to the application at runtime. A DataBindings.cpx file lists all the data controls that are being used by pages in the application and maps the binding containers, which contain the binding objects defined in the page definition files, to web page URLs, or in the case of a Java Swing application, the Java class. The page definition files define the binding objects used by the application pages. There is one page definition file for each page.

Figure 2-10 ADF Binding File Runtime Usage

dcx, cpx, page def make up binding context

The binding context does not contain real live instances of these objects. Instead, the map first contains references that become data control or binding container objects on demand. When the object (such as the page definition) is released from the application (for example when a task flow ends or when the binding container or data control is released at the end of the request), data controls and binding containers turn back into reference objects. For more information, see the "Understanding the Fusion Page Lifecycle" chapter of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

2.4.4 What You May Need to Know About Iterator Result Caching

When a data control modifies a collection, the data control must instantiate a new instance of the collection in order for the ADF Model layer to understand that it has been modified. In other words, although some action in the client may change the collection, that change will not be reflected in the UI unless a new instance of the collection is created. However, for performance reasons, accessor and method iterators cache their results set (by default, the cacheResults attribute on the iterator is set to true). This setting means that the iterator is refreshed and a new instance of the collection is created only when the page is first rendered. The iterator is not refreshed when the page is revisited, for example, if the page is refreshed using partial page rendering, or if the user navigates back to the page.

For example, say you want to allow sorting on a table on your page. Because you want the page to refresh after the sort, you add code to the listener for the sort event that will refresh the table using partial page rendering (for more information, see the "Rendering Partial Page Content" chapter of the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework). Because the instance of the collection for the table has already been instantiated and is cached, the accessor iterator will not reexecute, which means that a new instance of the collection with the new sort order will not be created, so the sort order on the page will remain the same.

To work around this issue, you can either configure the iterator so that it does not cache the results, or you can place a button on the page that can be used to reexecute the iterator when the page is refreshed. If your page does not have a button whose action attribute can be bound to a method, then you can use an invokeAction executable that will be invoked whenever the page is refreshed.

Note:

If your page uses the navigation operations to navigate through the collection, do not set CacheResults to false, as that navigation will no longer work. You must use a button to reexecute the iterator. For more information about the navigation operations, see Section 3.4, "Incorporating Range Navigation into Forms."

Performance Tip:

If you set an iterator to not cache its result set, and that result set is a collection that may return a large number of rows, performance will be negatively affected. For large result sets, you should use an invokeAction.

To set an iterator to not cache its result set:

  1. Open the page definition file, and in the Structure window, select the iterator whose results should not be cached.

  2. In the Property Inspector, expand the Advanced section and set CacheResults to false.

To use a button to reexecute the iterator:

  1. From the ADF Faces page of the Component Palette, drag and drop a Button onto the page.

  2. In the Structure window, right click the button and in the context menu, choose Bind to ADF Control.

  3. In the Bind to ADF Control dialog, expand the accessor associated with the iterator to reexecute, expand that accessor's Operations node, and select Execute.

To use an invokeAction to reexecute the iterator:

  1. Open the page definition file, and in the Structure window, right-click executables and choose Insert inside executables > invokeAction.

  2. In the Insert invokeAction dialog, set id to a unique name. Use the Binds dropdown list to select the iterator to be reexecuted.

  3. With the newly created invokeAction still selected, in the Property Inspector, set Refresh to prepareModel.

    This setting will cause the accessor method to be invoked during the Prepare Model phase. This refreshes the binding container.

  4. Set RefreshCondition to an EL expression that will cause the refresh to happen only if any value has actually changed. If this condition is not set, the invokeAction will be called twice.

    For example, the expression #{(userState.refresh) and (!adfFacesContext.postback)} will cause the refresh to happen only if the page is refreshed.

    For more information about the page lifecycle phases and using the refresh and refreshCondition attributes, see "The JSF and ADF Page Lifecycles" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

2.4.5 What You May Need to Know About Configuring Validation

You can set validation on the attribute bindings in a page definition file. When a user edits or enters data in a field for an attribute for which validation has been defined, and submits the form, the bound data is validated against the configured rules and conditions. If validation fails, the application displays an error message. For information and procedures on setting model layer validation, see the "Adding ADF Model Layer Validation" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

When you set validation, you can define the error message that will be displayed. By default, these messages are displayed in a client dialog. You can configure these messages to display inline instead. For more information, see the "Displaying Error Messages" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

You can also change the way messages are handled by creating your own error handling class. For more information, see the "Customizing Error Handling" section of the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.