JPA Single Table per Class Hierarchy

THERE IS A NEW VERSION OF THIS POST.
CLICK HERE: http://uaihebert.com/?p=1674&page=13

Hello, how are you?

Let us talk today about how to persist in the database table classes with hierarchy. We will start with the “Single Table per Class Hierarchy” mode.

The others posts about JPA you can find in here: @ManyToMany Unidirectional and Bidirectional, @OneToMany and @ManyToOne Unidirectional and Bidirectional, OneToOne Unidirectional and Bidirectional, Mapping two Tables in one Class, Mapping Date and Enum, Composite Primary-Key, SequenceGenerator, TableGenerator – Simple Primay Key, Auto Create Schema Script with: Ant, Hibernate 3 and JPA 2, Tutorial Hibernate 3 with JPA 2.

In the end of this post you will find the source code of this post.

We will use a car project where we will have a Main class do compile and other two class extending a super class. Let us see the initial code:

package com.model;

public abstract class Car {
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}

}
package com.model;

public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

We have a simple model with three classes only; we will need to persist our class’s data. Today I will use the Postgres database as database, just to display how our data will be stored in the database.

Let us edit our model classes and add the JPA annotations:

package com.model;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FROM_CLASS", discriminatorType=DiscriminatorType.STRING)
public abstract class Car {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

I will talk a little bit about the code above:

  • The “@Inheritance” indicates to the JPA which model of hierarchy persistence we will use.
  • The “@DiscriminatorColumn” it is used to store a column name that will describe to which column that data line belongs to.

With the class bellow we will insert the data in the database. Run it and let us take a look in the result:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.model.Beetle;
import com.model.Ferrari;

public class Main {

	public static void main(String[] args) {
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("Database");
		EntityManager em = emf.createEntityManager();

		em.getTransaction().begin();

		try {
			Ferrari ferrari = new Ferrari();
			ferrari.setModel(455);
			ferrari.setName("WWJD");

			Beetle beetle = new Beetle();
			beetle.setGasCapacity(46);
			beetle.setName("Heeerbie");

			em.persist(ferrari);
			em.persist(beetle);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			em.getTransaction().commit();
			emf.close();
		}

		System.out.println("It is over");
	}

}


Notice that in our table appeared a column named “from_clas” that indicates to which class that line belongs to.

To finish our post, what could we do to change that column value? What if we want to change how the name of our class will be persisted? What if we want CAR_FERRARI instead of Ferrari?

Let us edit our class and leave it like the code below:

package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_BEETLE")
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_FERRARI")
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Run the Main class again and check the result:

Take a closer look and notice that the line 1 and 2 hold the older values; lines 3 and 4 already has the new values that we defined in the annotation “@DiscriminatorValue”.

Let us talk now about the Advantages and Disadvantages:

  • Advantages: The system will have a better performance; the JPA will not need to do join in all classes tables to gather the information. To those developers that are not familiar with this kind of persistence it will be easy to check the data in the tables.
  • Disadvantages: Your subclasses can not have any “not null” attributes. If the Beetle class has a not null attribute and you call the persist method on the Ferrari object, the database would raise an Exception.

Click here to download the code from this post. To run the code you will need the Postgres database running with a schema named SingleTable. Do not forget to change the user/password. ^o^

I hope this post can be useful to you.

If you have any question/comment just post it.

See you soon. o_

4 thoughts on “JPA Single Table per Class Hierarchy

  1. Lets say Ferrari factory would like to keep where abouts of its cars. So How do we make bidirectional many to one relation between Ferrari car(subclass of Cars) and its factory?

    • Hello Ram,

      A factory should not be a relationship with an entity.

      The truth is that I can not see how to implement a factory of entities. The EntityManager already works as a Factory. It will create/delete/update all needed entity relationships.

      Thanks for passing by.

Leave a Comment