One of the biggest challenges in mapping objects to relations is inheritance. Relational databases do not support this concept. So, ORM solutions need to get creative when dealing with this issue. JPA specifies several strategies, all of which are implemented by Hibernate and these will be discussed here.
The default strategy to support class hierarchy, in the case of inheritance, is single table strategy. If you don't specify any strategy, Hibernate will look for (or create) a single table with the name of the parent class. This table has columns for every attribute in all the classes in the inheritance model. Let's consider the following superclass and its subclasses:
@Entity public class Person { @Id @GeneratedValue private long id; private String firstname; private String lastname; } @Entity public class Driver extends Person { @Column(name="LIC_NUM") private String licenseNumber; } @Entity public class Passenger extends Person { @Column(name="DEST") private String destination; }
Note that all the classes are annotated as entity and the children inherit the id
property from the parent. If you enable Hibernate schema generation and save a Person
entity, a Driver
entity, and a Passenger
entity, your table will look like the following:
The first thing to note is a column called DTYPE
. This is a column that's created by Hibernate and is used to distinguish between the different rows. The value in this column tells Hibernate which class it maps to.
Also, note that the Person
row doesn't have a value in the DEST
and LIC_NUM
column. That's because the Person
class doesn't have an attribute with these names. On the other hand, the Driver
entity has a license number, and Passenger
has a destination.
You can apply further customization. For example, using @DiscriminatorColumn
on the parent class, you can set the name of the Discriminator
column:
@Entity @DiscriminatorColumn(name="PERSON_TYPE") public class Person { @Id @GeneratedValue private long id; private String firstname; private String lastname; }
You can also instruct Hibernate to use a different value (and different type) for the discriminator column:
@Entity @DiscriminatorColumn(name="PERSON_TYPE", discriminatorType=DiscriminatorType.INTEGER) @DiscriminatorValue(value="100") public class Person { … } @Entity @DiscriminatorValue(value="200") public class Driver extends Person { … } @Entity @DiscriminatorValue(value="300") public class Passenger extends Person { … }
The single table strategy is not ideal, but it's simple and may be perfect for simple situations. However, this strategy might lead to a highly denormalized schema and leave a lot of holes in the table. Furthermore, in real life scenarios, a class will have many attributes. Therefore, if you are mapping two or more classes into one table, you will have to deal with database clutter.
In the next section, we'll discuss other strategies that might be a better fit for different scenarios.
As the name suggests, this strategy maps a table per entity class. In this case, the table itself is the discriminator, so all you need to do is set your inheritance strategy to the right value:
@Entity @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public class Person { … } @Entity public class Driver extends Person { … } @Entity public class Passenger extends Person { … }
In this case, a Person
class will be saved in the PERSON
table, Driver
in the DRIVER
table, and Passenger
in the PASSENGER
table. This is a better strategy because you don't have the additional discriminator column and you are not leaving any holes in the table. However, you are still repeating the shared attributes of the parent class in all tables. So, each table will have all the columns that correspond to the attributes of the parent class. If this is not the desired structure either, you should consider the next strategy.
If you use a joined strategy to support inheritance mapping, Hibernate stores the common attribute in one table and the attributes of the child class in a separate table with a foreign key reference to the parent table ID. Every child class gets a table. When the entity is read from database, Hibernate performs a left outer join operation on every table in the hierarchy to fetch the data. The SQL will look something similar to this (using the PostgreSQL dialect):
select person.id as id, person.firstname as firstname, person.lastname as lastname, passenger.DEST as DEST, driver.LIC_NUM as LIC_NUM, case when passenger.id is not null then 1 when driver.id is not null then 2 when person.id is not null then 0 end as clazz_0_ from Person person left outer join Passenger passenger on person.id=passenger.id left outer join Driver driver on person.id=driver.id where person.id=?
As you can see, in some cases the left outer joins may not be necessary, but Hibernate can't make this distinction and has to include every table mapping a subclass in the hierarchy.
3.16.75.165