Java is an object-oriented programming language, and while working with the object-oriented paradigm, one thing comes to our mind: inheritance. We form a real-world scenario using IS A
and HAS A
relationships. Inheritance is supported by many languages, but relational databases are unable to understand the relationship of inheritance. Hibernate provides a way to map real-time relationships to the database.
Hibernate provides multiple strategies to achieve such a relationship for relational databases. There are three inheritance mapping strategies defined in hibernate:
In this recipe, we will take a look at table per class hierarchy.
Here, we will create a new data structure that will help you understand the inheritance strategy.
Consider a class, Employee
. We will extend the Employee
class into two subclasses—PermanentEmployee
and ContractualEmployee
. The following figure represents the relationship:
Update the following code in their respective files:
@Entity @Table(name="employee") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn( name="emp_type" , discriminatorType=DiscriminatorType.STRING , length=2 ) @DiscriminatorValue(value="E") public class Employee { @Id @GeneratedValue @Column(name="id") private long id; @Column(name="name") private String name; //getters and setters }
Source file: PermanentEmployee.java
@Entity @Table(name = "employee") @DiscriminatorValue(value="PE") public class PermanentEmployee extends Employee { @Column(name="salary") private Double salary; //getters and setters }
Source file: ContractualEmployee.java
@Entity @Table(name = "employee") @DiscriminatorValue(value="CE") public class ContractualEmployee extends Employee { @Column(name="hourly_rate") private Double HourlyRate; @Column(name="contract_period") private Float ContractPeriod; //getters and setters }
Use the following table script if the hibernate.hbm2ddl.auto
configuration property is not set to create
:
Use the following script to create the employee
table:
CREATE TABLE `employee` ( `emp_type` VARCHAR(2) NOT NULL, `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) DEFAULT NULL, `contract_period` FLOAT DEFAULT NULL, `hourly_rate` DOUBLE DEFAULT NULL, `salary` DOUBLE DEFAULT NULL, PRIMARY KEY (`id`) );
Now, hibernate will create a table with the fields of Employee
, ContractualEmployee
, PermanentEmployee
, and one more column defined in the name attribute of the @DiscriminatorColumn
annotation.
Here, the
Employee
class is the topmost in the hierarchy, and we used some annotation on the Employee
class. Let's take a look at the annotations used in all three classes.
Following are the annotations used in Employee.java
:
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
: This annotation is used to define the inheritance strategy. It is used only on the root class in the hierarchy.@DiscriminatorColumn(name="emp_type", discriminatorType=DiscriminatorType.STRING, length=2)
: This annotation is used to define the discriminator column for the SINGLE_TABLE
and JOINED
mapping strategies. The attributes are as follows:name="emp_type"
: hibernate creates a column with the value provided for the name attributediscriminatorType=DiscriminatorType.STRING
: This is used to define the datatype of the discriminator columnlength=2
: This is used to define the field size of the discriminator column@DiscriminatorValue(value="E")
: This annotation defines the value of the discriminator column for this particular class. If the value is not provided, hibernate uses a class name in DiscriminatorType.STRING
, and the provided specific functions will be used otherwise.Now, we will save three records of each type of the classes Employee
, PermanentEmployee
, and ContractualEmployee
:
Session session = sessionFactory.openSession(); Transaction transaction = session.getTransaction(); transaction.begin(); Employee employee = new Employee(); employee.setName("Aarush"); session.save(employee); PermanentEmployee permanentEmployee = new PermanentEmployee(); permanentEmployee.setName("Mike"); permanentEmployee.setSalary(10000D); session.save(permanentEmployee); ContractualEmployee contractualEmployee = new ContractualEmployee(); contractualEmployee.setName("Vishal"); contractualEmployee.setHourlyRate(200D); contractualEmployee.setContractPeriod(100F); session.save(contractualEmployee); transaction.commit(); session.close();
Hibernate: insert into employee (name, emp_type) values (?, 'E') Hibernate: insert into employee (name, salary, emp_type) values (?, ?, 'PE') Hibernate: insert into employee (name, contract_period, hourly_rate, emp_type) values (?, ?, ?, 'CE')
The following employee
table below shows the database table structure after saving three records:
emp_type |
id |
name |
contract_period |
hourly_rate |
salary |
---|---|---|---|---|---|
|
|
|
( |
( |
( |
|
|
|
( |
( |
|
|
|
|
|
|
( |
Here, we defined the
Employee
class as the parent class, and the ContractualEmployee
and PermanentEmployee
classes are defined as the subclasses of the Employee
class.
When we save a record in the parent class, hibernate saves the values in the fields of that particular class and the other columns from the subclasses are saved with a null value.
If we save the record in the ContractualEmployee
and PermanentEmployee
subclasses, hibernate saves the values in the fields of the current and parent class.
We can use the value of the emp_type
column, which is E
(Employee
), PE
is for PermanentEmployee
and CE
for ContractualEmployee
to determine records.