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
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 in Map the properties 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:truefalseSee Lazy Loading for details. |
name |
Required |
|
Name of the collection. |
optimisticlock |
Optional |
true |
Specifies the locking strategy.truefalse |
orderBy |
Optional |
|
Specifies the Order By string. |
readonly |
Optional |
false |
truefalseIf 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:arraystruct |
Struct
Syntax
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 in Map the properties 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: truefalseSee Lazy Loading for details. |
name |
Required |
|
Name of the collection property. |
optimisticlock |
Optional |
true |
truefalse |
orderby |
Optional |
|
Specifies the Order By string. |
readonly |
Optional |
false |
Value are:truefalseIf 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: arraystruct |
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.
If the discriminator column and discriminator value is not specified, a default discriminator column name and value is picked up.
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>
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"> |
CreditCardPayment.cfc
<cfcomponent persistent="true" extends="Payment" table="Payment" discriminatorValue="CCard"> |
CheckPayment.cfc
<cfcomponent persistent="true" extends="Payment" table="Payment" discriminatorValue="check"> |
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.
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"> |
CreditCardpayment.cfc
<cfcomponent persistent="true" extends="Payment" table="CreditCardPayment" |
CheckPayment.cfc
<cfcomponent persistent="true" extends="Payment" table="CheckPayment" joinColumn="paymentId"> |
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"> |
CreditCardPayment.cfc
<cfcomponent persistent="true" extends="Payment" table="CreditCardPayment" joinColumn="paymentId" discriminatorValue="CCard"> |
CheckPayment.cfc
<cfcomponent persistent="true" extends="Payment" table="CheckPayment" joinColumn="paymentId" discriminatorValue="Check"> |
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.
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). |
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"> |
employee.cfc
<cfcomponent persistent="true"> |
employee.hbmxml
<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"> |
IMData.cfc
<cfcomponent embedded="true"> |
employee.hbmxml
<hibernate-mapping> |
Emp.cfm
<cfscript> |
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"> |
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.
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>