This release supports need-based loading of related entities for applications that use ColdFusion ORM in the back end and Flex as the front end. Your application can now fetch the main entity and not return the related entities. Only when the client application tries to access the related entities, they are loaded.
Example
Maria uses ColdFusion ORM and is building a Flex application. She wants to list all employees and projects that each employee in her company is part of. Using lazy loading, users in her company can fetch the employee information first and the number of projects the employee has worked on. And later when the users click on a particular project, the feature lets them load the project information of a particular employee rather than having all the data loaded initially when the application is loaded (which was the behavior in ColdFusion 9).
Setting up lazy loading
For lazy loading enhancements to take effect, you need to perform various configurations on both client-side and server-side.
Server-side configuration
- In the services-config.xml, go to the section that specifies the channel-definition (for the channel that you use) and search the following:<serialize-array-to-arraycollection>*false*
- Change the value to true.
- (Optional) If you want to change the name of the method that is invoked while related entities are loaded, modify the name of the method from loadProxy.<proxy-load-method>*loadProxy</proxy-load-method>*
Add the loadProxy method to your service CFC as shown in the following sample code. The service CFC should be in the same directory as your ORM components:service.cfc
component {
remote function loadProxy(any proxyKey, any fullyqualifiedname)
{
//writeDump(var="#proxyKey#,,,#fullyqualifiedname#",output="console" );
writedump(var="#proxykey#",output="console");
if(fullyqualifiedname contains "cproducts"){
return EntityLoadByPK("cproducts",proxykey );
}else{
return EntityLoadByPK("ccategories",proxyKey );
}
}
}
|
The loadProxy method gets proxyKey (primary key of the entity instance) and fullyqualifiedname of the entity as arguments.The fullyqualifiedname sent by dphibernate is checked and the required object (in this case cproducts or ccategories) is returned.
- Set remotingFetch to lazy. lazy is the new value added to remtoingFetch (in addition to true and false) in the tag cfproprety:
- true: The value of the property is sent to Flash by way of Flash Remoting.
- false: Null is sent to Flash.
- lazy: Proxy object for the related entities is sent with only the primary key value.Only when any property on the proxy object is accessed, another remoting call reaches the load method defined on the CFC to which the primary key is passed. The load method returns the object with all the values populated.
Sample configuration
employee.cfc
<cfcomponent persistent="true" table="employees">
<cfproperty name="employeeID" fieldtype="id" generator="native"/>
<cfproperty name="personalObj" fieldtype="one-to-one" cfc="cpersonal" cascade="all"
remotingfetch="lazy"/>
<cfproperty name="lastName"/>
<cfproperty name="firstName"/>
</cfcomponent>
|
personal.cfc
<cfcomponent persistent="true" table="personal" >
<cfproperty name="personalID" generator="foreign" params="{property=EmployeeObj}">
<cfproperty name="employeeObj" fieldtype="one-to-one" cfc="employee constrained="true">
<cfproperty name="SSN">
<cfproperty name="fatherName">
</cfcomponent>
|
service.cfc
component {
remote function loadProxy(any proxyKey, any fullyqualifiedname)
{
//writeDump(var="#proxyKey#,,,#fullyqualifiedname#",output="console" );
writedump(var="#proxykey#",output="console");
if(fullyqualifiedname contains "cproducts"){
return EntityLoadByPK("cproducts",proxykey );
}else{
return EntityLoadByPK("ccategories",proxyKey );
}
}
}
|
Client-side configurations
- Add dpHibernate.swc to your Flex project. The file can be found in the following directory: /CFIDE/scripts/.
- Use the HibernateRemoteObject instead of the default RemoteObject to make remoting calls.
- Set HibernateManaged.defaultHibernateService to the Remote Object instance. The dpHibernate.swc uses this remote object instance to make load calls to the server for lazy loading.
- Ensure the following:
- ActionScript class is mapped to the CFC using the attribute alias in the RemoteClass.For example, RemoteClass(alias="orm.employees")
- ActionScript class extends from org.dphibernate.core.HibernateBean.
- Managed metadata is added to the ActionScript class.
- Perform Flash Remoting to fetch the entities.The related entities are loaded only when specifically accessed on the client.
Example
Category.as
package model.beans
{
import mx.collections.ArrayCollection;
import org.dphibernate.core.HibernateBean;
[RemoteClass(alias="lazyloading.o2m.ccategories")]
[Managed]
public class Category extends org.dphibernate.core.HibernateBean
{
public function Category()
{
}
public var categoryID:Number;
public var categoryName:String;
public var description:String;
public var products:ArrayCollection;
}
}
Product.as
package model.beans
{
import mx.collections.ArrayCollection;
import org.dphibernate.core.HibernateBean;
[RemoteClass(alias="lazyloading.o2m.cproducts")]
[Managed]
public class Product extends org.dphibernate.core.HibernateBean
{
public function Product()
{
}
public var productID:Number;
public var productName:String;
public var categoryID:Category;
}
}
LazyLoading.MXML
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:dp="org.dphibernate.rpc.*"
applicationComplete="onAppComplete(event)" minWidth="955" minHeight="600" xmlns:dphibernate="http://www.dphibernate.org/2010/mxml">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<dp:HibernateRemoteObject destination="ColdFusion" source="newmanual.apollounit.orm.lazyloading.o2m.service"
endpoint="http://localhost:8500/flex2gateway/lazyloading" id="BasicService"
result="BasicService_resultHandler(event)" fault="BasicService_faultHandler(event)">
</dp:HibernateRemoteObject>
</fx:Declarations>
<fx:Script>
<![CDATA[
import model.beans.*;
import mx.binding.utils.BindingUtils;
import mx.collections.ArrayCollection;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import org.dphibernate.rpc.HibernateManaged;
[Bindable]
public var categories:ArrayCollection;
[Bindable]
public var ProdsList:ArrayCollection;
[Bindable]
public var displaytext:String;
public var category:Category;
public var product:Product;
public var employee:Employee;
public var employee_self123:Employee_self;
protected function BasicService_resultHandler(event:ResultEvent):void
{
if( event.token.operation == "getCategories"){
categories = event.result as ArrayCollection;
var len:Number = categories.length;
allintext.text = event.result.length;
}else if(event.token.operation == "getProducts"){
ProdsList = event.result as ArrayCollection;
}
}
protected function BasicService_faultHandler(event:FaultEvent):void
{
allintext.text="There is an error";
}
protected function onAppComplete(event:Event):void
{
HibernateManaged.defaultHibernateService=BasicService;
//var token:AsyncToken = BasicService.getCategories();
//token.operation="getCategories";
}
protected function load_clickHandler(event:MouseEvent):void
{
var token:AsyncToken = BasicService.getCategories();
token.operation="getCategories";
}
]]>
</fx:Script>
<s:TextArea x="10" y="39" id="allintext" text="hello"/>
<s:Button x="240" y="10" label="Load Categories" id="load" click="load_clickHandler(event)"/>
<mx:DataGrid x="240" y="39" id="CategoryList" dataProvider="{categories}">
<mx:columns>
<mx:DataGridColumn headerText="CategoryName" dataField="categoryName"/>
<mx:DataGridColumn headerText="CategoryID" dataField="categoryID"/>
</mx:columns>
</mx:DataGrid>
<mx:DataGrid x="239" y="198" id="ProductList" dataProvider="{Category(CategoryList.selectedItem).products}">
<mx:columns>
<mx:DataGridColumn headerText="ID" dataField="productID"/>
<mx:DataGridColumn headerText="Name" dataField="productName"/>
</mx:columns>
</mx:DataGrid>
</s:Application>
|
ccategories.cfc
<cfcomponent persistent="true" table="categories">
<cfproperty name="categoryID" fieldtype="id" generator="native"/>
<cfproperty name="categoryName"/>
<cfproperty name="description"/>
<cfproperty name="products" fieldtype="one-to-many" cfc="cproducts" cascade="all"
fkcolumn="categoryid" lazy="true" remotingfetch="lazy"/>
</cfcomponent>
|
cproducts.cfc
<cfcomponent persistent="true" table="products">
<cfproperty name="productID" fieldtype="id" generator="native"/>
<cfproperty name="productName"/>
<cfproperty name="categoryID" fieldtype="many-to-one" cfc="ccategories" lazy="true"
remotingfetch="lazy"/>
</cfcomponent>
|
Note on using debugger/network monitor in FlashBuilder
When you inspect the results returned by the server in debugging mode, debugger fetches the related entities (and defies the purpose of lazy loading). This can also occur when you use network monitor.
However, this issue does not occur when you run the application.