Data-Centric Development with ColdFusion 9 and Flash Builder 4 - Part I
Because of its built in support for Flash Remoting ColdFusion has
always been an excellent choice for powering Flex applications, but
with the recent release of ColdFusion 9 and upcoming releases of Flash
Builder 4 and ColdFusion Builder, Adobe is looking to re-define how
developers create data-centric applications. In particular the powerful
introspection and code generation tools in Flash Builder 4 greatly
simplify the work flow for creating data driven applications. In this
tutorial we will walk through the creation of a simple contact manager
application demonstrating many of these new data-centric development
features in the process.
Prerequisites
If you would like to follow along with this tutorial you will need to have both ColdFusion 9 and Flash Builder 4 installed. ColdFusion 9, including a free developer edition, is available from Adobe at http://www.adobe.com/products/coldfusion. The latest Flash Builder 4 Beta is available on Adobe Labs, http://labs.adobe.com. For this tutorial ColdFusion was installed in the server configuration, with RDS enabled, using the built-in web server. Flash Builder 4 was installed using the stand-alone installer. For more information on the installation of either product, please see the documentation. Finally, while not required to complete the tutorial, you may also want to download the latest ColdFusion Builder Beta from Adobe labs for editing the ColdFusion files created in this tutorial.Database Setup
To begin we will create an Embedded Derby ColdFusion data source to hold our sample contact data.- Create a new ColdFusion template in the root of the ColdFusion
built in web server with the contents of Listing 1. (i.e.
/Applications/ColdFusion9/wwwroot/dzoneDBSetup.cfm)
Listing 1 - Database Setup Template (dzoneDBSetup.cfm)
<cfparam name="form.cfAdminPwd" default="" />
<cfparam name="form.dbName" default="" />
<cfparam name="form.dbFolder" default="" />
<cfif len(form.dbName) and len(form.dbFolder)>
<!--- use the administrator api to set up a new datasource --->
<cfset adminObj = createObject("component","cfide.adminapi.administrator").login(form.cfAdminPwd) />
<cfset dsObj = createObject("component","cfide.adminapi.datasource") />
<cfset dsObj.setDerbyEmbedded(name=form.dbName,
database=form.dbFolder,
description="Sample database for DZone ColdFuison 9 Flex integration tutorial.",
isnewdb="true") />
<!--- add the example table --->
<cfquery name="createTable" datasource="dzone">
CREATE TABLE CONTACT (
CONTACTID INT NOT NULL GENERATED ALWAYS AS IDENTITY,
FIRSTNAME VARCHAR(50),
LASTNAME VARCHAR(50),
EMAIL VARCHAR(255),
PRIMARY KEY(CONTACTID)
)
</cfquery>
<!--- add example data--->
<cfloop from="1" to="5000" index="i">
<cfquery name="insertContact" datasource="dzone">
INSERT INTO CONTACT (FIRSTNAME, LASTNAME, EMAIL)
VALUES ('FirstName#NumberFormat(i,'0000')#','LastName#NumberFormat(i,'0000')#','user#NumberFormat(i,'0000')#@test.com')
</cfquery>
</cfloop>
<!--- confirm example record count --->
<cfquery name="getContacts" datasource="dzone">
SELECT COUNT(*) AS CONTACTCOUNT FROM CONTACT
</cfquery>
</cfif>
<cfoutput>
<html>
<head>
<title>Database Setup</title>
</head>
<body>
<h1>Database Setup</h1>
<cfif len(form.dbName) and len(form.dbFolder)>
<p>Database created with #getContacts.contactCount# example records.</p>
<cfelse>
<form method="post"/>
<p>This template will create a new Derby ColdFusion data source named "dzone."
The template will also create a new table in the database and populate it with sample data.
Depending on your server setup, you may need to adjust the default </em>Database Folder</em> value below.</p>
<label for="cfAdminPwd">Administrator Password:</label>
<input type="password" name="cfAdminPwd" id="cfAdminPwd" /><br/>
<label for="dbFolder">Database Name:</label>
<input type="text" name="dbName" id="dbName" value="dzone" /><br/>
<label for="dbFolder">Database Folder:</label>
<input type="text" name="dbFolder" id="dbFolder" value="#ExpandPath('../db/dzone')#" size="50" /><br/>
<input type="submit" value="Create Data Source" />
</form>
</cfif>
</body>
</html>
</cfoutput> - Browse to the template. (i.e. http://localhost:8500/dzoneDBSetup.cfm)
- Enter the following information:
- Click Submit.
The template will create a new ColdFusion data source with sample data for use in this tutorial. This process may take several seconds. - You will see a confirmation message when the data source and sample data are created.
Project Setup
Next we will create the Flex project in Flash Builder.- In Flash Builder select File > New > Flex Project.
- Enter the following settings for the New Flex Project - Create a Flex project wizard:
- Click Next.
- Enter the following setting for the New Flex Project - Configure ColdFusion Server wizard:
- Click Validate Configuration.
- Click Finish.
The ColdFusion Service
Now that the basic Flex project is set up, the next step is to create the ColdFusion service component we will use to power our application. While there are many new options in Flash Builder 4 for importing existing services and creating new services, for this tutorial we will explore generating the service from a template.- Click the Data/Services tab in Flash Builder.
- Click the "Connect to Data/Service..." link.
- Select ColdFusion in the Connect to Data/Service - Select Service Type wizard.
- Click Next.
- On the Connect to Data/Service - Configure ColdFusion Service wizard, click the "click here to generate a sample" link.
- Enter the following settings for the Generate Sample CFC Service wizard:

- Press OK.
- Read the security information.
- Press OK.
- Confirm the service settings on the Connect to Data/Service - Configure ColdFusion Service wizard.
- Press Finish.
- Enter the ColdFusion RDS in the Authentication form:
- Press OK.
At this point Flash Builder creates a sample service CFC and introspects it to generate a ActionScript service class. The generated ActionScript service, ContactService.as, can be seen in the services.contactservice package while the ColdFusion CFC can be seen as a linked file under the services folder in the Package Explorer.
Additional details about the generated service can be seen in the Data/Services tab.
- Modify the ContactService.cfc template for use with the Derby ColdFusion data source.
Listing 2 shows the generated service CFC which contains sample code specific to the MySQL database engine.
Listing 2 - The generated ContactService.cfc with sample code.
<cfcomponent output="false">
<!---
README for sample service
This generated sample service contains functions that illustrate typical service operations.
Use these functions as a starting point for creating your own service implementation. Modify the
function signatures, references to the database, and implementation according to your needs.
Delete the functions that you do not use.
Save your changes and return to Flash Builder. In Flash Builder Data/Services View, refresh
the service. Then drag service operations onto user interface components in Design View. For
example, drag the getAllItems() operation onto a DataGrid.
This code is for prototyping only.
Authenticate the user prior to allowing them to call these methods. You can find more
information at http://www.adobe.com/go/cf9_usersecurity
--->
<cffunction name="getAllItems" output="false" access="remote" returntype="any" >
<!--- Auto-generated method
Retrieve set of records and return them as a query or array.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<cfset var qAllItems="">
<cfquery name="qAllItems" datasource="YOUR DATASOURCE NAME HERE">
SELECT * FROM TABLENAME
</cfquery>
<cfreturn qAllItems>
--->
</cffunction>
<cffunction name="getItem" output="false" access="remote" returntype="any" >
<cfargument name="itemID" required="true" />
<!--- TODO Auto-generated method
Retrieve a single record and return it as a query or array.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<cfset var qItem="">
<cfquery name="qItem" datasource="YOUR DATASOURCE NAME HERE">
SELECT *
FROM TABLENAME
WHERE ITEMID = <CFQUERYPARAM CFSQLTYPE="CF_SQL_INTEGER" VALUE="#ARGUMENTS.itemID#">
</cfquery>
<cfreturn qItem>
--->
</cffunction>
<cffunction name="createItem" output="false" access="remote" returntype="any" >
<cfargument name="item" required="true" />
<!--- TODO Auto-generated method
Insert a new record in the database.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<cfquery name="createItem" datasource="YOUR DATASOURCE NAME HERE" result="result">
INSERT INTO TABLENAME (FIELD1, FIELD2, FIELD3)
VALUES (<CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" VALUE="#item.FIELD1#">,
<CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" VALUE="#item.FIELD2#">,
<CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" VALUE="#item.FIELD3#">)
</cfquery>
<!--- The GENERATED_KEY is valid for mysql database only, you can modify it for your database --->
<cfreturn result.GENERATED_KEY/>
--->
</cffunction>
<cffunction name="updateItem" output="false" access="remote" returntype="void" >
<cfargument name="item" required="true" />
<!--- TODO Auto-generated method
Update an existing record in the database.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<cfquery name="updateItem" datasource="YOUR DATASOURCE NAME HERE">
UPDATE TABLENAME SET FIELD1 = <CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" VALUE="#item.FIELD1#">,
FIELD2 = <CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" VALUE="#item.FIELD2#">,
FIELD3 = <CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" VALUE="#item.FIELD3#">
WHERE ITEMID = <CFQUERYPARAM CFSQLTYPE="CF_SQL_INTEGER" VALUE="#item.itemID#">
</cfquery>
--->
</cffunction>
<cffunction name="deleteItem" output="false" access="remote" returntype="void" >
<cfargument name="itemID" type="numeric" required="true" />
<!--- TODO Auto-generated method
Delete a record in the database.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<cfquery name="delete" datasource="YOUR DATASOURCE NAME HERE">
DELETE FROM TABLENAME
WHERE ITEMID = <CFQUERYPARAM CFSQLTYPE="CF_SQL_INTEGER" VALUE="#ARGUMENTS.itemID#">
</cfquery>
--->
</cffunction>
<cffunction name="count" output="false" access="remote" returntype="numeric" >
<!--- TODO Auto-generated method
Return the number of items in your table.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<cfquery name="qread" datasource="YOUR DATASOURCE NAME HERE">
SELECT COUNT(ITEMID) AS itemCount FROM TABLENAME
</cfquery>
<cfreturn qread.itemCount>
--->
</cffunction>
<cffunction name="getItems_paged" output="false" access="remote" returntype="any" >
<cfargument name="startIndex" type="numeric" required="true" />
<cfargument name="numItems" type="numeric" required="true" />
<!--- TODO Auto-generated method
Return a page of numRows number of records as an array or query from the database for this startRow.
Add authorization or any logical checks for secure access to your data --->
<!--- Sample Code
<!--- The LIMIT keyword is valid for mysql database only, you can modify it for your database --->
<cfset var qRead="">
<cfquery name="qRead" datasource="YOUR DATASOURCE NAME HERE">
SELECT * FROM TABLENAME LIMIT #startIndex#, #numItems#
</cfquery>
<cfreturn qRead>
--->
</cffunction>
</cfcomponent>
Update this file with the contents from Listing 3, which has updated code specific to the Derby data base engine.
Listing 3 - Updated ContactService.cfc with Derby specific code.
<cfcomponent output="false">
<cffunction name="getAllContacts" output="false" access="remote" returntype="query" >
<cfset var qAllContacts = "" />
<cfquery name="qAllContacts" datasource="dzone">
SELECT
CONTACTID
,FIRSTNAME
,LASTNAME
,EMAIL
FROM
CONTACT
</cfquery>
<cfreturn qAllContacts />
</cffunction>
<cffunction name="getContact" output="false" access="remote" returntype="query" >
<cfargument name="contactID" type="numeric" required="true" />
<cfset var qContact = "" />
<cfquery name="qContact" datasource="dzone">
SELECT
CONTACTID
,FIRSTNAME
,LASTNAME
,EMAIL
FROM
CONTACT
WHERE
CONTACTID = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.contactID#" />
</cfquery>
<cfreturn qContact />
</cffunction>
<cffunction name="createContact" output="false" access="remote" returntype="numeric" >
<cfargument name="Contact" required="true" />
<cfset var qCreateContact = "" />
<cfset var qResult = "" />
<cfquery name="qCreateContact" datasource="dzone" result="qResult">
INSERT INTO CONTACT
(
FIRSTNAME
,LASTNAME
,EMAIL
)
VALUES
(
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.Contact.firstName#" />
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.Contact.lastName#" />
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.Contact.email#" />
)
</cfquery>
<cfreturn qResult.generatedKey />
</cffunction>
<cffunction name="updateContact" output="false" access="remote" returntype="void" >
<cfargument name="Contact" required="true" />
<cfset var qUpdateContact = "" />
<cfquery name="qUpdateContact" datasource="dzone">
UPDATE
CONTACT
SET
FIRSTNAME = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.Contact.firstName#" />
,LASTNAME = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.Contact.lastName#" />
,EMAIL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.Contact.email#" />
WHERE
CONTACTID = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.Contact.contactID#" />
</cfquery>
</cffunction>
<cffunction name="deleteContact" output="false" access="remote" returntype="void" >
<cfargument name="contactID" type="numeric" required="true" />
<cfset var qDelete = "" />
<cfquery name="qDelete" datasource="dzone">
DELETE FROM CONTACT
WHERE CONTACTID = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.contactID#" />
</cfquery>
</cffunction>
<cffunction name="count" output="false" access="remote" returntype="numeric" >
<cfset var qRead = "" />
<cfquery name="qRead" datasource="dzone">
SELECT COUNT(CONTACTID) AS CONTACTCOUNT
FROM CONTACT
</cfquery>
<cfreturn qRead.CONTACTCOUNT>
</cffunction>
<cffunction name="getContacts_paged" output="false" access="remote" returntype="query" >
<cfargument name="startIndex" type="numeric" required="true" />
<cfargument name="numItems" type="numeric" required="true" />
<cfset var endIndex = arguments.startIndex + arguments.numItems />
<cfset var qRead = "" />
<cfquery name="qRead" datasource="dzone">
SELECT
CONTACTID
,FIRSTNAME
,LASTNAME
,EMAIL
FROM
( SELECT
ROW_NUMBER() OVER() AS ROWNUM
,CONTACTID
,FIRSTNAME
,LASTNAME
,EMAIL
FROM
CONTACT
) AS TMP
WHERE
ROWNUM > <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.startIndex#" />
AND ROWNUM <= <cfqueryparam cfsqltype="cf_sql_integer" value="#endIndex#" />
</cfquery>
<cfreturn qRead>
</cffunction>
</cfcomponent>
- Save the updated ContactService.cfc.
- Refresh the ContactService.
In the Data/Services tab, right click on the ContactService and select Refresh.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)




