Ever wonder what happens at the database level when you use GORM's domain class inheritance? What's the difference between table-per-hierarchy and table-per-subclass inheritance? That's what this article is all about.

Grails' database mapping layer GORM provides two ways to achieve domain class inheritance with polymorphic GORM queries. Let me introduce you to the first method, the default in Grails and the right choice for most use cases. Let's give a round of applause to... table-per-hierarchy inheritance!

Table-per-hierarchy

Table-per-hierarchy inheritance means that the super-class class and all its sub-classes share the same database table. Take a look at these domain classes:

Employee.groovy
1
2
3
4
5
6
7
8
class Employee {

    String firstName
    String lastName

    static constraints = {
    }
}
Supervisor.groovy
1
2
3
4
5
6
class Supervisor extends Employee {

    static hasMany = [subordinates: Employee]
    static constraints = {
    }
}

An Employee contains firstName and lastName properties. A Supervisor is a subclass of Employee and only adds the ability to have subordinates; simply a collection of Employees. To get a good understanding of how this works in practice, lets take a peak at the database.

The database tables

GORM implements the two domain classes as one database table.

EMPLOYEE table

ID VERSION FIRST_NAME LAST_NAME CLASS
1 0 Dagny Taggart Supervisor
2 0 Eddie Willers Employee
3 0 James Taggart Supervisor

In addition to the EMPLOYEE table, GORM creates the SUPERVISOR_EMPLOYEE table for the one-to-many subordinates association. This table is not critical to inheritance, so I won't discuss it any further. For details on associations read this article.

The queries

The EMPLOYEE table has a CLASS column which distinguishes which domain class corresponds to the record. This makes it possible to list all employees...

SQL
1
2
SELECT ID, FIRST_NAME, LAST_NAME
FROM   EMPLOYEE
GORM
def employees = Employee.list()

... or to list only Supervisors...

SQL
1
2
3
SELECT ID, FIRST_NAME, LAST_NAME
FROM   EMPLOYEE
WHERE  CLASS = 'emmanuel.rosa.grailsinheritanceexample.Supervisor'
GORM
def supervisors = Supervisor.list()

So far, except for the subordinates association, the Supervisor domain class contains the same properties as the Employee domain class. Lets add an office property. Only supervisors have offices right?

Supervisor.groovy
1
2
3
4
5
class Supervisor extends Employee {
    String office

    ...
}

Now the EMPLOYEE table has an OFFICE column.

EMPLOYEE table

ID VERSION FIRST_NAME LAST_NAME CLASS OFFICE
1 0 Dagny Taggart Supervisor Operations Suite
2 0 Eddie Willers Employee null
3 0 James Taggart Supervisor Basement

What this means in practice is that it's now impossible to create instances of the Employee domain class because Employees don't have an office, yet the office field cannot be NULL. Don't worry, GORM offers a way out: table-per-subclass inheritance.

Table-per-subclass

Table-per-subclass means that a subclass is represented in the database as a table consisting of the difference between itself and its super-class. First, the default inheritance stragety must be changed.

Employee.groovy
1
2
3
4
5
6
7
class Employee {
    ...

    static mapping = {
        tablePerHierarchy false
    }
}

With the change described above applied, the OFFICE column is moved to a separate table.

EMPLOYEE table

ID VERSION FIRST_NAME LAST_NAME
1 0 Dagny Taggart
2 0 Eddie Willers
3 0 James Taggart

SUPERVISOR table

ID OFFICE
1 Operations Suite
3 Basement

Notice that Supervisors have a record in the SUPERVISOR table and in the EMPLOYEE table. In addition, the record IDs are shared between both tables. The relationship at the database level is a one-to-one. This means querying for Supervisors in SQL requires a table join.

SQL
1
2
3
SELECT EMPLOYEE.ID, FIRST_NAME, LAST_NAME
FROM   SUPERVISOR INNER JOIN EMPLOYEE 
       ON SUPERVISOR.ID = EMPLOYEE.ID

The end

Well, this wraps up this discussion on Grails polymorphic sub-classing. You just learned about the differences between GORM'S two methods of inheritance and what they look like at the database level. Want to play around with the code? Just clone the Mercurial repository as shown below:

$ hg clone ssh://hg@bitbucket.org/erosa/grails-inheritance-example