Requirements      
Prerequisite knowledge
Required products
Sample files User level
This tutorial is intended for advanced developers who have knowledge of CFML, MXML, Flex 4, and ActionScript 3.0, as well as experience with ColdFusion 9, MySQL, ColdFusion Administrator, ORM concepts, and object-oriented programming.
Flash Builder 4 (Download trial)
Adobe ColdFusion Builder (2016 release)(Download trial)
Adobe ColdFusion Enterprise Edition (2016 release) (Download trial)
cf9_orm_flex4.zip (15 KB)
Advanced

 

In this tutorial you will learn how to integrate a Flex 4 application and ColdFusion 9, using Object Relational Mapping (ORM) for communication with the database.
Flex, ActionScript, and MXML allow you to easily build applications for Flash Player that communicate with a wide variety of servers, using services such as BlazeDS or LiveCycle Data Services for data source access. This capability is helping large companies replace legacy desktop software with Rich Internet Applications (RIAs).
ColdFusion 9 is a dynamic, practical back end for RIAs. Perhaps the biggest breakthrough in this last release of ColdFusion is the inclusion of Hibernate to give ColdFusion native ORM support. With a Flash Remoting engine for communicating with Flex applications, ColdFusion 9 is poised to be the perfect back end for your RIAs.
The development scenario for this article is based building an application for a client who provided the following requirements:
  • A screen that lists all system users from a database (including first and last name) by group
  • Users will be displayed filtered by group
  • On the same screen there will be a form to edit or create a user
  • The system must store name, last name, e-mail, and group, for each user
  • The system should not perform page refreshes; instead it should use asynchronous access.
 

 
Configuring the development environment

By combining the capabilities of Flash Builder and ColdFusion Builder, you can set up a development environment for efficient RIA development. You can install ColdFusion Builder as a plug-in to Flash Builder, or you can install Flash Builder as a plug-in to ColdFusion Builder. Either option will work because both products are based on Eclipse, and they inherit Eclipse's powerful plug-in capabilities.
To get started you'll need to install ColdFusion 9 (use the server configuration), Flash Builder 4, and ColdFusion Builder. The instructions that follow assume you have installed ColdFusion Builder as a plug-in to Flash Builder; you may need to modify them slightly if you have installed Flash Builder as a plug-in to ColdFusion Builder or both products as plug-ins to another Eclipse deployment.
 
Setting up the project
Note: The instructions that follow can be used to set up a project that uses Flex and ColdFusion, such as the example project. You can also set up the project by importing the sample files, as explained in Importing the source code.
Follow these steps to set up the project in Flash Builder and add Flex properties:
  1. Choose File > New > Other and then select ColdFusion > ColdFusion Project (see Figure 1).
  2. Click Next.
 
Figure 1. Creating a new project
Figure 1. Creating a new project
 
  1. Type a name and specify the location of the project (see Figure 2).
    As a practice, I create a folder below the ColdFusion server root, and immediately below that I create a subfolder for the company that the project is for. Below that I can store multiple projects for the company. In this case I specified C:\ColdFusion9\wwwroot\com\pcsilva\adc\lesson1 as the location.
  2. Click Next.

 

Figure 2. Specifying the name and location of the project
  1. Select the ColdFusion server to be used with this project (you may need to add and configure it first).
  2. Click Finish (see Figure 3).
 
Figure 3. Selecting the server for the project
Figure 3. Selecting the server for the project
 
At this point, your ColdFusion project is configured, and you need to add Flex properties to it.
  1. Right-click the project name, select Add/Change Project Type and then select Add Flex Project Type (see Figure 4).
 
Figure 4. Adding the Flex project type
Figure 4. Adding the Flex project type
 
  1. In the Adding Flex Project Type dialog box, select the Flex 4 SDK (the default), and select ColdFusion as the Application Server Type.
  2. Make sure Use Remote Object Service is selected, and then select ColdFusion Flash Remoting (see Figure 5).
  3. Click Next.

 

Figure 5. Configuring the Flex project
Figure 5. Configuring the Flex project
 
Next, type your ColdFusion root directory and web root, and then click Validate (see Figure 6). (I also changed the Output Folder to html; this is where the SWF is stored after compilation.)
  1. Click Finish.
 
Figure 6. Specifying the server location
 
Now you'll need to add to the project two SWC files, fds.swc (for Flex Data Services) and cfservices.swc (for ColdFusion services).
  1. Right-click the project and select Properties.
  2. Select Flex Build Path.
  3. Click Add SWC and then type C:/ColdFusion9/wwwroot/CFIDE/scripts/AIR/cfservices.swc and click OK.
  4. You can also change the main source folder. I changed mine to mxml (see Figure 7).
  5. Click OK.
 
Figure 7. Adding SWCs to the library path
Figure 7. Adding SWCs to the library path
 
  1. Lastly, move the contents of the src folder to the mxml folder (see Figure 8).

 

Figure 8. Folders and files of the project in Package Explorer
Figure 8. Folders and files of the project in Package Explorer

 
Setting up the data source

Next you'll need to configure the data source for the project.
  1. Download the sample file cf9_orm_flex4.zip and unpack it a folder named com in your ColdFusion wwwroot directory (For example, on Windows this would be C:\ColdFusion9\wwwroot\com).
  2. To set up the database, start MySQL and execute the commands in C:\ColdFusion9\wwwroot\com\pcsilva\adc\article1\database\database.sql. (In MySQL, type source <filename> to execute all the commands.) This creates a database named user_manager, which contains two tables: user and group.
  3. To set up the data source, start ColdFusion Administrator and create a new data source named user_manager_mysql for the database you just created.
 
Importing the source code
You can use the sample files in cf9_orm_flex4.zip to set up the project and you can use them as support material; follow these steps to import the sample source code:
  1. In Flash Builder, choose File > Import > Other; then expand the General folder and select Existing Projects Into Workspace (see Figure 9).
  2. Click Next
 
Figure 9. Importing an existing project
Figure 9. Importing an existing project
 
  1. Type the root directory of the sample project; in this case it will be C:\ColdFusion9\wwwroot\com\pcsilva\adc\article1 (see Figure 10).

 

Figure 10. Specifying the location of the project to import
Figure 10. Specifying the location of the project to import
 
  1. Verify the Flex Build Path is correct (see Figure 7).
  2. Choose Project > Clean to discard previous build states.
  3. In the Problems view you'll see an HTML wrapper error; right-click the error and select Recreate HTML Templates (see Figure 11).

 

Figure 11. Recreating the HTML templates
Figure 11. Recreating the HTML templates
 
  1. Choose Project > Clean once more and you're ready to use the example source code.

 

 
Creating ORM and CRUD operations

You can use the ColdFusion Builder Extension Adobe CFC Generator to accelerate deployment by automatically creating ORM entities, their respective Data Access Objects (DAOs), and the needed directory structures. Follow these steps:
  1. Open the RDS Dataview tab. If it is not visible, choose Window > Other Views, expand ColdFusion, and select RDS Dataview.
  2. Expand the user_manager_mysql node, right-click the group table and select Adobe CFC Generator > Create ORM CFC (see Figure 12).
 
Figure 12. Creating ORM CFC for one table
Figure 12. Creating ORM CFC for one table
 
  1. In the Adobe CFC Generator dialog box type the folder of the project (C:\ColdFusion9\wwwroot\com\pcsilva\adc\article1) and the ColdFusion mapping of this path; for example: com.pcsilva.adc.article1 (see Figure 13).
  2. Click OK.

 

Figure 13. Specifying the location for the ORM CFC
Figure 13. Specifying the location for the ORM CFC
 
  1. On the next screen, click Generate Code (see Figure 14). The extension will create Group.cfc and GroupDAO.cfc.

 

Figure 14. Generating the ORM CFC code for one table
Figure 14. Generating the ORM CFC code for one table
 
  1. Back in the RDS Dataview tab, select both the group and user tables together (see Figure 15).
  2. Right-click and select Adobe CFG Generator > Create ORM CFC (see Figure 15).

 

Figure 15. Creating the ORM CFC for two tables
Figure 15. Creating the ORM CFC for two tables
 
In the Create ORM CFC dialog box (see Figure 16), the steps are more complicated this time.
  1. Select the user table in the left column.
  2. Under the Relationships grid, click insert.
  3. In the first column (Relation Name), type the ColdFusion property name: group.
  4. In the second column (Target Table), type the entity name of destination: group.
  5. In the third column (Multiplicity), specify the relation type, in this case n-1.
  6. Click Save under the Relationships grid and select this row.
  7. Under the Join Conditions grid, click Insert.
  8. Type user.group_id as the Source Field and group.id as the Target Field.
  9. Click Save under the Join Conditions grid.
 
Figure 16. Generating ORM CFC code for two tables
Figure 16. Generating ORM CFC code for two tables
 
Now, you have created two entities and two DAOs.
  1. After creating the ORM CFC, open User.cfc and locate the cfproperty named group.
  2. Add the attribute fetch="join" , which will make the User object use queries with JOIN in them to load data into the related Group object and reduce the number of database queries you'll need to make to populate the User object.
  3. Also add remotingfetch="true" , to ensure that it executes a fetch for remoting calls. The code should now read as follows:
 
<cfproperty name="group" remotingfetch="true" fetch="join" fieldtype="many-to-one" cfc="Group" fkcolumn="group_id" >
  1. To ensure the ColdFusion entities remain visible in all CFML applications, you should create a Hibernate mapping (HBMXML) file as described in the ColdFusion 9 documentation.
 

 
ColdFusion remoting configuration

Next you need to create a CFC that will simplify access to the application server DAO from Flex. This CFC provides data model access, allowing the Flex application to work as a controller, and serves as a single source for the endpoint. In the sample files, you'll find UserFacade.cfc, which includes both entities in just one file.
 
UserFacade.cfc
 
component output="false" { import com.pcsilva.adc.article1.cfml.entity.User; import com.pcsilva.adc.article1.cfml.entity.Group; import com.pcsilva.adc.article1.cfml.dao.UserDAO; import com.pcsilva.adc.article1.cfml.dao.GroupDAO; userDAO = new UserDAO(); groupDAO = new GroupDAO(); /* * User opertators */ remote User function saveUser(User user) { return userDAO.Save(user); } remote void function deleteUser(numeric ID) { return userDAO.DeleteUser(ID); } remote Array function getUsers() { return userDAO.GetAllUser(); } remote Array function getUsers_paged(numeric startIndex, numeric numItems) { return userDAO.GetUsers_paged(startIndex,numItems); } remote User function getUser(numeric ID) { return userDAO.GetUser(ID); } remote numeric function countUser() { return userDAO.Count(); } //specific method for client remote Array function getUsersByGroup(Group group) { //return userDAO.GetUsersByGroup(group); //this must be done in userDAO.GetUsersByGroup var foreignKeysMap = { group = group }; return entityload("User",foreignKeysMap); } /* * Group opertators */ remote Group function saveGroup(Group group) { return groupDAO.save(group); } remote void function deleteGroup(numeric ID) { return groupDAO.DeleteGroup(ID); } remote Array function getGroups() { return groupDAO.GetAllGroup(); } remote Array function getGroups_paged(numeric startIndex, numeric numItems) { return groupDAO.GetGroups_paged(startIndex,numItems); } remote Group function getGroup(numeric ID) { return groupDAO.GetGroup(ID); } remote numeric function countGroup() { return groupDAO.Count(); } }
To complete the ColdFusion configuration, you should verify the destination named ColdFusion in your C:/ColdFusion9/wwwroot/WEB-INF/flex/remoting-config.xml file.
If it does not exist, add the destination shown in bold below. When you set <source>*</source> within a destination , ColdFusion Remoting enables all CFC methods with access="REMOTE" to be accessed by the Flex application.
 
remoting-config.xml
 
<?xml version="1.0" encoding="UTF-8"?> <service id="remoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage"> <adapters> <adapter-definition id="cf-object" class="coldfusion.flash.messaging.ColdFusionAdapter" default="true"/> <adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter"/> </adapters> <default-channels> <channel ref="my-cfamf"/> </default-channels> <destination id="ColdFusion"> <channels> <channel ref="my-cfamf"/> </channels> <properties> <source>*</source> </properties> </destination> </service>

 
The Flex Remote Class

With the UserFacade service implemented and managing access to the entities you can now turn to the development of the Flex application.
Entities that are on the server must be mirrored in ActionScript. This enables the reflection of the database tables in the application view, maintains data integrity, and makes client side validation possible.
You can generate the ActionScript Value Objects (VOs) using another ColdFusion Builder Extension:
  1. Right-click the user table in the RDS Dataview tab and select Generate AS Class from DB Table > Generate AS Class (see Figure 17).
  2. Verify the locations and click OK.
  3. Repeat the steps above for the group table.
 
Figure 17. Generating ActionScript VOs
Figure 17. Generating ActionScript VOs
 
Take a moment to review the generated ActionScript VO and verify that it reflects the ColdFusion VO. You can also change it to meet customer needs.
 
User.as
 
package com.pcsilva.adc.article1.mxml.entity { [Bindable] // pointing to an entity in ColdFusion [RemoteClass(alias='com.pcsilva.adc.article1.cfml.entity.User')] // declare entity [Entity] public class User extends Base { private var _id:Number = 0; // NaN is not going to ColdFusion public var name:String; public var lastname:String; public var email:String; // statement of relationship, pointing to an entity in ColdFusion [ManyToOne(targetEntity="com.pcsilva.adc.article1.cfml.entity.Group", fetchType='EAGER', cascadeType='All')] [JoinColumn(name='group_id',referencedColumnName='id')] // start property for use in getter groupname public var group:Group = new Group(); [Id] //statement identifier public function get id():Number { return _id; } public function set id(value:Number):void { if(_id != value) { selectedIndex++; } _id = value; } [Transient] // which is not reflected in ColdFusion // add property read-only – user.fullname public function get fullname():String { return name+' '+lastname; } [Transient] // user.groupname simplifies access the property user.group.name public function get groupname():String { return group.name; } } }

 
The Flex Remoting object

At this point, you need to create UserService.as, which is a simplified implementation of the Model Locator pattern containing properties that reflect the client's needs (see code below). It includes variables representing the current User, two ArrayCollections containing lists of Users and Groups, a general method for fault communication, and methods to enable transactions through the UserFacade. The other constants (const) are to standardize the text.
 
UserService.as
 
package com.pcsilva.adc.article1.mxml.services { import com.pcsilva.adc.article1.mxml.entity.Group; import com.pcsilva.adc.article1.mxml.entity.User; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.remoting.Operation; import mx.rpc.remoting.RemoteObject; public class UserService { private var _remoteObject:RemoteObject; /* it perfoms ownership lock transaction */ private var _inUse:Boolean = false; private static var _instance:UserService; /* constant configuration remoting */ public static const DESTINATION:String = "ColdFusion"; public static const SOURCE:String = "com.pcsilva.adc.article1.cfml.facade.UserFacade"; public static const SHOWBUSYCURSOR:Boolean = true; /* constants with names of methods for transactions */ public static const OPERATION_LIST_GROUP:String = 'getGroups'; public static const OPERATION_LIST_USER:String = 'getUsersByGroup'; public static const OPERATION_SAVE_USER:String = 'saveUser'; /* constant system, property names in VO */ public static const LABELFIELD_GROUP:String = 'name'; public static const LABELFIELD_USER:String = 'fullname'; /* constants with texts for the VIEW */ public static const TITLE:String = 'ColdFusion ORM Flex'; public static const LIST_GROUPS:String = 'Groups'; public static const LIST_USERS:String = 'Users'; public static const LABEL_USER:String = 'User'; public static const LABEL_USER_NAME:String = 'Name'; public static const LABEL_USER_LASTNAME:String = 'Last name'; public static const LABEL_USER_EMAIL:String = 'E-mail'; public static const LABEL_GROUP:String = 'Group'; public static const MESSAGE_ERROR_TITLE:String = 'Error'; public static const MESSAGE_ERROR_CONTENT:String = 'Invalid user!'; /* singleton data for VIEW */ [Bindable] public var user:User; [Bindable] public var group:Group; [Bindable] public var users:ArrayCollection; [Bindable] public var groups:ArrayCollection; /* system methods */ public function clean(instance:SingletonUserService=null):void { users = new ArrayCollection(); user = new User(); if(instance != null) { groups = new ArrayCollection(); } } protected function onFault(evt:FaultEvent):void { _inUse = false; Alert.show(evt.fault.message); } /* start constructor */ public function UserService(instance:SingletonUserService) { if(instance != null) { clean(instance); _remoteObject = new RemoteObject(); _remoteObject.destination = DESTINATION; _remoteObject.source = SOURCE; _remoteObject.showBusyCursor = SHOWBUSYCURSOR; _remoteObject.addEventListener(FaultEvent.FAULT, onFault); } } public static function getInstance():UserService { if(_instance == null) { _instance = new UserService(new SingletonUserService()); } return _instance; } /* end constructor */ /* start getGroups */ public function getGroups(result:Function=null):void{ clean(new SingletonUserService()); /* I capture operation in UserFacade, without dirtying the base configuration of remoting in Flex */ var service:Operation = Operation(_remoteObject.getOperation(OPERATION_LIST_GROUP)); service.addEventListener(ResultEvent.RESULT,onGetGroups); if(result != null) { service.addEventListener(ResultEvent.RESULT,result); } service.send(null); } protected function onGetGroups(evt:ResultEvent):void { [ArrayElementType('com.pcsilva.adc.article1.mxml.entity.Group')] var result:Array = evt.result as Array; groups.source = result; } /* end getGroups */ /* start getUsersByGroup */ public function getUsersByGroup(_group:Group,result:Function=null):void{ clean(); var service:Operation = Operation(_remoteObject.getOperation(OPERATION_LIST_USER)); service.addEventListener(ResultEvent.RESULT,onUsersByGroup); if(result != null) { service.addEventListener(ResultEvent.RESULT,result); } service.send(_group); } protected function onUsersByGroup(evt:ResultEvent):void { [ArrayElementType('com.pcsilva.adc.article1.mxml.entity.User')] var result:Array = evt.result as Array; users.source = result; } /* end getUsersByGroup */ /* start saveUser */ public function saveUser(result:Function=null,fault:Function=null):void{ /* it perfoms ownership lock transaction */ if(!_inUse){ _inUse = true; var _user:User = user; var service:Operation = Operation(_remoteObject.getOperation(OPERATION_SAVE_USER)); service.addEventListener(ResultEvent.RESULT,onSaveUser); if(result != null) { service.addEventListener(ResultEvent.RESULT,result); } if(fault != null) { service.addEventListener(FaultEvent.FAULT, fault); } service.send(_user); user = new User(); } } protected function onSaveUser(evt:ResultEvent):void { _inUse = false; } /* end saveUser */ } } class SingletonUserService {}
Because the singleton distributes the service properties through all application components, you must implement the validators in the View.
 

 
The Flex View

The article is not focused on a refined user interface, so the example Flex application is fairly simple (see Figure 18). Instead the focus has been on data access via ColdFusion ORM, requests made to UserService, and how to handle the data returned by UserFacade.
 
Figure 18. The Flex application
Figure 18. The Flex application
 
If you open the Master.mxml file, you can see how it handles the data and how transparent the data views are. The component's creationComplete event starts the data retrieval process.
 
Master.mxml
 
<?xml version="1.0" encoding="utf-8"?> <mx:Form xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" creationComplete="{userService.getGroups()}"> <fx:Script> <![CDATA[ import com.pcsilva.adc.article1.mxml.entity.Group; import com.pcsilva.adc.article1.mxml.entity.User; import com.pcsilva.adc.article1.mxml.services.UserService; import spark.events.IndexChangeEvent; [Bindable] private var userService:UserService = UserService.getInstance(); /* start user events */ // action is triggered when a group is selected private function groupsList_changeHandler(event:IndexChangeEvent):void { if(event.currentTarget.selectedItem is Group){ userService.getUsersByGroup(event.currentTarget.selectedItem); } } // ction is triggered when a user is selected private function usersList_changeHandler(event:IndexChangeEvent):void { userService.user = new User(); if(event.currentTarget.selectedItem is User) { Group(User(event.currentTarget.selectedItem).group).selectedIndex = groupsList.selectedIndex; userService.user = User(event.currentTarget.selectedItem); } } /* end user events */ ]]> </fx:Script> <mx:FormItem width="100%" label="{UserService.LIST_GROUPS}"> <s:ComboBox id="groupsList" change="{groupsList_changeHandler(event)}" labelField="{UserService.LABELFIELD_GROUP}" selectedIndex="{userService.group.selectedIndex >= 0?userService.group.selectedIndex:-1}" dataProvider="{userService.groups}" width="100%" /> </mx:FormItem> <mx:FormItem width="100%" label="{UserService.LIST_USERS}"> <s:List id="usersList" change="usersList_changeHandler(event)" labelField="{UserService.LABELFIELD_USER}" dataProvider="{userService.users}" width="100%" height="100%" /> </mx:FormItem> </mx:Form>

 
Where to go from here

The sample code for this article was written in a simple style so that it can be understood and modified easily. You can do a lot more with the code than what has been covered here. Feel free to use this code as a starting place for your own efforts.