Lesson  in  ColdFusion 2025: Foundations

ORM Basics — ColdFusion Hibernate ORM

Use ColdFusion's built-in Hibernate ORM to map CFCs to database tables. Learn entity definition, CRUD operations, and basic HQL queries.

What is ColdFusion ORM (Object-Relational Mapping)?

ColdFusion ships with Hibernate as its built-in ORM layer. Mark a CFC as persistent="true" and ColdFusion automatically maps it to a database table, generates getter/setter methods, and provides CRUD functions — no SQL DDL required.

Why use ORM? Real advantages — and honest trade-offs

ORM adds a layer of abstraction. That layer has real benefits — but it is not free. Here is an honest breakdown:

What you genuinely gain:

AdvantageWhy it matters
Less boilerplate codeNo manual INSERT, UPDATE, or SELECT id, title, status FROM... for every table. entitySave(t) handles it.
Schema kept in sync with codeWith dbcreate="update", adding a property to your CFC adds the column to the table automatically — no ALTER TABLE scripts to manage.
Auto-generated getters/settersColdFusion generates getTitle(), setTitle(), etc. from property declarations — one less thing to write.
Object graph navigationWith relationships defined (one-to-many, many-to-one), you can write ticket.getAssignee().getEmail() instead of a JOIN query.
Database portabilitySwitch from H2 to MySQL or PostgreSQL by changing the datasource — the same entity code works unchanged.
First-level cacheLoading the same entity twice in one request hits the database only once — Hibernate returns the cached object automatically.

What you do NOT gain (common misconceptions):

MisconceptionReality
"ORM is faster than SQL"It is not. ORM-generated SQL is often less optimal than hand-written SQL. For complex reports and aggregates, cfquery is faster and easier to tune.
"ORM gives you more control"The opposite — ORM abstracts the SQL away. You have less direct control. Use cfquery when you need precise SQL.
"ORM makes data easier to find"For simple lookups by PK or a single field, yes. For complex searches across multiple tables, HQL is harder to write and debug than SQL.
"ORM eliminates the need to know SQL"No. You still need to understand SQL to debug ORM-generated queries, tune performance, and write HQL correctly.

The honest summary: ORM pays off when your application has a clear domain model — objects with state and behaviour (a Ticket that can be opened, assigned, resolved). It saves time on repetitive CRUD. It pays off less when your application is primarily report-driven — reading and aggregating data across many tables. Most real ColdFusion applications use both: ORM for domain objects, cfquery for reporting and complex queries.

What is Hibernate? (the engine behind ColdFusion ORM)
Hibernate ORM logo — the orange and white Hibernate wordmark with the tagline "Relational Persistence for Idiomatic Java"

Hibernate — the most widely used Java ORM framework, bundled inside ColdFusion.

Hibernate is an open-source Java ORM (Object-Relational Mapping) framework originally released in 2001 by Gavin King. It is now maintained by Red Hat / JBoss and is one of the most widely deployed Java frameworks in the world. ColdFusion has bundled Hibernate since ColdFusion 9 (2009).

What Hibernate does: Hibernate sits between your application code and the database. Instead of writing SQL manually, you define how your objects (CFCs in ColdFusion's case) map to database tables, and Hibernate generates and executes the SQL for you — selects, inserts, updates, deletes, joins, and even schema creation.

Key Hibernate concepts you will encounter in ColdFusion:

ConceptWhat it means
EntityA CFC with persistent="true" — maps to one database table
SessionHibernate's unit of work — CF manages this per-request automatically
HQLHibernate Query Language — SQL-like but uses entity/property names
Lazy loadingRelated entities are loaded from the DB only when accessed
First-level cacheHibernate caches loaded entities within a session — repeated entityLoadByPK calls for the same ID don't hit the DB twice
Dirty checkingHibernate tracks changes to loaded entities — calling entitySave() only issues an UPDATE if properties actually changed

Hibernate versions in ColdFusion:

CF VersionHibernate version
ColdFusion 9–10Hibernate 3.x
ColdFusion 11–2016Hibernate 4.x
ColdFusion 2018–2021Hibernate 5.x
ColdFusion 2023–2025Hibernate 6.x

Why does the version matter? HQL syntax, lazy loading behaviour, and some mapping annotations changed between major versions. If you find older ColdFusion ORM examples online that behave differently, the Hibernate version is usually why.

Hibernate vs JPA: Hibernate implements the JPA (Jakarta Persistence API) standard. JPA is the specification; Hibernate is the implementation. ColdFusion exposes Hibernate's native API (entityLoad, ORMExecuteQuery, etc.) rather than the raw JPA API, but under the hood it is all Hibernate.

Two-column mapping diagram — on the left a Ticket.cfc component box shows property declarations with persistent="true", property name="id" fieldtype="id", property name="title" ormtype="string", and property name="status" ormtype="string"; on the right a database table box shows the corresponding hd_tickets table with columns id (PK), title (VARCHAR), status (VARCHAR) — a bidirectional arrow labelled "Hibernate ORM" bridges the two sides

ColdFusion ORM maps persistent CFC properties directly to database columns via Hibernate — no SQL DDL required.

ORM vs cfquery — when to use each?
[object Object]

Use ORM for domain-model CRUD, cfquery for reporting and complex JOINs — they coexist naturally.

ScenarioRecommendation
Simple CRUD on one tableORM — less boilerplate
Complex multi-table JOINcfquery / queryExecute — more control
Reporting queriescfquery — easier to optimise
Domain model with relationshipsORM — handles lazy loading

Both can be mixed in the same application — use the right tool for each job.


Enable ORM in Application.cfc

ORM is disabled by default. Add this.ormenabled = true and an ormsettings struct to Application.cfc:

component {
  this.name       = "HelpdeskApp";
  this.datasource = "training_db";
  this.ormenabled = true;
  this.ormsettings = {
    datasource: "training_db",
    dbcreate:   "update",   // "none" | "create" | "update" | "dropcreate"
    logsql:     false
  };
}

dbcreate: "update" tells Hibernate to alter the schema to match your entities without dropping existing data. Use "none" in production once your schema is stable.

What do the dbcreate options mean?
ValueWhat Hibernate does
noneNever touches the schema — use in production
createDrops and recreates all tables on every app start
updateAdds missing columns/tables, never drops existing data
dropcreateDrops everything and recreates — wipes all data on restart

The safe rule for development: use update. It keeps your seed data intact while Hibernate adjusts the schema as you add properties. Switch to none before going to production.


Define an entity CFC

// Ticket.cfc
component persistent="true" table="hd_tickets" {
  property name="id"          fieldtype="id" generator="native";
  property name="title"       ormtype="string";
  property name="description" ormtype="string";
  property name="status"      ormtype="string"  default="open";
  property name="priority"    ormtype="string"  default="medium";
  property name="category"    ormtype="string";
}

Each property maps to a column. fieldtype="id" marks the primary key; generator="native" uses the database's auto-increment. ColdFusion generates getTitle(), setTitle(), etc. automatically.


CRUD operations

<cfscript>
  // CREATE
  t = new Ticket();
  t.setTitle("Keyboard not working");
  t.setStatus("open");
  t.setPriority("medium");
  t.setCategory("Hardware");
  entitySave(t);

  // READ — all open tickets
  tickets = entityLoad("Ticket", { status: "open" });

  // READ — single by primary key
  t = entityLoadByPK("Ticket", 1);

  // UPDATE
  t.setStatus("resolved");
  entitySave(t);

  // DELETE
  entityDelete(t);
</cfscript>

HQL queries

Hibernate Query Language is SQL-like but operates on entity names, not table names:

<cfscript>
  // Return all open tickets ordered by id
  openTickets = ORMExecuteQuery(
    "FROM Ticket WHERE status = :status ORDER BY id DESC",
    { status: "open" }
  );

  // Count high priority tickets (unique=true returns a scalar)
  total = ORMExecuteQuery(
    "SELECT COUNT(*) FROM Ticket WHERE priority = :p",
    { p: "high" },
    true
  );
</cfscript>
Hibernate Query Language (HQL) — deep dive

HQL is an object-oriented query language built into Hibernate. It looks like SQL but operates on persistent objects and their properties — never on raw table or column names. At runtime, Hibernate translates HQL into database-specific SQL automatically, so the same HQL works on H2, MySQL, PostgreSQL, and Oracle without changes.

What makes HQL different from SQL:

SQLHQL equivalentWhat changed
FROM student_tableFROM StudentEntity class name, not the table name
WHERE student_table.dept_id = 1WHERE s.department = :deptProperty name, not the column name
JOIN tickets t ON t.assignee = u.idJOIN t.assignee aNavigate object relationships — no ON clause needed

Supported clauses and features:

FeatureExample
Basic selectFROM Ticket
FilteringFROM Ticket WHERE status = :s
OrderingFROM Ticket ORDER BY createdAt DESC
AggregatesSELECT COUNT(*), AVG(t.id) FROM Ticket t
GROUP BY / HAVINGSELECT t.priority, COUNT(*) FROM Ticket t GROUP BY t.priority HAVING COUNT(*) > 2
Bulk UPDATEUPDATE Ticket SET status = 'closed' WHERE priority = 'low'
Bulk DELETEDELETE FROM Ticket WHERE status = 'resolved' AND priority = 'low'
Named parametersWHERE id = :id — safe, reusable, no SQL injection
Polymorphic queriesQuerying a parent entity returns instances of all mapped subclasses

HQL in ColdFusion vs raw Hibernate (Java):

In Java/Hibernate you call session.createQuery(hql, Entity.class) directly. In ColdFusion, the same thing is done with ORMExecuteQuery():

<cfscript>
  // Equivalent to: session.createQuery("FROM Ticket t WHERE t.id = :id").setParameter("id", 1).getResultList()
  results = ORMExecuteQuery("FROM Ticket t WHERE t.id = :id", { id: 1 });

  // unique=true → equivalent to getSingleResult() — use for COUNT, MAX, etc.
  total = ORMExecuteQuery("SELECT COUNT(*) FROM Ticket", {}, true);
</cfscript>

HQL vs JPQL: HQL is a superset of JPQL (Jakarta Persistence Query Language — the JPA standard). Any valid JPQL query is valid HQL, but HQL adds extra features (bulk DML, non-standard functions, Hibernate-specific extensions) that JPQL does not support. If you see JPQL examples online, they will work in ColdFusion ORM unchanged.

Key things to remember:

  • Always use named parameters (:paramName) — never concatenate values into an HQL string
  • Entity and property names are case-sensitiveFROM ticket will fail if the entity is named Ticket
  • HQL operates on the Hibernate session — changes made via bulk UPDATE/DELETE bypass dirty-checking and first-level cache; call ORMClearAllSession() after bulk operations if you continue loading entities in the same request

Activity 1 — Enable ORM in Application.cfc

Activity: In the Terminal tab, update Application.cfc to enable Hibernate ORM:

sudo tee /opt/coldfusion2025/cfusion/wwwroot/Application.cfc << 'EOF'
component {

  this.name       = "HelpdeskApp";
  this.datasource = "training_db";
  this.ormenabled = true;
  this.ormsettings = {
    datasource : "training_db",
    dbcreate   : "update",
    logsql     : false
  };

}
EOF

Verify ormenabled is present:

grep "ormenabled" /opt/coldfusion2025/cfusion/wwwroot/Application.cfc
Terminal showing the sudo tee command writing Application.cfc with ormenabled=true and ormsettings, followed by the grep confirming the ormenabled line is present

Application.cfc with ORM enabled — Hibernate will now manage entity mapping for this application.


Activity 2 — Create a persistent entity CFC

Activity: Create Ticket.cfc — a persistent entity mapped to the existing hd_tickets table:

sudo tee /opt/coldfusion2025/cfusion/wwwroot/Ticket.cfc << 'EOF'
component persistent="true" table="hd_tickets" {

  property name="id"          fieldtype="id"  generator="native";
  property name="title"       ormtype="string";
  property name="description" ormtype="string";
  property name="status"      ormtype="string" default="open";
  property name="priority"    ormtype="string" default="medium";
  property name="category"    ormtype="string";

}
EOF

Verify the file was created with persistent="true":

grep "persistent" /opt/coldfusion2025/cfusion/wwwroot/Ticket.cfc
Terminal showing the sudo tee command writing Ticket.cfc with persistent="true" and property declarations, followed by grep confirming persistent="true" is in the file

Ticket.cfc as a Hibernate entity — persistent="true" and table="hd_tickets" map it to the existing Help Desk table.


Activity 3 — Load entities and run an HQL query

Activity: Create orm_test.cfm — a page that uses entityLoad and ORMExecuteQuery to read tickets through Hibernate:

sudo tee /opt/coldfusion2025/cfusion/wwwroot/orm_test.cfm << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>ORM Test</title>
  <style>
    body  { font-family: sans-serif; max-width: 820px; margin: 2rem auto; }
    table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
    th    { background: #3b82d4; color: #fff; padding: .5rem .75rem; text-align: left; }
    td    { padding: .45rem .75rem; border-bottom: 1px solid #e5e7eb; }
    tr:hover td { background: #f7f8fa; }
    .result { padding: 1rem; background: #f0f4ff; border-left: 4px solid #3b82d4; margin: 1rem 0; }
  </style>
</head>
<body>
  <h1>ColdFusion Hibernate ORM — Test</h1>

  <cfscript>
    // entityLoad — load all open tickets via ORM
    openTickets = entityLoad("Ticket", { status: "open" }, "title asc");

    // ORMExecuteQuery — HQL count of high priority tickets
    highCount = ORMExecuteQuery(
      "SELECT COUNT(*) FROM Ticket WHERE priority = :p",
      { p: "high" },
      true
    );
  </cfscript>

  <div class="result">
    <strong>entityLoad:</strong> <cfoutput>#arrayLen(openTickets)#</cfoutput> open ticket(s) loaded via ORM<br>
    <strong>HQL COUNT:</strong> <cfoutput>#highCount#</cfoutput> high priority ticket(s)
  </div>

  <h2>Open Tickets (via entityLoad)</h2>
  <table>
    <tr><th>ID</th><th>Title</th><th>Priority</th><th>Category</th></tr>
    <cfoutput>
      <cfloop array="#openTickets#" index="t">
        <tr>
          <td>#t.getId()#</td>
          <td>#encodeForHTML(t.getTitle())#</td>
          <td>#encodeForHTML(t.getPriority())#</td>
          <td>#encodeForHTML(t.getCategory())#</td>
        </tr>
      </cfloop>
    </cfoutput>
  </table>

</body>
</html>
EOF

Open /orm_test.cfm in the ColdFusion 2025 browser tab. You should see a count of open tickets loaded by Hibernate and the high priority count from HQL.

curl -s http://localhost:8500/orm_test.cfm | grep -i "entityload"
Browser showing orm_test.cfm with a blue result box showing the entityLoad count and HQL COUNT result, and a table below listing open tickets with their ID, title, priority, and category loaded via Hibernate ORM

orm_test.cfm — tickets loaded via entityLoad and counted via HQL ORMExecuteQuery.


When all the checks above are green, this lesson is complete. Your progress is saved automatically — move straight on to the next lesson.