Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: James Allen
Ben Nadel at CFCamp 2023 (Freising, Germany) with: James Allen ( @CFJamesAllen )

Learning ColdFusion 9: EntityNew() vs. The NEW Operator / CreateObject()

By on
Tags:

With ColdFusion 9's ORM (Object-Relational Mapping) functionality, there are now two different ways to create objects. Using the NEW operator (or CreateObject() or CFInvoke), we can create objects using their class paths. For ColdFusion components that are ORM-enabled (persistent), we can also use the EntityNew() method to create an object based on its unique "entity name." Other than this class path vs. entity name invocation difference, is there any difference between these two methods of object creation?

To get to the bottom of this, I put together a few demos that use objects created using the two different methods. Before we look at the usage, however, let's take a quick look at the ColdFusion components being feature in this demo. I am using simple Movie and Actor components in which a Movie can have zero or more Actor instances aggregated within it.

Actor.cfc

<!---
	When defining the component, the Persistent attribute will
	enable ORM control for this component. The Table attribute
	will define the database table name (which by default is the
	same as the CFC name).
--->
<cfcomponent
	output="false"
	hint="I represent an actor."
	persistent="true"
	table="actor">

	<!--- Define CFC properties. --->

	<cfproperty
		name="id"
		type="numeric"
		setter="false"
		hint="I am the unique ID of the actor."

		fieldtype="id"
		ormtype="integer"
		generator="identity"
		notnull="true"
		/>

	<cfproperty
		name="name"
		type="string"
		validate="string"
		validateparams="{ minlength=1, maxlength=50 }"
		hint="I am the name of the actor."

		fieldtype="column"
		ormtype="string"
		length="50"
		notnull="true"
		/>

</cfcomponent>

As you can see here, nothing special going on. Technically, in our domain, there is a many-to-many relationship between Movies and Actors, but for the sake of my demos, I only defined the aggregation in the Movie objects; I didn't need to reference movies from within the Actor objects.

Movie.cfc

<!---
	When defining the component, the Persistent attribute will
	enable ORM control for this component. The Table attribute
	will define the database table name (which by default is the
	same as the CFC name).
--->
<cfcomponent
	output="false"
	hint="I represent a movie."
	persistent="true"
	table="movie">

	<!--- Define CFC properties. --->

	<cfproperty
		name="id"
		type="numeric"
		setter="false"
		hint="I am the unique ID of the movie."

		fieldtype="id"
		ormtype="integer"
		generator="identity"
		notnull="true"
		/>

	<cfproperty
		name="name"
		type="string"
		validate="string"
		validateparams="{ minlength=1, maxlength=100 }"
		hint="I am the name of the movie."

		fieldtype="column"
		ormtype="string"
		length="100"
		notnull="true"
		/>

	<cfproperty
		name="rating"
		type="numeric"
		default="5"
		validate="integer"
		validateparams="{ min=1, max=10 }"
		hint="I am the rating of the movie between 1 and 10 (10 being the best)."

		fieldtype="column"
		ormtype="short"
		length="1"
		notnull="true"
		/>

	<cfproperty
		name="actors"
		type="array"
		hint="I am the collection of actors in this movie."

		fieldtype="many-to-many"
		cfc="Actor"
		linktable="actor_movie_jn"
		fkcolumn="movie_id"
		inversejoincolumn="actor_id"
		lazy="false"
		cascade="save-update"
		/>

</cfcomponent>

This Movie ColdFusion component is also straightforward; the only complex thing about it is that it contains a collection of Actor objects in the property, "Actors." Notice that the Cascade property of the Actors collection is set to "save-update;" this will allow my saves and updates to the Movie objects to cascade down into not-yet-persisted Actors contained within the given Movie.

Now that we understand the domain model with which we are experimenting, let's check to see if there are any functionally differences in the way we create our domain objects. In the first experiment, I'm simply going to create the objects and output them, looking for inconsistencies:

<!--- Load the movie object based on the component path. --->
<cfset movie = new com.Movie() />

<!--- Output the movie object. --->
<cfdump
	var="#movie#"
	label="Movie via NEW"
	/>

<br />

<!--- Output the movie as a query. --->
<cfdump
	var="#entityToQuery( movie )#"
	label="Movie Query via NEW"
	/>


<br />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<br />


<!---
	Load the movie object using entityNew(). This uses
	the entity name rather than the component path.
--->
<cfset movie2 = entityNew( "Movie" ) />

<!--- Output the movie object. --->
<cfdump
	var="#movie2#"
	label="Movie via entityNew()"
	/>

<br />

<!--- Output the movie object. --->
<cfdump
	var="#entityToQuery( movie2 )#"
	label="Movie Query via entityNew()"
	/>

As you can see here, one method uses the NEW operator to create a ColdFusion component based on its class path and one method uses the EntityNew() function to create a ColdFusion component based on its unique entity name. After each object is created, I am outputting it as well as converting it to a query, just to see if that functionality is available. And, when we run this code, we get the following output:

Learning ColdFusion 9: New vs. EntityNew() - Is There A Difference In How You Create Your ColdFusion Components?

As you can see, these objects appear to be exactly the same and can both be converted to a query.

Next, let's not just create objects but try to use them as well. In this demo, we will create both movies and actors and persist them:

<!--- Create an actor to add in the movie. --->
<cfset actor = new com.Actor() />

<!--- Set actor properties. --->
<cfset actor.setName( "Christina Cox" ) />


<!--- Load the movie object based on the component path. --->
<cfset movie = new com.Movie() />

<!--- Set movie properties. --->
<cfset movie.setName( "Better Than Chocolate" ) />
<cfset movie.setRating( 9 ) />
<cfset movie.addActors( actor ) />

<!--- Save the new movie object. --->
<cfset entitySave( movie ) />

<!--- Output the saved movie. --->
<cfdump
	var="#movie#"
	label="Movie via NEW"
	/>


<br />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<br />


<!--- Create an actor to add in the movie. --->
<cfset actor2 = new com.Actor() />

<!--- Set actor properties. --->
<cfset actor2.setName( "Leisha Hailey" ) />


<!---
	Load the movie object using entityNew(). This uses
	the entity name rather than the component path.
--->
<cfset movie2 = entityNew( "Movie" ) />

<!--- Set movie properties. --->
<cfset movie2.setName( "All Over Me" ) />
<cfset movie2.setRating( 7 ) />
<cfset movie2.addActors( actor2 ) />

<!--- Save the new movie object. --->
<cfset entitySave( movie2 ) />

<!--- Output the movie object. --->
<cfdump
	var="#movie2#"
	label="Movie via entityNew()"
	/>

Here, we are really testing the capabilities of the ColdFusion component properties. Are all the implicit setters created? Are the aggregated-property utility methods created (has<property>(), add<Property>, etc.)? Will both version of the created components persist properly? Does all the cascading logic apply? When we run this code, we get the following output:

Learning ColdFusion 9: New vs. EntityNew() - Is There A Difference In How You Create Your ColdFusion Components?

Again, both objects work the same way. It seems that these two types of object creation - class path and entity name - do not differ in terms of functionality. At least, not as far as we can see through experimentation. I actually asked this question during the "Advanced ORM" session at CFUNITED and got a slightly different answer. The speaker (forgive me for not remembering his name - I think he was the head Adobe engineer), told me that when you load an object using EntityNew(), ColdFusion actually presents it in a slightly different way than a standard CFC. While this difference is not noticeable to the programmer, a component created using EntityNew() does afford some efficiencies at the Hibernate integration level. As such, he recommended that all new ORM-enabled objects be created with EntityNew().

Want to use code from this post? Check out the license.

Reader Comments

54 Comments

Excellent stuff Ben, I'm enjoying all this new ORM stuff in CF9, I've not had the time or opportunity to crack it open and work with it myself just yet but it really looks excellent.

During your time playing with things have you tried persisting inherited objects? This is something I've always wanted to use in current ORM options such as Transfer but it's not currently supported, I wonder if/how his can be implemented in using the CF9 ORM methods as we use this kind of object model quite extensively here.

Rob

15,640 Comments

@Ray,

Good point. And yeah, it's waaaay nicer than having to type out the whole CreateObject() syntax.

@Robert,

I started playing around with inherited objects the other day. CF9 has like 4 different types of inherited situations depending on how tables need to be split of up and what kind of discriminators you use. I haven't fully wrapped my head around it because I don't generally have a lot of database tables that work that way. I'm still learning a lot about this.

38 Comments

I think I'm right in saying that EntityNew uses Hibernate directly to create the entity, so it has Java performance. Whereas using new has the performance impact of ColdFusion creating the object.

I like using the two methods so that I can see in my code if I'm working with a persisted entity of a plain CFC.

38 Comments

@Rob, I've played around with the hibernate inheritance and find that it works really well. The method I prefer is the one-table-per-entity and the discriminator column.

For example for a user superclass, with a manager subclass my code would be something like this:

/**
* @output false
* @hint I am a User Persisted Entity
* @discriminatorcolumn usertype
* @persistent true
*/
component
{
property name="UserID" ormtype="string" length="32" fieldtype="id" generator="native";
... stuff ....
}

/**
* @output false
* @hint I am a Manager Persisted Entity
* @extends User
* @discriminatorValue PARENT
* @persistent true
* @joinColumn UserID
*/
component
{
... stuff ....
}

3 Comments

One of the differences between CreateObject() and new Object() is that using new Object() will automatically execute the constructor (if it exists).

I haven't tested whether EntityNew() automatically executes the constructor. Have you tested this Ben?

54 Comments

@Ben,

That's great to hear they have a few different options available, seems they're giving pretty extensive access to the underlying hibernate stuff which really does turn me on.

@John,

Thanks, that's certainly quite a clean implementation, I've used some hasked versions of single table inheritance before with Transfer.

At the moment my current model generally has multiple table inheritance going on, for instance we deal with a great deal of multimedia messages, so we'll have a super class of 'message' and then subclasses, each with a different table for 'text', 'picture', 'audio', 'video' and so forth.

Thanks guys,

Rob

44 Comments

i haven't had the chance to play with cf9's orm stuff yet, but i'm wondering if there is anything out there about setting up and doing validations with the orm.

15,640 Comments

@Devin,

Ahhh, nice catch! Yeah, new Class() calls the constructor but EntityNew() does not! That's kind of lame, wish it would.

@Robert,

Yeah, I have a bunch of stuff like that. For example, I have a master table for "applications". And then, I have sub tables for "teacher application" and "professor application". I'll try to make some examples of this cause it is good to know.

25 Comments

Let me clear the confusion around EntityNew. Its nothing but a shorthand for createObject or New. Performance wise or functionality wise, its exactly the same. The only difference being - EntityNew works on the entityName. That makes its easier for you when you are creating the entity as you don't need to use the fully qualified name of cfc and it gels well with other entity functions.
Lets say one of the entity is "x.y.z.Actor" whose entityname is 'actor'. To create an actor, you would just say EntityNew('actor') no matter what the application mapped directory is, no matter which directory you are calling from, no matter what the fully qualified name is.

Hope that helps.

15,640 Comments

@Rupesh,

Ah, very nice. Thanks for the clarification. So, there is not Hibernate-level optimization going on with EntityNew()?

38 Comments

@Ben, @Rupesh In that case I apologise for posting incorrect information. I was under the impression that hibernate was being used. Sorry guys!

29 Comments

There are a couple of reasons that I like the EntityNew() function. As Rupesh describes above, it allows one to create an entity without having to know the full path to the cfc. This allows for better encapsulation. It is also consistent with EntityLoad() in that an object can be created solely by knowing the entity name. Why is that useful?

As an example, I might have a save() method in a service, which accepts an ID and a packet of data (e.g., the contents of a form post). I could define the entity name in the service itself (e.g., 'Movie'), and then, when the save method is called I could have code that conditionally chooses whether to retrieve an existing object, via EntityLoad() or create a new object, via EntityNew(), and then populate, validate and save the object.

In fact I can simply create one abstract service that does this, and then create dozens of services that extend that abstract service without having to write (almost) any code for them.

If I were going to use New() instead of EntityNew(), my service would have to know both the entity name and the actual path to the cfc, which seems silly.

15,640 Comments

@Bob,

I see exactly what you're saying. And after watching your presentation on OO domain models, a lot of what you're saying rings familiar. I remember in your presentation that you heavily leveraged extending services and what not. Very cool stuff.

354 Comments

Rupesh, I want to pick on one thing you said - that entityNew was nothing but a shorthand for createObject or new. That isn't entirely true. As Devin points out, only NEW actually runs your init method. createObject and entityNew do not. I'm not surprised by createObject not running init, but I'm kinda surprised entityNew does not.

15,640 Comments

@Ray,

I agree, it would be cool if EntityNew() would also call the constructor in the same way that the NEW directive does. That way, we could put initialization code in there rather than in the pseudo constructor.

13 Comments

Ben,

Thanks for the post. While I'm not certalnly in EntityNew(), do you know if the "new" directive supports Java classes as well? For example:

<cfset myFile = new java.io.File("myFile.txt")>

I haven't had the chance to play with our new CF9 server extensively yet so maybe the functionality exists. I just came across an instance in one of my CF pages where I'm using some Java classes like so:

<!--- Create the temporary File --->
<cfset oFile = createObject("java", "java.io.File").init("#expandPath(hash(createUUID()) & '.pdf')#")>
<cfset oFileOS = createObject("java", "java.io.FileOutputStream").init(oFile)>
<cfset oBufferedOS = createObject("java", "java.io.BufferedOutputStream").init(oFileOS)>


<!--- Read the BinaryArrayOutputStream from the cfhttp.fileContent --->

<cfset httpFile.fileContent.writeTo(oBufferedOS)>
<cfset oBufferedOS.flush()>
<cfset oBufferedOS.close()>

Generally, in my practice with Java, you would wrap those classes like so:

<cfset oBufferedOS = new java.io.BufferedOutputStream(new java.io.FileOutputStream(new java.io.File("myFile.txt")))>

I have no idea if the "new" directive works with Java though. It would be much nicer than implicitly calling CFCs using createObject().

13 Comments

Ben,

I guess I answered my own question. I got around to playing on our CF9 server and tried to create a File object:

<cfset file = new java.io.File("myFile.txt")>

With the result of:

Could not find the ColdFusion component or interface java.io.File.

However, this works with a CFC.

Darn. That would have been a helpful feature.

10 Comments

Does anyone have an example of the new functionality for entity new in cf 9.01.

Apparently you can pass a struct of properties to to the function. So you can now post a form and pass it into an ORM entity on creation, allowing you to populate it without having to call all of the setters in sequence.

I know you can do this in coldbox with the populate method, and its really helpful. Much better then having to type out entity.setName(FORM.name), entity.setAge(FORM.age) individually for all the properties.

I just havent been able to see an example of it at work got cf 9 .01

Cheers

13 Comments

Ahh, another favorite of CF9 is the implicit arguments. If I recall, passing an implicit struct or array as an argument would throw an exception in CF8 (at least I had issues with it) so I had to change some code back, but it seems to work perfectly fine in CF9.

10 Comments

Thanks for the response John W.

Yeah i had seen that, but wondered if you could pass a form struct to it?

user = EntityNew("User",form);

As you can do this with in coldbox..

user = entityNew("user")

user.populate(form);

Would make things a lot easier if that was possible in cf 9.0.1, but not seen any examples confirming it.

15,640 Comments

@John,

You can always create a base component that provided an Init() method that will loop over the argument collection and set properties. Then, you could have your components simply extend this component and let it do its thing; or, they could overwrite the init() method and call the super.init() method explicitly.

But for all things ORM, I'll defer to the others - I'm not up to snuff on that stuff :(

10 Comments

Hi ben..

Yes I have actually done what you have suggested using a base component created by bob silverberg - http://bit.ly/b57VbL.

Just thought it would be cool if cf9.0.1 had it as default.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel