Now that we can easily retrieve and display record sets using the Gateway Object, it's time to work with a specific record. Using a Bean to manage the properties of a single data record we can maintain strict control over a record as it is used throughout one or more applications.
One bean, one . . . instance of associated data
Is it getting better
Or do you feel the same
Will it make it easier on you now
You got [some bean] to blame
(with apologies to U2).
The name "bean" comes from the Java programming language (java > coffee > coffee bean, get it?). According to Wikipedia, a bean object is "used to encapsulate many objects into a single object (the bean), so that the bean can be passed around rather than the individual objects."
A bean in Coldfusion is created using a single Component (CFC) to encapsulate a single "record" of data. (As opposed to a Gateway Object, which returns record sets.)
In this example, the data in a bean will be read from and recorded to a single record in a single database table.
One bean != one database table record
In many applications, the "single record" of data stored in a bean could be compiled from reading multiple tables or other sources and later recorded to multiple destinations.
What makes a bean a bean?
From the Wikipedia article, the required conventions [of a bean] are:
- The class must have a no-argument constructor
- Its properties must be accessible using get, set and other methods (so called accessor methods) following a standard naming convention
- The class should be serializable (able to persistently save and restore its state)
- It should not contain any required event-handling methods
Let's take a look at this Category bean (CFC) and see where a Coldfusion bean deviates from a Java bean:
Bean convention #1: a no-argument constructor
As discussed in part 1 of this primer, init() is the constructor method of the bean. While the init() method has arguments, they are all set to required="false", which essentially makes this function a "no-argument constructor".
Bean convention #2: Standard access to its properties
The constructor method init() creates a struct variable named "instance" in the variables scope of the CFC. The variable "variables.instance" is local to the Component, and therefore is not directly accessible to any external request.
The keys of this struct are comprised of the bean's properties.
The standard convention for changing the value of a property is to use a setProperty() function. This type of Bean functions is commonly called a setter. Its more technical term is mutator.
The standard convention for reading a property is to use a getProperty() function. This type of Bean function is commonly called a getter. Its more technical term is accessor.
Alternate naming convetions for getters that return boolean values are names like isProperty() and hasProperty().
Bean convention #3: Serializable objects
From the Sun Java docs: "Object Serialization supports the encoding of objects and the objects reachable from them, into a stream of bytes."
Through ColdfusionMX 7.0.2, an instance of a Coldfusion Component (CFC) cannot be serialized, although it is possible to mimic this through programming. Beginning with ColdfusionMX 8 (Scorpio), a CFC can be serialized.
Bean convention #4: No required event-handling methods
Beans in Coldfusion applications don't handle events, they are data containers.
In Anatomy of an Object.cfc, we discovered what is in the variables scope of a CFC:
- functions - each function name is a key in the variables struct
- "this" - an instance of the component itself
- variables in the variables scope are accessible to all functions in the CFC
If you were to have a function "foo()"
and a bean property named "foo",
then variables.foo would first be defined as the function foo(). When you then call setFoo( x ), you've just redefined variables.foo as the value of x, eliminating the function foo().
By using variables.instance, you're ensuring that all the properties of a bean are insulated from the rest of the object's variables scope.
Two types of beans
The type of bean you implement depends on the needs of your application.
In the example Category.cfc above, all setter functions are specified as private, so no external process can call them directly. To change the values of the properties of this bean, you must send argument values through the init() method.
When you call init() in this manner, values are passed to the specified arguments and then the setter functions are called. Each setter takes a single argument which then updates its associated property of "variables.instance".
The getter functions will now return the updated property values.
Read / Write Bean
A read / write bean allows the setter methods to be public. When a bean is written like this, you can change the value of any bean property outside of the init() method and at any time.
How do we interact with a datasource?
We now have an object that represents a single data "record". While we can get a bean's data from any source, we need a way to manage how a bean talks to that source. Next we'll go over the most common way to abstract that interaction by creating a Data Access Object (DAO).