Engineering Full Stack Apps with Java and JavaScript
Hibernate uses some strategies for saving an inheritance hierarchy of classes created in Java to the databases like SINGLE_TABLE, TABLE_PER_CLASS and JOINED. We use @Inheritance annotation over the entity class to specify the inheritance strategy. We specify the inheritance strategy by assigning a strategy from the InheritanceType enumeration to the strategy parameter of the @Inheritance annotation. Strategies defined in the InheritanceType enumeration are InheritanceType.SINGLE_TABLE, InheritanceType.TABLE_PER_CLASS and InheritanceType.JOINED. If you don’t provide the @Inheritance annotation or any strategies, the default strategy is SINGLE_TABLE.
In the SINGLE_TABLE strategy, hibernate will create a single table for all the classes in an inheritance hierarchy. There will be a discriminator column that tells us the class to which that row belongs. Problem with single table is that it is the least normalized and columns for all classes in the inheritance hierarchy will be present in the table, even if it is not used by other classes. For instance, circle will also have length and breadth columns, though not used; and Rectangle will have the radius field. We will understand this better with an example.
In this example, we will create and save three entity classes – Shape, Rectangle and Circle; where Rectangle and Circle extends from Shape. If you are not following the tutorials in order, refer to the article http://javajee.com/your-first-hibernate-program for the basic setup including hibernate configuration file. You should replace the user class in the configuration file with Shape, Rectangle and Circle classes as:
<!-- Names the annotated entity class -->
<mapping class="com.javajee.hiberex.dto.Shape"/>
<mapping class="com.javajee.hiberex.dto.Rectangle"/>
<mapping class="com.javajee.hiberex.dto.Circle"/>
Else you will get org.hibernate.MappingException: Unknown entity:…
The base entity class, shape will have id and fillcolor properties:
@Entity
public class Shape {
@Id @GeneratedValue
int shapeId;
String fillcolor;
//Getters and setters not shown here; create them manually or generate in eclipse.
}
Entity class Rectangle extends from Shape, and have length and breadth properties:
@Entity
public class Rectangle extends Shape{
long length;
long breadth;
//Getters and setters not shown here; create them manually or generate in eclipse.
}
Entity class Circle extends from Shape, and has one property radius:
@Entity
public class Circle extends Shape{
long radius;
//Getters and setters not shown here; create them manually or generate in eclipse.
}
Now use the below test class to create objects for these classes, populate data and save them:
public class ShapeTest {
public static void main(String[] args) {
Shape s = new Shape();
s.setFillcolor("Green");
Rectangle r = new Rectangle();
r.setBreadth(4);
r.setLength(6);
Circle c = new Circle();
c.setRadius(5);
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(s);
session.save(r);
session.save(c);
session.getTransaction().commit();
}
}
When you execute the above test class, below queries are executed by hibernate (as seen from the console):
Hibernate: insert into Shape (fillcolor, DTYPE) values (?, 'Shape')
Hibernate: insert into Shape (fillcolor, breadth, length, DTYPE) values (?, ?, ?, 'Rectangle')
Hibernate: insert into Shape (fillcolor, radius, DTYPE) values (?, ?, 'Circle')
You can see that only one table, Shape is created and all data is being inserted into the same table. From the above queries, we can find the columns of the Shape table: fillcolor, DTYPE, breadth, length, radius. Columns for all classes in the inheritance hierarchy will be present in the table, even if it is not used by other classes. For instance, circle will also have length and breadth columns, though not used; and Rectangle will have the radius field. Things will be more clear, once you see the database snapshot. Just do a select all query (select * from Shape). There will be only a single table. The DTYPE column is the discriminator and has values Shape, Rectangle or Circle to denote to which class each row belongs. If a field (or column) is not applicable for a particular class, it will have null values for that class.
This behavior is known as SINGLE_TABLE strategy, and is the default inheritance behavior in hibernate if we don’t specify a different strategy using the @Inheritance annotation. You can also explicitly specify SINGLE_TABLE strategy using the @Inheritance annotation:
@Entity
@Inheritance (strategy = InheritanceType.SINGLE_TABLE)
public class Shape {
…
If you execute the code after placing @Inheritance annotation with strategy as SINGLE_TABLE, you will get the same behavior as below. You can however use another annotation @DescriminatorValue to specify a different name than the class name for the discriminator column (DTYPE) value:
@Entity
@Inheritance (strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorValue("SH")
public class Shape {
…
@Entity
@DiscriminatorValue("RE")
public class Rectangle extends Shape{
…
@Entity
@DiscriminatorValue("CI")
public class Circle extends Shape{
…
If you execute the same test class, you will see the below queries generated by hibernate:
Hibernate: insert into Shape (fillcolor, DTYPE) values (?, 'SH')
Hibernate: insert into Shape (fillcolor, breadth, length, DTYPE) values (?, ?, ?, 'RE')
Hibernate: insert into Shape (fillcolor, radius, DTYPE) values (?, ?, 'CI')
You can also change the discriminator column name using @DiscriminatorColumn annotation as:
@Entity
@Inheritance (strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorValue("SH")
@DiscriminatorColumn (name="DISC_COL")
public class Shape {
…
If you execute the same test class, you will now see the below queries generated by hibernate:
Hibernate: insert into Shape (fillcolor, DISC_COL) values (?, 'SH')
Hibernate: insert into Shape (fillcolor, breadth, length, DISC_COL) values (?, ?, ?, 'RE')
Hibernate: insert into Shape (fillcolor, radius, DISC_COL) values (?, ?, 'CI')
Single table strategy is the least normalized strategy. Other two strategies are TABLE_PER_CLASS and JOINED.