Inheritance

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.

Single table strategy

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:

Single table strategy

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 {
…
}

Tip

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.

Table per class strategy

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.

Joined 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165