Wednesday, 22 August 2012

JPA Inheritance Types - Joined, Single Table, Table Per Class

There are three strategies to store data of classes inheriting from each other in JPA. This is defined in the root classes of inheritance structures with the @Inheritance annotation.

Joined

The joined strategy creates a table for the root class of an inheritance structure, and a separate table for each inheriting class in the structure. The data of the root class table is not copied in the inheriting class tables.

This is a root class example:
@Entity
@AutoProperty
@Inheritance(strategy=InheritanceType.JOINED)
public class Joined implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;

    private String s;

    // Setter, Getters, Constructors, Pojomatic...

}

Single Table

The single table strategy creates a unique table for a given inheritance structure where all classes in the hierarchy are saved. This table contains a discriminating column which will help differentiate between the different classes. Each class must define a discriminating value with @DiscriminatorValue.

For the root class:
@Entity
@AutoProperty
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="discr", discriminatorType=DiscriminatorType.INTEGER)
@DiscriminatorValue("44")
public class SingleTable implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;

    private String s;

    // Setter, Getters, Constructors, Pojomatic...

}
For an inheriting class:
@Entity
@AutoProperty
@DiscriminatorValue("45")
public class InheritsSingleTable extends SingleTable {

    ...

}

Table Per Class

The table per class strategy create a separate table per class.

Root class example:
@Entity
@AutoProperty
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class TablePerClass implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private long id;
 
    private String s;

    // Setter, Getters, Constructors, Pojomatic...

}
Notice that one must use a TABLE generation type for the Id.

Check

The following:
JPA.INSTANCE.clear();

InheritsJoined ij = new InheritsJoined();
ij.setS("IJ1");
ij.setS2("IJ2");

JPA.INSTANCE.save(ij);

InheritsSingleTable st
    = new InheritsSingleTable();
st.setS("ST1");
st.setS2("ST2");

JPA.INSTANCE.save(st);

InheritsTablePerClass tpc
    = new InheritsTablePerClass();
tpc.setS("TPC1");
tpc.setS2("TPC2");

JPA.INSTANCE.save(tpc);

JPA.INSTANCE.clear();

InheritsJoined retr_ij = JPA.INSTANCE.get(
    InheritsJoined.class, ij.getId());
System.out.println("Source == Retrieved: " + (ij==retr_ij));
System.out.println(retr_ij);

InheritsSingleTable retr_st = JPA.INSTANCE.get(
    InheritsSingleTable.class, st.getId());
System.out.println("Source == Retrieved: " + (st==retr_st));
System.out.println(retr_st);

InheritsTablePerClass retr_tpc = JPA.INSTANCE.get(
    InheritsTablePerClass.class, tpc.getId());
System.out.println("Source == Retrieved: " + (tpc==retr_tpc));
System.out.println(retr_tpc);
Generates the following output:
Source == Retrieved: false
InheritsJoined{id: {1}, s: {IJ1}, s2: {IJ2}}
Source == Retrieved: false
InheritsSingleTable{id: {1}, s: {ST1}, s2: {ST2}}
Source == Retrieved: false
InheritsTablePerClass{id: {1}, s: {TPC1}, s2: {TPC2}}
The above example is available from Github in the JPA directory. It relies on Pojomatic too. Some errors messages will be displayed because of a known and harmless issue.