This entry will hopefully be brief and to the point in regard to the title. I want to start off by stating how much I like the ColdFusion extension support in Flex 2 and hope to see Adobe add extenstions that support other languages/middle tier solutions as well hint: (java, php, c#).
There is nothing like beginning your application architecture and development with an out of the box mvc approach utilizing well known approaches to server side development utilizing delegates, services, daos, gateways, and value objects.
Where I see a major problem with the extension is how it is implemented. I have been involved with several projects that have utilized Flex and ColdFusion and upon initially beginning my trek into Flex 2 I saw this issue right away only because I have been working with ColdFusion for 10 years, have consulted on its benefits, scaled servers, architected highly successful and efficient enterprise solutions with it, and know the do's and don'ts...
The problem I am speaking of with the CF extension implementation is the iterating over read functions in the data access object cfcs.
To be more clear, in the templated or skeleton dao cfcs there are db calls made, mostly crud calls. The read function I am discussing here is "r" in crud. The problem is that in many cases I've seen engineers design a composite or aggregate VO. This is a VO cfc that contains other VO cfcs.
This is a perfectly efficient and best practice OOP approach that ColdFusion handles very well. The problem arises when a child resultset of ids (usually a query or array) is used to iterate over and call the read function in the dao to get the vo properties from the db to return via remoting to the Flex application.
The main issue here is performance, specifically with the possible 100s of calls that can be made to the database in one ColdFusion request from the application. Imagine a dump truck going 200 times to the gravel pit taking one pebble, with today's gas prices I don't think you'd last long on the job if you took that approach 8-)... This is what you are doing when you loop over queries. Not only could these calls prove to be slow and taxing on the database but it can also lock up threads in ColdFusion and create cpu and memory spiking out of the shoot. You also have to deal with any kind of network latency that may exist between your CF server and the database as well. AAAACKKKKK!
But there is a solution: First a band-aid needs to be applied. For these complex objects an investigation must be made to locate where these iterating processes could be going on. Second, there is a function in ColdFusion that automagically converts a query field to a comma-delimited value list(or delimiter of your choosing). The function is called "valueList(query.column, ',')" there is also a quotedValueList for strings. With this list you could then use the SQL IN clause on the hopefully indexed id field used to pull back the child VO data. Add another function in the dao or better yet the data gateway that specifies readMultiple or getMulipleObjectName...
Once this is done the code should truly be refactored to call the database only once for all its data (think one cf call/one db call). If you did not know ColdFusion is one of the most robust languages when integrating with the more popular dbms on the market (Oracle, SQL Server, MySQL, DB2, Informix). The cfstoredproc tag allows you to call into a stored procedure to get procedure resultsets.
cfstoredproc also allows a developer to return multiple resultsets from a stored procedure to ColdFusion. So, one call to a stored procedure in Oracle that returns 2,3,4,or more ref cursors to ColdFusion. These could be all your objects' data which only requires you to loop once over all of them (maybe some nesting and brief logic to create them, but still 1000s of times faster than separate db calls for each loop).
This may be one of the drier entries I've had (no code, pictures, jokes). But I hope it helps you when using and not abusing the ColdFusion extension wizards in Flex 8-).
any advice on how to modify the wizard then, as far as the code it generates?
(when using increasing integers as PKeys...) another thing I'd love to change is to throw the DAO a get() with a 0 for it's PKey. At the moment it runs a query, and stuffs the(NULL) results into the bean before returning that. Trouble is that the PKey is returned as "", not 0 which is used to drive the insert/update Save() method on the DAO. Of course it throws an error.
Sure it's cheating using the DAO as a factory to create the empty bean in the first place, but up against Ruby-On-Rails, a bit of code generation and scaffolding goes a long way.
Maybe CF-Talk or Flexcoders knows.
You do mean the wizard generated code is a great starting place and feature in general, but requires customization and some insight correct?
@Barry
You lost me, shouldn't the db itself be generating the auto incremented field?It appears that you are stating the the wizard has an inherent flaw or bug. Which I haven't experience. Thanks
"You lost me, shouldn't the db itself be generating the auto incremented field"
yes, it does. but of course it only does that on a proper insert. I'm talking about the steps leading up to that, inc getting an empty bean to populate with values to then save.
What I'm getting at is throwing a known sentinal ID value to the GATEWAY.getById(), getting back an empty bean as a result, populating the bean with values then sending it to the GATEWAY.save(). The bean's sentinal value is what drives the GATEWAY.save() action (choosing an insert in this case instead of an update).
the problem with the wizard is that the underlying DAO always runs a query on the read() and populates the bean with the results of the query - including NULL values if the record doesn't exist - inc the ID field. so ... if I throw a sentinal/known value ("0" in this case for an autonumber) it wastes time running a query for a known non-existent record, then it doesn't stick that given value in the ID which means the GATEWAY.save() has a datatype mismatch when it needs to work out whether it's an insert or update.
<cffunction name="save" output="false" access="remote">
<cfargument name="obj" required="true" />
<cfscript>
if( obj.getevalID() eq 0 )
{
return createObject("component", "_evalDAO").create(arguments.obj);
} else {
return createObject("component", "_evalDAO").update(arguments.obj);
}
</cfscript>
</cffunction>
Yes, I'm using the GATEWAY as part of my service layer. Without too much effort I rarely need to explicitly create DAO's or beans - the GATEWAY does it all for me.
and sure, I'm using the GATEWAY.getByID(0) as a factory method. I could either hack the DAO's read() to bits to look for the sentinal value then don't run a query and return an empty bean with the sentinal value, or create a GATEWAY.getNew() method which would create an empty bean with the sentinal ID value.
Either way, I'm not getting want I want and saying that the wizard isn't production code just means it's a toy solution when I need real ones. It's a waste of my time if I have to hack what it generates to bits - and then make sure I haven't missed anything. What I'm after is not for everyone but that's why I'd rather hack the CodeGen to get just what I want. Because of the mods using this generated code is barely any faster than doing it by hand with snippets - and that's pretty pointless.
meanwhile RAILS takes Ruby further down the RAD track...
We can only hope Adobe is reading and addresses the possible performance impacts that have been discussed.
With regard to the wizard, it is a starting point and as I am still a proponent for database code residing in the database (non-RAD), I don't know of any middle-tier solution, hibernate, rails, etc. that stubs out properly written (brand centric) procedure code. i.e. Oracle packaged procedures/functions or SQL Server procedure/functions.
So at the very least it provides the place holders for developers to inject their database access code, cfquery/cfstoredproc and hopefully this blog will help them in understanding what not to do: "LOOP OVER QUERIES!"
Now wouldn't that be nice...
are we asking too much? is it a case of not being able to please all the people all the time?
is it the 80/20 rule biting?
which is why I'd to like to find out how to hack the wizard to get what I want.
I don't want to impose my workflow/nut-case ideas onto you (or anyone) but I am keen on getting something I (personally) can actually use, and gives me the productivity that I need for those boring CRUD uses, especially to compete against DHH's Active record idea for Ruby.
"We can only hope Adobe is reading and addresses the possible performance impacts that have been discussed."
aye.
1. You have to be living in New York City for now.
2. Work part time or full time with us.
3. Good at Adobe Flex technology.
4. Please contact us for other requirement and details.
Busycode Inc. is a top Adobe Flex shop who develops Flex/AIR applications for clients.
For more info, please visit http://www.busycode.com