Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with:

How To Handle NULL Values In Object Oriented Programming In ColdFusion

By Ben Nadel on
Tags: ColdFusion

As you may or may not know, I am HUGELY AGAINST allowing NULL values in a database unless absolutely necessary (and yes, there are many valid cases where NULL is essential). Now, as I get more into object oriented programming (OOP) in ColdFusion, the idea of a NULL value becomes even more complicated. This has to do with how ColdFusion handles NULL values. Let's explore a little scenario in which we have a Property.cfc ColdFusion component that encapsulates one hidden variable. The Property.cfc has a Get() method and Set() method which get the private variable value and set it respectively.

This calling code on such an object, could looks like this:

  • <!--- Create the component. --->
  • <cfset objProperty = CreateObject( "component", "Property" ) />
  •  
  • <!---
  • Set the property to be a NULL value. We are explicitly
  • setting a NULL value here, but this could just as easily
  • have been a NULL value coming out of a query.
  • --->
  • <cfset objProperty.Set(
  • JavaCast( "null", 0 )
  • ) />
  •  
  • <!--- Get the value back out of the property. --->
  • <cfset strValue = objProperty.Get() />

Notice that I am explicitly setting a NULL value into the Set() method using JavaCast(). In my comments, I state that this might be value coming from the database. Now, in ColdFusion, NULL values come across as empty strings, but there are plenty of ways to get the actual NULL value from the ColdFusion query object. And, remember, this value doesn't have to come from a query - this could come from anywhere. The point is, we are modelling something that can be NULL.

Let's take a look at the Property.cfc ColdFusion component:

  • <cfcomponent>
  •  
  • <!--- Store private varaible. --->
  • <cfset VARIABLES.Value = "" />
  •  
  •  
  • <cffunction
  • name="Get"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Gets the property value.">
  •  
  • <cfreturn VARIABLES.Value />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="Set"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="Sets the property value.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Value"
  • type="any"
  • required="false"
  • />
  •  
  • <cfset VARIABLES.Value = ARGUMENTS.Value />
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

Pretty basic. Now, if we run our index code with this cfc, we get the following ColdFusion error:

Element VALUE is undefined in ARGUMENTS.

The problem here is that the JavaCast() null destroys the variable into which it was placed. Therefore, the Value key is no longer available in the ARGUMENTS struct when a NULL value gets passed through.

So what can we do about this? Well, we can put logic into the Set() method that checks for an existing Value key and then sets the private property depending on the existence:

  • <cfcomponent>
  •  
  • <!--- Store private varaible. --->
  • <cfset VARIABLES.Value = "" />
  •  
  •  
  • <cffunction
  • name="Get"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Gets the property value.">
  •  
  • <cfreturn VARIABLES.Value />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="Set"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="Sets the property value.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Value"
  • type="any"
  • required="false"
  • />
  •  
  •  
  • <!---
  • Check to see if the value exists. Since it can be
  • NULL, then the value might have been erased.
  • --->
  • <cfif NOT StructKeyExists( ARGUMENTS, "Value" )>
  •  
  • <!--- Store NULL into property. --->
  • <cfset VARIABLES.Value = JavaCast( "null", 0 ) />
  •  
  • <cfelse>
  •  
  • <!--- Store passed in value. --->
  • <cfset VARIABLES.Value = ARGUMENTS.Value />
  •  
  • </cfif>
  •  
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

A little bit wordy, but easy enough to handle. Now, if we re-run our index code, we get the following ColdFusion error:

Element VALUE is undefined in VARIABLES.

We fixed our Set() method, so the NULL handling issues moved right up to the Get() method. When we stored a NULL value into VARIABLES.Value, it destroyed the Value key. Then, when the internals of the Get() method try to access VARIABLES.Value, we get an error because that key doesn't exist.

So what we can about this? Same as before, we can put more logic in to handle the special NULL scenario:

  • <cfcomponent>
  •  
  • <!--- Store private varaible. --->
  • <cfset VARIABLES.Value = "" />
  •  
  •  
  • <cffunction
  • name="Get"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Gets the property value.">
  •  
  • <!---
  • Check to see if the value exists. Since it can be
  • NULL, then the value might have been erased.
  • --->
  • <cfif NOT StructKeyExists( VARIABLES, "Value" )>
  •  
  • <!--- Return a NULL value. --->
  • <cfreturn JavaCast( "null", 0 ) />
  •  
  • <cfelse>
  •  
  • <!--- Return stored value. --->
  • <cfreturn VARIABLES.Value />
  •  
  • </cfif>
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="Set"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="Sets the property value.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Value"
  • type="any"
  • required="false"
  • />
  •  
  •  
  • <!---
  • Check to see if the value exists. Since it can be
  • NULL, then the value might have been erased.
  • --->
  • <cfif NOT StructKeyExists( ARGUMENTS, "Value" )>
  •  
  • <!--- Store NULL into property. --->
  • <cfset VARIABLES.Value = JavaCast( "null", 0 ) />
  •  
  • <cfelse>
  •  
  • <!--- Store passed in value. --->
  • <cfset VARIABLES.Value = ARGUMENTS.Value />
  •  
  • </cfif>
  •  
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

Now, when we run our index code, we get no errors. Of course, our index code is very simple. If we start passing around NULL values, our calling code has to become ultra aware of the implications of the possible returned NULL value. Not a horrible thing, but not an easy thing either.

I don't know about you, but this seems like way too much work to handle null values. As such, I am sure that I will stick with treating null values as empty strings rather than true Java null value. The downside to this is that the Set() method will not know whether I am passing in a NULL value or an explicitly empty string.

But this brings us back to the whole "NULL values in the database" argument. I am saying that the Set() method won't know the difference between a NULL value and an explicitly empty string value. I am sure there are those that would say that this is OK because when you go to INSERT the value into the database, you will just replace empty strings with NULL. But here, again, we are seeing that the use of NULL (most of the time) seems very much like an arbitrary business rule that has absolutely nothing to do with "proper" database structure, which is again, why NULLs in database make no sense most of the time.... but that is whole other topic / war that I don't want to get into again.




Reader Comments

Hey Ben,

There's a typo in the Get method for your final example. The StructKeyExists(ARGUMNTS,"Value") should read StructKeyExists(VARIABLES,"Value").

I've had a long time struggle with the use of null in CF. I was thinking that it might be wise to create a NULL data type, but when I thought about it some more, I realized that null is not a data type. It's a value, or, more specifically, the absence of a value.

Reply to this Comment

@Paul,

Thanks for the typo catch. I have fixed in the demo code. However, the result is still the same, but by coincidence it was working :)

I don't mind having NULL as empty strings in the ColdFusion language. I am comfortable with it at this point. But, part of what this entails is the ambiguity of NULL and empty string. It is this ambiguity that forces us to put in arbitrary business logic as to what becomes translates to a NULL in the database.

I don't mind the business logic at all.... but let's call it just that - business practice, NOT "best practice" database design... but again, I am off topic.

Reply to this Comment

Some databases (Oracle in older versions, not sure about more recent ones) will treat an empty string as a null value whether you like it or not. You tell it to insert '', it inserts null :)

Reply to this Comment

This is actually a good argument for CF to NOT handle NULL built into the language. NULL is a very complicated concept even tho' it seems like the most obvious thing in the world. As you've seen, NULL sort of permiates everything once you let it in and it makes all of your code more complicated. The best thing to do is trap it at the boundary - the database - and not let it into your code. Transfer takes that approach, BTW.

Reply to this Comment

@Sean,

I certainly don't mind the way that ColdFusion treats query NULLs as empty strings. It helps to keep the code nice and simple. Trapping this stuff at the ColdFusion-Database interaction level suits me just fine.

Reply to this Comment

Not having null seems to be fair when you create ne data base (my DBA starts looking strange at me).
But what if you have some old data base and you just really have to handle it? Lets say - in mysql you may set date to 0000-00-00 but JDBC doesn't like it and will throw the error, select stamement will throw eeception. Of course you can treat zero date values as nulls by setting it on JDBC connection string but then when you select the data from table and your CFC expects date there CF will just crash as it expects date not empty string.
Of course you can handle that in your code but why write workarounds for something that has been already invented? I mean nulls, they are already there and they are really usefull in so many languages (Java, C#, AS3). One of base programming rules is: do not reinvent the wheel.
One of the reason why I started using CF was that indirect promise: write less code. But I don't think creating workarounds is writing less code.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.