Learning ColdFusion 9: Refreshing And Rebuilding An ORM Application
Disclaimer: I am learning about ColdFusion 9's new ORM features as I go - I am not an authority on this topic by any means.
When you use the ORM features in ColdFusion 9, ColdFusion has to create a number of Hibernate configuration files and component mappings. These configurations are created when the application starts up (either running for the first time, having timed out, or being stopped manually with ApplicationStop()). From what I can gather, there are two aspects to this configuration process:
- Configuring the database.
- Configuring the ColdFusion component mappings.
The first part, configuring the database, depends on the dbCreate ORM settings defined in the Application.cfc:
<!--- Do not alter the database. --->
<cfset this.ormSettings.dbCreate = "none" />
This ORM settings property, dbCreate, can take one of three values:
None: The default, tells the ORM system not to alter the database in anyway.
Update: Tells the ORM system to create any tables that do not exist or to update any existing tables with new columns or properties.
DropCreate: Tells the ORM system to drop existing tables and recreate them.
The second part of the configuration process, setting up the ColdFusion component mappings, will happen when the application starts up, or when you call the ColdFusion 9 method, ORMReload(). ORMReload() can be called at any time to update the ORM mappings, but be careful because you might end up mapping a CFC property to a database column that does not yet exist:
As you saw in the video, when we added a property to the Task.cfc ColdFusion component and then called ORMReload(), ColdFusion remapped all the properties in the ORM system. The problem with this is that the newly mapped property does not yet have a corresponding column in the database; and so, when we go to save our newly mapped object, we get the ColdFusion error:
'ISCOMPLETE' is not a column in table or VTI 'APP.TASK'.
The ORM system is trying to persist the IsComplete property of the Task.cfc instance; but since ORMReload() didn't update the database, this action fails.
As it turns out, however, this last statement - that ORMReload() didn't update the database - is not entirely true. ORMReload() doesn't update the database most of the time because the ORM Setting, dbCreate, in the Application.cfc is set to or defaults to "none." When you call ORMReload(), it actually updates the ColdFusion component mappings AND carries out the ORM Setting's dbCreate command. Therefore, if we change the dbCreate property to "update" or "dropcreate", ORMReload() will, in fact, update the database structure.
(Note: I sound a bit confused in the video because I actually recorded that one first. At the point of writing this, my understanding of the re-mapping process was stronger than it was 30 minutes ago.) As you can see in the video, by using Refresh and Rebuild URL flags, I both update the ORM settings and call ORMReload(). This will update the ColdFusion component mappings and alter the database as necessary.
Application.cfc
<cfcomponent
output="false"
hint="I define the application settings and event handlers.">
<!--- Define the application. --->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
<!---
Store the datasource for this entire application. This
will be used with all the CFQuery tags as well as by
the ORM system.
--->
<cfset this.datasource = "cf9orm" />
<!---
This will turn on ORM capabilities for this application.
This tells ColdFusion to load all of the Hibernate code
and to communicate with the datasource above to prepare
the configuration files.
--->
<cfset this.ormEnabled = true />
<!---
This will define how the ORM system will work. Because
we are not starting out with any database, but using the
ORM system to build the database, we want to turn off
the use of the DB to get mapping data - we will be
defining all of the mapping information in our CFCs.
NOTE: "None" is the default for dbCreate. I have left it
in here only because I am overriding it afterwards.
--->
<cfset this.ormSettings = {
dbCreate = "none",
useDBForMapping = false
} />
<!---
Check to see if we need to rebuild the database. Normally,
these ORM settings only take effect when the application
is starting up; however, if we change them here AND then
call ORMReload() later on in the page, these settings seem
to take effect without stopping the appliation first. The
call to ORMReload(), however CANNOT be inside the
Application.cfc pseudo constructor.
--->
<cfif !isNull( url.rebuild )>
<!---
Signal to the ORM that we want to drop and then re-
create our database.
--->
<cfset this.ormSettings.dbCreate = "dropcreate" />
</cfif>
<!---
Check to see if we need to update the database (adding
columns and mappings that might not have previously
existed. Like the other ORM Settings, this only takes
effect when the application is restarted... OR, if
ORMReload() is called on the same page.
--->
<cfif !isNull( url.refresh )>
<!---
Signal to the ORM that we want to update existing
tables or add new ones.
--->
<cfset this.ormSettings.dbCreate = "update" />
</cfif>
<cffunction
name="onRequestStart"
access="public"
returntype="boolean"
output="false"
hint="I intialize the request.">
<!--- Define the request settings. --->
<cfsetting showdebugoutput="false" />
<!---
Check to see if the refresh of rebuild flag is
present. If it is, then we need to reload the ORM
mappings and configuration.
--->
<cfif (
!isNull( url.rebuild ) ||
!isNull( url.refresh )
)>
<!--- Reload the ORM configuration and mappings. --->
<cfset ORMReload() />
</cfif>
<!--- Return true so that the page can run. --->
<cfreturn true />
</cffunction>
</cfcomponent>
By default, ColdFusion will rebuild all of the ColdFusion component ORM mappings when the application starts up. If you want to rebuild these mappings at any point, you can either restart the application by calling ApplicationStop() (and then making a subsequent page request to start the application), or you can call ORMReload(). ORMReload() will update the ORM mappings and execute the dbCreate command in the ORM settings defined in the Application.cfc.
Want to use code from this post? Check out the license.
Reader Comments
I am on the fence as to whether or not I'm keen on the Hibernate integration that has been done. It's nice, but I do worry that it may become the ORM solution for CF, instead of just one of many options. And options are good. (My reticence may also stem from my preference for Transfer, I admit.)
A whole pile of stuff just got injected into the core language -- I guess it's just going to take me a while to get used to it.
@Rick,
My concern right now is that I feel it's distracted me from the fact that I don't fully understand how to use objects. For example, just because we have ORM it doesn't mean that my issues with data validation are any different. But now, I'm so distracted by learning a completely new methodology of SQL abstraction that I won't even worry for the moment that the validation throws errors.
It's like taking two steps to the side, later to realize, you still need to take a step forward at some point.
These posts about how to fire it ColdFusion ORM up will be valuable for many people. Took a little getting used to with "update" and "dropCreate". My Application.cfc looks a lot like yours.
I've been a Transfer fan boy and have been using it on numerous project for a while.
It took a little bit of getting used to but this release of CF is the Bees Knees. The Hibernate integration is da-bomb and I am very pleased with the implementation.
Only thing I wish adobe would add would be a "clearXXX()" method like Transfer has to remove all objects from a collection.
@John,
I'm glad you're really liking this release - that gives me hope that Hibernate will start to click a bit more in my head.
As far as the clearXXXX(). You should be able to call: setXXXX( [] ).
search the google then find your site.
that is a really excellent post.
Thanks for the post.
setXXX([]).... thanks for that.
@John,
No problem. If I find another way, I'll let you know.
@John,
Nice to hear that you like this release of CF9. It is packed with huge set of features - most of them requested by community.
ORM in itself is a huge feature (the doc running in more than 60 pages for this and still counting). Each ORM related attribute on cfproperty can be a feature in itself :-)
My series of posts for ORM are here
http://www.rupeshk.org/blog/index.php/category/coldfusion/orm-coldfusion/
One note, Update will populate new columns if they don't exist, but it won't change settings on those properties (columns). I altered a length parameter on one of my columns and this change did not persist to the DB.
If you change this.ormSettings.dbCreate = "dropCreate" it will change the table to reflect this change.
Great Blog, thank you.
I got this working - sorta -
I have an empty mySQL db and I use your ?rebuild
on a simple index.cfm the cf server takes about 4 to 5 seconds and then returns an error msg to the page that myDB.myTable doesn't exist and then gives link to the docs.
However, I check the db and the table was actually created. Next, I run the same index.cfm with ?refresh and no error msg.
I've deleted the tables and tried it a few times and on some occasions it returns the same error msg that myDB.myTable doesn't exist - but this time the server says the application.cfc has and error on the line with <cfset ORMRefresh() />
Any suggestions? (rebooting no help)
@Drew,
You are saying that it won't change the properties of an existing column?
@TomLong74,
Hmm, are you making any references to the ORM entities before you run the ORMReload() method? That's the only thing I can think of, especially if subsequent page requests don't cause any errors.