Hibernate : Inheritance Mapping

Object oriented systems can model both “is a” and “has a” relationship. Relational model supports only “has a” relationship between two entities. Hibernate can help you map such Objects with relational tables. Hibernate supports the three basic inheritance mapping strategies:

  • table per class hierarchy
  • table per concrete class
  • table per subclass

Lets take the first case – table per class hierarchy, which is actually the default case in hibernate if we save objects of a hierarchy.In table per hierarchy mapping, single table is required to map the whole hierarchy. Lets take ComputerDetails, Desktop and Laptop our entities, where Desktop and Laptop extend ComputerDetails.


package com.hibernate.inheritance.singleTableStatergy;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class ComputerDetails {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String operatingSystem;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getOperatingSystem() {
return operatingSystem;
}
public void setOperatingSystem(String operatingSystem) {
this.operatingSystem = operatingSystem;
}

}

package com.hibernate.inheritance.singleTableStatergy;

import javax.persistence.Entity;

@Entity
public class Desktop extends ComputerDetails{

private String keyBoardDetails;

public String getKeyBoardDetails() {
return keyBoardDetails;
}

public void setKeyBoardDetails(String keyBoardDetails) {
this.keyBoardDetails = keyBoardDetails;
}
}

package com.hibernate.inheritance.singleTableStatergy;

import javax.persistence.Entity;

@Entity
public class Laptop extends ComputerDetails {

private String chargerId;

public String getChargerId() {
return chargerId;
}

public void setChargerId(String chargerId) {
this.chargerId = chargerId;
}

}

Make these entities mapping in the hibernate-cfg.xml. HibernateInheritanceSingleTableTest.java:


package com.hibernate.inheritance.singleTableStatergy;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateInheritanceSingleTableTest {
public static void main(String[] args) {

ComputerDetails aComputerDetails = new ComputerDetails();
aComputerDetails.setOperatingSystem("Windows 8.1");

Desktop aDesktop=new Desktop();
aDesktop.setOperatingSystem("Windows 7");
aDesktop.setKeyBoardDetails("From iBall");

Laptop aLaptop=new Laptop();
aLaptop.setOperatingSystem("Ubuntu 14.04");
aLaptop.setChargerId("QWERT123");


SessionFactory sessionFactory = new Configuration().configure("/com/hibernate/inheritance/singleTableStatergy/hibernate.cfg.xml").buildSessionFactory();
Session    session = sessionFactory.openSession();
session.beginTransaction();
session.save(aComputerDetails);
session.save(aDesktop);
session.save(aLaptop);
session.getTransaction().commit();
session.close();

}

}

Output:


Hibernate: insert into ComputerDetails (operatingSystem, DTYPE) values (?, 'ComputerDetails')
Hibernate: insert into ComputerDetails (operatingSystem, keyBoardDetails, DTYPE) values (?, ?, 'Desktop')
Hibernate: insert into ComputerDetails (operatingSystem, chargerId, DTYPE) values (?, ?, 'Laptop')

Database table:

HibernateInheritanceSingleTableTest

As you can see, Hibernate created only one table for these objects. It has a created an extra column DTYPE (known as discriminator column) is added to identify the class, because now 3 different objects are in the same table, so we got to have a way to differentiate among them.  Disadvantage of this type of storage is that changes to members of the hierarchy require column to be altered, added or removed from the table. You can change the name of this extra column DTYPE like following:


@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="Computer_Type",
discriminatorType=DiscriminatorType.STRING)
public class ComputerDetails {

//rest of the class as above

Now the extra column created will be named ‘Computer_Type’. Notice the @Inheritance stratergy and the discriminatorType, these are the defaults taken by hibernate even if not declared. If we want to change the default discriminator value, which is the class name, we can change by using @DiscriminatorValue.


@Entity
@DiscriminatorValue(value="Desktop computer")
public class Desktop extends ComputerDetails{

Lets take the second case – table per concrete class. This is the easiest method of Inheritance mapping to implement. Tables are created as per class, but duplicate column is added in subclass tables. All we have to do to implement this strategy is to use InheritanceType.TABLE_PER_CLASS). Also note that the strategy use for GeneratedValue will be Table. Auto wont work.


@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class ComputerDetails {

@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private int id;

Output after running the test class:


Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'ComputerDetails' for update
Hibernate: insert into hibernate_sequences(sequence_name, sequence_next_hi_value) values('ComputerDetails', ?)
Hibernate: update hibernate_sequences set sequence_next_hi_value = ? where sequence_next_hi_value = ? and sequence_name = 'ComputerDetails'
Hibernate: insert into ComputerDetails (operatingSystem, id) values (?, ?)
Hibernate: insert into Desktop (operatingSystem, keyBoardDetails, id) values (?, ?, ?)
Hibernate: insert into Laptop (operatingSystem, chargerId, id) values (?, ?, ?)

DB tables:

HibernateInheritanceTablePerConceteClassStatergyTest

As you can see, data that’s belongs to a parent class is scattered across a number of subclass tables, which represents concrete classes. Changes to a parent class is reflected to large number of tables. Hence this hierarchy is not recommended for most cases.

Table per subclass- In this strategy, tables are created as per class but related by foreign key. So there are no duplicate columns. Changing the InheritanceType to ‘JOINED’


@Inheritance(strategy=InheritanceType.JOINED)
public class ComputerDetails {

Output of the test class:


Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'ComputerDetails' for update
Hibernate: insert into hibernate_sequences(sequence_name, sequence_next_hi_value) values('ComputerDetails', ?)
Hibernate: update hibernate_sequences set sequence_next_hi_value = ? where sequence_next_hi_value = ? and sequence_name = 'ComputerDetails'
Hibernate: insert into ComputerDetails (operatingSystem, id) values (?, ?)
Hibernate: insert into ComputerDetails (operatingSystem, id) values (?, ?)
Hibernate: insert into Desktop (keyBoardDetails, id) values (?, ?)
Hibernate: insert into ComputerDetails (operatingSystem, id) values (?, ?)
Hibernate: insert into Laptop (chargerId, id) values (?, ?)

DB tables:

HibernateInheritanceTablePerSubclassStatergyTest

As you can see, tables are created different per class, but it is more normalized now. Also, in order to retrieve the full data, we need to use join the tables, hence the ‘JOINED’ strategy.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: