Advanced mapping

Collection Mapping

Collection mapping is similar to a one-to-many relationship mapping. However, in collection mapping, you have a collection of values instead of a collection of persistent target objects.

Consider the Artist-Art tables. If you want each Artist object to contain an array of artwork names instead of artwork objects, collection mapping should be used.

To define collection mapping in the CFC, use fieldtype="collection" in the cfproperty tag.

The collection can either be Array or Struct.

Array

Syntax

name="field_name" 
fieldtype="collection" 
type="array" 
table="table_name" 
fkcolumn="foreign_key_column_name" 
elementtype="ormtype" 
elementColumn="column_name from the link table that should be 
used for populating" 
orderby="order by string" 
lazy = "true|[false]" 
readonly="true|[false]" 
optimisticlock="[true]|false" 
batchsize="batch size">

Example

If each Artist object contains an array of artwork names instead of artwork objects, this mapping can be defined in Artist.cfc as:

<cfproperty name="artNames" fieldtype="collection" type="array" table="ART" fkcolumn="ARTISTID" elementcolumn="ARTNAME" elementtype="string">

Attribute

Attribute

Req/Opt

Default

Description

batchsize

Optional

 

An integer value that specifies the "batchsize" for fetching uninitialized collections. For details, see Lazy Loading.

elementColumn

Required

 

Specifies the column name that contains the data to be fetched for collection.

elementtype

Optional

String

Data type of the selected column. See ORM data types for details.

fieldtype

Required

 

Should be “collection”.

fkcolumn

Optional

 

The foreign key column in the specified table.

If you do not specify the foreign key column and useDBForMapping is true in ormsettings, ColdFusion automatically determines a foreign key column after inspecting the database.

lazy

Optional

true

Specifies if loading is to be done lazily:

true

false

See Lazy Loading for details.

name

Required

 

Name of the collection.

optimisticlock

Optional

true

Specifies the locking strategy.

true

false

orderBy

Optional

 

Specifies the Order By string.

readonly

Optional

false

true

false

If set to true, it indicates that the collection never changes and can be cached.

table

Required

 

Name of the table from where the values will be fetched.

type

Optional

array

Specifies if the collection type is:

array

struct

Struct

Syntax

<cfproperty 
name="field_name" 
fieldtype="collection" 
type="struct" 
table="table_name" 
fkcolumn="foreign_key_column_name" 
structkeycolumn="column in the target table to be used as key in the struct" 
structkeytype="ormtype of the key in the struct" 
elementtype="ormtype of the valye in the struct" 
elementColumn="column name from the table that should be used in 
value of struct" 
orderby="order by string" 
lazy = "[true]|false" 
readonly="true|[false]" 
optimisticlock="[true]|false" 
batchsize="batch size">

Attribute

Req/Opt

Default

Description

batchsize

Optional

 

An integer value that specifies the "batchsize" for lazily fetching instances of this collection.

elementcolumn

Required

 

Specifies the column name that contains the data to be fetched for collection.

elementtype

Required

 

Data type of the value. See ORM data types for details.

fieldtype

Required

 

Should be a collection.

fkcolumn

Optional

 

The foreign key column in the table.

If foreign key column is not specified and useDBForMapping is set to true in ORMSetting, ColdFusion automatically determines the Foreign Key column after inspecting the database.

lazy

Optional

true

Specifies if loading is to be done lazily:

true

false

See Lazy Loading for details.

name

Required

 

Name of the collection property.

optimisticlock

Optional

true

true

false

orderby

Optional

 

Specifies the Order By string.

readonly

Optional

false

Value are:

true

false

If you set it to true, the collection never changes and can be cached.

structkeycolumn

Required

 

Column name in the table that will be used as key of struct.

structkeyType

Required

 

Specifies the data type of the key, when type=struct.

For the entire list of data types, see the Data Type section.

table

Required

 

Name of the table from where the collection will be fetched.

type

Optional

array

Specifies if the collection type is:

array

struct

Inheritance mapping

If the object you need to persist has a hierarchy, the CFCs of that object hierarchy need to be mapped to the relational tables such that the entire hierarchy is persisted.

There are multiple strategies followed for inheritance mapping:

  • Table per hierarchy

  • Table per subclass without discriminator

  • Table per subclass with discriminator

Table per hierarchy

In this model, the object hierarchy is persisted in a single table. This table includes columns for all the properties of all the CFCs in the hierarchy. The concrete subclass represented by a row is identified based on the value of the discriminator column. In this strategy, all the CFCs of the hierarchy must have the same table name.

Note: If the discriminator column and discriminator value is not specified, a default discriminator column name and value is picked up.

Example

The following example demonstrates an implementation of table per hierarchy:

View full size graphic
Example for table per hierarchy

In the preceding figure, discriminatorColumn is PaymentType. Depending on the values of PaymentType whether it is credit card or check, the row is represented as a CreditCardpayment or checkPayment object respectively.

The following example illustrates how you can model the table per hierarchy:

Payment.cfc (parent class)

<cfcomponent persistent="true" table="Payment" discriminatorColumn="paymentType">  <cfproperty name="id"> 
  <cfproperty name="amount"> 
</cfcomponent>

CreditCardPayment.cfc

<cfcomponent persistent="true" extends="Payment" table="Payment" discriminatorValue="CCard">    
    <cfproperty name="cardNo"> 
    <cfproperty name="cardType"> 
</cfcomponent>

CheckPayment.cfc

<cfcomponent persistent="true" extends="Payment" table="Payment"  discriminatorValue="check">    
    <cfproperty name="checkNo"> 
    <cfproperty name="bankName"> 
    <cfproperty name="city"> 
</cfcomponent>

Table per subclass without discriminator

In this model, there are separate tables for each class in the hierarchy and these tables are joined by a primary key. When the object is persisted, properties of the parent component are stored in the parent table and the remaining properties are stored in the child table.

View full size graphic
Table per subclass without discriminator

In the preceding figure, the tables are joined by join column paymentId. You can model the tables as follows:

Payment.cfc

<cfcomponent persistent="true" table="Payment"> 
  <cfproperty name="paymentId"> 
  <cfproperty name="amount"> 
</cfcomponent>

CreditCardpayment.cfc

<cfcomponent persistent="true" extends="Payment" table="CreditCardPayment" 
              joinColumn="paymentId"> 
    <cfproperty name="cardNo"> 
  <cfproperty name="cardType"> 
</cfcomponent>

CheckPayment.cfc

<cfcomponent persistent="true" extends="Payment" table="CheckPayment" joinColumn="paymentId"> 
    <cfproperty name="checkNo"> 
  <cfproperty name="bankName"> 
  <cfproperty name="city"> 
</cfcomponent>

When an object of type CreditCardPayment is persisted, the property amount is stored in the payment table and the properties cardNo and cardType are stored in the CreditCardPayment table. The primary key of the CreditCardPayment remains the same as the primary key of the Payment table.

Table per subclass with discriminator

This model is similar to the table per subclass without discriminator strategy except that there is a discriminator column in the parent table. In addition, the child components has a disciminatorValue attribute in the cfcomponent tag.

The following example demonstrates the table per subclass with discriminator attribute:

Payment.cfc

<cfcomponent persistent="true" table="Payment" discriminatorColumn="paymentType">       
    <cfproperty name="paymentId"> 
    <cfproperty name="amount"> 
</cfcomponent>

CreditCardPayment.cfc

<cfcomponent persistent="true" extends="Payment" table="CreditCardPayment" joinColumn="paymentId" discriminatorValue="CCard"> 
    <cfproperty name="cardNo"> 
    <cfproperty name="cardType"> 
</cfcomponent>

CheckPayment.cfc

<cfcomponent persistent="true" extends="Payment" table="CheckPayment" joinColumn="paymentId" discriminatorValue="Check"> 
    <cfproperty name="checkNo"> 
    <cfproperty name="bankName"> 
    <cfproperty name="city"> 
</cfcomponent>

When an object of type CreditCardPayment is persisted, the property amount is stored in the payment table and the properties cardNo and cardType are stored in the CreditCardPayment table. The primary key of CreditCardPayment remains the same as the primary key of the Payment table. The value of PaymentType is the value of disciminatorColumn attribute of the respective object.

Embedded mapping

This mapping is used when a CFC has an embedded object which also needs to be persisted along with the parent's data. The CFC of the embedded object must have the attribute embedded set to "true" on the cfcomponent tag.

Important: The embedded object cannot be a persistent object. This feature is supported only when the hibernate mapping is explicitly defined in the hibernate mapping file (.hbmxml files).
Name.cfc embedded in Employee.cfc

The diagram shows two CFCs Employee and Name where EmployeeName field of the Employee.cfc is an object of Name.cfc. In the database, both these objects are persisted in the Employee table as a single row. Name object itself does not have its own identity. This mapping can be modeled as follows:

name.cfc

<cfcomponent embedded="true"> 
    <cfproperty name="FirstName"> 
    <cfproperty name="LastName"> 
    <cfproperty name=" Title"> 
</cfcomponent>

employee.cfc

<cfcomponent persistent="true"> 
    <cfproperty name="EmployeeID"> 
    <cfproperty name="EmployeeName"> 
    <cfproperty name="Designation"> 
</cfcomponent>

employee.hbmxml

<hibernate-mapping > 
    <class  name="cfc:Employee" table="Employees"> 
        <id name="EmployeeID" type="integer" column="EmployeeID"> 
            <generator class="native"/> 
        </id> 
        <component name="EmployeeName" class="cfc:Name"> 
        <property name="LastName" type="string" column="LastName"/> 
        <property name="FirstName" type="string" column="FirstName"/> 
        <property name="Title" type="string" column="Title"/> 
        </component> 
        <property name="Designation" type="string" column="Designation"/> 
    </class> 
</hibernate-mapping>

If the persistent CFC has a collection of embedded objects, then this mapping also has to be defined in the XML as shown in the following example. Here, employee object has a collection of IMData objects. Note that the IMData object is not persistent.

employee.cfc

<cfcomponent     persistent="true"> 
    <cfproperty name="EmployeeID"> 
    <cfproperty name="EmployeeName"> 
    <cfproperty name= "IMIDs" type="array"> 
    <cfproperty name="Designation"> 
</cfcomponent>

IMData.cfc

<cfcomponent embedded="true"> 
    <cfproperty name="type"> 
    <cfproperty name="ID"> 
</cfcomponent>

employee.hbmxml

<hibernate-mapping> 
    <class  name="cfc:Employee" table="Employees"> 
        <id name="EmployeeID" type="integer" column="EmployeeID"> 
                        <generator class="native"/> 
        </id> 
        <property name="EmployeeName" type="string" column="EmployeeName"/> 
            <bag name="IMIDs" table="IMData" lazy="true"> 
                <key column="EmployeeID" /> 
                <composite-element class="cfc:IMData"> 
                    <property name="type" type="string" column="Type"/> 
                    <property name="ID" type="string" column="ID"/> 
                </composite-element> 
            </bag> 
            <property name="Designation" type="string" column="Designation"/> 
    </class> 
</hibernate-mapping>

Emp.cfm

<cfscript> 
    employee = EntityNew("Employee"); 
    employee.setEmployeeName("Dan Watson"); 
    imdata1 = new IMData(); 
    imdata1.setType("IMClient1"); 
    imdata1.setID("msngrId1"); 
    imdata2 = new IMData(); 
    imdata2.setType("IMClient 2"); 
    imdata2.setID("msngrId2"); 
    employee.setIMIDs([imdata1, imdata2]); 
    EntitySave(employee); 
</cfscript>

For more details on component mapping in hibernate, see Component Mapping in Hibernate Reference Guide.

Join mapping in a CFC

Join mapping is used to map the properties of one CFC to several tables. In this case, tables are joined using a join column. For example, consider a case where the Employee and Address tables, are mapped to a single CFC Employees. Therefore, the employee.cfc has some fields, which are persisted in Employee table and some fields that are persisted in Address table. The attributes joincolumn and table should be specified for those fields that need to be persisted in Address table. In this case, table would be Address and joinColumn would be AddressID.

Note: Hibernate uses outer join by default for join fetching. For inner join, use HQL.

Following is a sample employee.cfc

Employee.cfc

<cfcomponent persistent="true"> 
      <cfproperty name="id"> 
    <cfproperty name="name"> 
    <cfproperty name="houseno" column="houseno" table="Address" joincolumn="addressId"> 
    <cfproperty name="street" table="Address" joincolumn="addressId"> 
    <cfproperty name="city" table="Address" joincolumn="addressId"> 
    <cfproperty name="country" table="Address" joincolumn="addressId"> 
</cfcomponent>

Define the ORM mapping in the Hibernate mapping file

ColdFusion can also use the standard Hibernate Mapping XML file to define the mapping between the object and the database. You can use both Java classes and CFCs in Hibernate mapping. For a complete description of hibernate mapping, see www.hibernate.org/hib_docs/reference/en/html/mapping.html.

Note the following points when using Hibernate mapping files.
  • The extension of the Hibernate configuration file is *.hbmxml.

  • The file is placed in the Application folder.

  • The class name must be specified as cfc:<fully qualified name of cfc>. If a package is specified in the hibernate mapping, then specify the class name as cfc:<name of cfc>.

  • The entityname attribute is optional. If you do not specify this attribute, it takes the component name, by default. For example, for the component artgallery.art, the value of the entityname attribute is “Art”, by default.

  • The entity name must be unique for an application. If there are two components with the same name (even if they are in different packages), specify different entity names for each of these components.

The following is an example of Hibernate mapping:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping> 
    <class lazy="true" name="cfc:artGallery.Art" schema="APP" table="Art"> 
    <id name="artid" type="int"> 
        <column length="10" name="ARTID"/> 
        <generator class="identity"/> 
    </id> 
        <property name="artname" type="string"> 
            <column length="50" name="ARTNAME"/> 
        </property> 
 
        <property name="price" type="java.math.BigDecimal"> 
            <column length="19" name="PRICE"/> 
        </property> 
 
        <property name="largeimage" type="string"> 
            <column length="30" name="LARGEIMAGE"/> 
        </property> 
 
        <property name="mediaid" type="int"> 
            <column length="10" name="MEDIAID"/> 
        </property> 
 
        <property name="issold" type="boolean"> 
            <column length="5" name="ISSOLD"/> 
        </property> 
        <many-to-one class="cfc:artGallery.Artists"  column="artistid" name="artist"/> 
    </class> 
</hibernate-mapping>