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 inheritance means that the super-class class and all its sub-classes share the same database table. Take a look at these domain classes:
1 2 3 4 5 6 7 8
1 2 3 4 5 6
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.
The class column in the table actually contains the full class name; package name plus class name. I excluded the package name from the above listing.
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.
EMPLOYEE table has a
CLASS column which distinguishes which domain class corresponds to the record. This makes it possible to list all employees...
... or to list only
1 2 3
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?
1 2 3 4 5
EMPLOYEE table has an
By default domain class properties are not null-able (NOT NULL in SQL speak). I'm using an H2 database for this demonstration and apparently the office field is not being set to NOT NULL. If I were to use my favorite DBMS PostgreSQL, it would reject the Eddie Willers record. So I'm going to pretend that NOT NULL is working properly and that the
office field should be as-is; not null-able.
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 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.
1 2 3 4 5 6 7
With the change described above applied, the
OFFICE column is moved to a separate table.
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.
1 2 3
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: