Thursday, March 10, 2011

What you may need to know about pagination support in EJB and JavaBean Data Control

Once you application's business service is in place, you can use JDeveloper to create data controls that provide the information needed to declaratively bind UI components to those service. This meta information is also used by the ADF model layer to invoke the business services at run time. The Data Control act as a Proxy Cum Adaptor for your business services. ADF has built in support for most of the common technologies like EJB, Web services, POJO, ADF BC etc. This means that, you may get consistent development experience across these technologies, and moreover your application's view layer may remain decoupled from the business service implementation. In todays post, I'm discussing pagination support for EJB and JavaBean Data Control.

How to create Data Control?

Please refer Chapter 2 - ADF Model Data BindingJava EE Developer's Guide to learn more if you don't have much idea on this topic. This concept remains same for both EJB and JavaBean Data Controls.

Pagination Support

When you generated Data Control, design time 'wizard' creates Data control definition file (DataControls.dcx) within your project source. It contains meta data for your business service implementation and also settings that determine how the data control behaves at run time. Please take a look at the following sample dcx file(s).

Data Control definition for JavaBean:

 <?xml version="1.0" encoding="UTF-8" ?>  
 <DataControlConfigs xmlns="http://xmlns.oracle.com/adfm/configuration"  
           version="11.1.1.59.23" id="DataControls" Package="model">  
   <AdapterDataControl id="CustomPaginatedJavaServiceFacade"  
            FactoryClass="oracle.adf.model.adapter.DataControlFactoryImpl"  
            ImplDef="oracle.adf.model.adapter.bean.BeanDefinition"  
            SupportsTransactions="false" SupportsSortCollection="true"  
            SupportsResetState="false" SupportsRangesize="false"  
            SupportsFindMode="false" SupportsUpdates="true"  
            Definition="model.CustomPaginatedJavaServiceFacade"  
            BeanClass="model.CustomPaginatedJavaServiceFacade"  
            xmlns="http://xmlns.oracle.com/adfm/datacontrol">  
   <CreatableTypes>  
    <TypeInfo FullName="model.Employees"/>  
    <TypeInfo FullName="model.Departments"/>  
    <TypeInfo FullName="model.CustomPaginatedJavaServiceFacade"/>  
   </CreatableTypes>  
   <Source>  
    <bean-definition BeanClass="model.CustomPaginatedJavaServiceFacade"  
             DataControlHandler="oracle.adf.model.adapter.bean.DataFilterHandler"  
             xmlns="http://xmlns.oracle.com/adfm/adapter/bean"/>  
   </Source>  
  </AdapterDataControl>  
 </DataControlConfigs>  

Please note that, DataControlHandler entry that you see for the above JavaBean Data Control is keyed-in by me :) By default the editor may not generate DataControlHandler for a POJO Bean. Runtime will use the default DataControlHandler in that case.

Data Control definition for EJB:

 <?xml version="1.0" encoding="UTF-8" ?>  
 <DataControlConfigs xmlns="http://xmlns.oracle.com/adfm/configuration"  
           version="11.1.1.59.23" id="DataControls"  
           Package="model.ejb">  
  <AdapterDataControl id="SessionEJBLocal"  
            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="model.ejb.SessionEJBLocal"  
            BeanClass="model.ejb.SessionEJBLocal"  
            xmlns="http://xmlns.oracle.com/adfm/datacontrol">  
   <CreatableTypes>  
    <TypeInfo FullName="model.ejb.Departments"/>  
    <TypeInfo FullName="model.ejb.Employees"/>  
   </CreatableTypes>  
   <Source>  
    <ejb-definition ejb-version="3.0" ejb-name="SessionEJB" ejb-type="Session"  
            ejb-business-interface="model.ejb.SessionEJBLocal"  
            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>   
 </DataControlConfigs>  

What is the role of DataControlHandler in ADF binding?

Well, in nutshell, the DataControlHandler entry in the dcx file decides the Data Control behavior at runtime( pagination, filtering support etc.). This means that all your service calls routed through this guy.

 <bean-definition BeanClass="model.CustomPaginatedJavaServiceFacade"  
          DataControlHandler="oracle.adf.model.adapter.bean.DataFilterHandler"  
          xmlns="http://xmlns.oracle.com/adfm/adapter/bean"/>  

Let us take a look at Two possible DataControlHandler candidates for your Service Facade Bean.

oracle.adf.model.adapter.bean.DataFilterHandler:
This is a very generic implementation which supports both pagination and in memory filtering, typically used for JavaBean Data Control. To enable pagination, developers may need to define set of methods which follows predefined contracts. To explain this, let us assume that you have an accessor method in your JavaBean to get all Departments as shown below.

public List<departments> getDepartments(){...}

Now,to enable pagination, you may need to have two more supporting methods with specific naming conventions and signature as given below.

public List<departments> getDepartments(int start, int increment) {...) - Range method to fetch records pagewise
public long getDepartmentsSize(){...} - Returns total number of records

At runtime, data binding layer may call getDepartmentsSize() first to decide the number of records, followed by the paginated getter - getDepartments(int start, int increment).
Please note that data filtering is(if requested from UI) happening in memory here.

oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler:
This is a typical DataFilterHandler implementation for JPA based service facade. This class generates dynamic jpql for enabling pagination and filtering for you JPA based accessor methods defined in the Session Bean. The JPQLDataFilterHandler supports pagination, filtering and query criteria(model driven query) out of the box. When you build Session EJB Bean using the editor for your JPA based model project, the wizard would create a method with below signature in the generated source.

public Object queryByRange(String jpqlStmt, int firstResult, int maxResults){...}
All the request for paginated data and filtering of data are routed though this method. In other words, JPQLDataFilterHandler intercepts pagination, filter requests from a table and passes the request to queryByRange defined in the Bean with required set of parameters.

Important Note: By default, when you drag an accessor returned collection from an EJB Data Control and drop on a page, the result is a paginated collection and the session bean's built-in queryByRange() method will be used (instead of the getXXXFindAll() method) to retrieve the collection. If you have some custom code inside getXXXFindAll() and you want to invoke this piece while getting the collection, then there are two options for you:

Option 1: You can remove the DataControlHandler attribute in the source editor for the DataControls.dcx file. However, when you remove that attribute, the built-in support for named queries and pagination is also disabled. This approach is applicable if you don't need the built in pagination support at all.

Option 2: Replace the DataControlHandler attribute with oracle.adf.model.adapter.bean.DataFilterHandler using the source editor for the DataControls.dcx file. In this case you may need to define the supporting methods (as we discussed above under 'DataFilterHandler') to enable pagination. Of course, you may need to keep the custom logic(if any) which needs to be invoked during 'getter' invocation under the range fetch method. Query based filtering is disabled in this case, instead runtime may go for in memory filtering

Example:
public List<departments> getDepartments(){...} - accessor method
public List<departments> getDepartments(int start, int increment){...} - range fetch method
public long getDepartmentsSize(){...} - count fetch method


You can downloadthe sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

A glance at the implementation

This sample application uses JPA based model. To illustrate the above discussed concepts, I'm exposing services through two channels - 1. POJOServiceBean 2. SessionEJBBean

Bean Data Control for POJOServiceBean is configured to use oracle.adf.model.adapter.bean.DataFilterHandler as DataControlHandler. You can see supporting methods in the Bean class to enable pagination for the accessor methods.

EJB Data Control for SessionEJBBean is configured to use oracle.adf.model.adapter.bean.jpa.JPQLDataFilterHandler as DataControlHandler. In this case, pagination/filtering requests are routed through public Object queryByRange(...) method of the SessionEJBBean.

You can see two JSF pages in the view project(ejbView/pojoView). As the name sounds, each built from the different data controls.


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

9 comments:

Anonymous said...

Thanks!! this was helpful

amit said...

Hi Jobinesh,
I see the pagination working fine with POJO DC. Thanks for the help and notes.
I am in an issue with the DataControlHandler. Assume each department has a list of employees. There are three methods getEmployees(), getEmployeesSize() and getEmployees(int,int) inside Departments entity class.

Now, I draw a tree out of Departments collection. I link the Employees object as first level child in the tree iterator.
I see that the getEmployeesSize() and getEmployees(int,int) methods are called on expansion of tree nodes. This gives impression that pagination is working on second level hierarchical data.

However, all the Departments nodes of the tree show the same employees beneath them. All the department nodes show the employees of first expanded department node.

I have posted the issue on the forum.(https://forums.oracle.com/forums/thread.jspa?messageID=10268238&#10268238).

A sample workspace to see this behavior can be downloaded from http://goo.gl/3P8SW


Please review the issue and help me. Thanks.

amit said...

If I remove the getEmployeesSize() and getEmployees(int,int) methods from the Departments Entity class, things work fine. But then there is no pagination on second level.

amit said...

Thanks for your prompt reply and help.

For all other audience,

"Pagination for Bean DC is not supported for details list" is the answer to the question.

vinod kumar said...

Hi Jobinesh,
pagination with pojo and ejb works perfectly , but i tried to call method with parameters say getEmployeesByDept(depid) which will return a collection,from data control i will drag and drop the method return as table but pagination is not working, how shall i make a method with parameters to support pagination, i am using jdeveloper 11.1.15.0, Please review the issue and help me. Thanks

Jobinesh said...

Vinod,
This support has been added to the upcoming release. I don't think this is available in any of the erliear releases.

satyabrata debta said...

Hi Jobinesh,
By considering vinod's question,is there any declarative way to do it,if yes,then what is approach ? we can't change the Jdev version bcoz we need a Webcenter extension on that Jdeveloper which is not possible in R2 release.

So by considering all these point , how can we make a pagination and filter in the table.i will appreciate your valuable feedback on that.

Thanks,
Satya

satyabrata debta said...
This comment has been removed by the author.
krish509 said...

Hi Jobinesh,

we are implementing pagination in for adf table using pojo model . I have gone through your code and it was working fine. Is there any way we can achieve the same functionality with out converting to data control.

to be clear i don't want to create data control and don't want to use DataFilterHandler then how can i achieve the same .

Thanks in Advance
Kishore