Hibernate/JPA – Inheritance and Polymorphic Asociations

This post is part of a series of post I will be written about Hibernate. In this one, I will be describing how Hibernate and JPA deals with inheritance and polymorphic associations.

I’m going to implement a very simple bank domain model to demonstrate how to persist an inheritance hierarchy and deal with polymorphic associations.

Let suppose the bank has clients and accounts. We have two kinds of accounts: SavingsAccount and CheckingAccount. In the SavingsAccount you can do extractions only if you have enough money while in the CheckingAccount you can do extractions until you reach an overdraft set by the bank. In both accounts you can deposit money.

To implement this, I’m going to create the following abstract class called BankAccount, with some common behaviour.

public abstract class BankAccount {

	protected float amount;

	public abstract float extract(float anAmount);

	public void deposit(float anAmount) {
		this.amount += anAmount;
	}

	public float getAmount() {
		return amount;
	}
}

And then the SavingsAccount and CheckingAccount classes extending it.

public class SavingsAccount extends BankAccount {

	public SavingsAccount(float amount) {
		this.amount = amount;
	}

	@Override
	public float extract(float unMonto) {
		if (this.amount >= unMonto) {
			this.amount -= unMonto;
			return this.amount;
		}
		throw new RuntimeException("There is not enough money to do "
				+ "the extraction...");
	}
}
public class CheckingAccount extends BankAccount {

	private float overdraft;

	public CheckingAccount(float amount, float overdraft) {
		this.amount = amount;
		this.overdraft = overdraft;
	}

	public float getOverdraft() {
		return overdraft;
	}

	@Override
	public float extract(float unMonto) {
		if (unMonto <= (this.amount + overdraft)) {
			this.amount -= unMonto;
			return this.amount;
		}

		throw new RuntimeException("There is not enough money to do "
				+ "the extraction...");
	}
}

Now, bank’s clients might have any number and types of accounts. We should also be able to know the total amount of money each client has. To implement this, the clients will respond to the totalMoney() message.

public class Client {

	private String name;
	private Set bankAccounts = new HashSet<>();

	public Client(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public float totalMoney() {
		float total = 0;
		for (BankAccount bankAccount : bankAccounts) {
			total += bankAccount.getAmount();
		}
		return total;
	}

	public void addAccount(BankAccount ba) {
		this.bankAccounts.add(ba);
	}
}

Let’s add two test cases before move on:

public class TestClient {

	@Test
	public void client_with_no_accounts_the_total_is_zero() {
		Client client = new Client("aClient");
		Assert.assertEquals(0f, client.totalMoney(), 0);
	}

	@Test
	public void client_with_two_accounts_the_total_is_calculated() {
		Client client = new Client("aClient");
		BankAccount ba1 = new SavingsAccount(350);
		BankAccount ba2 = new CheckingAccount(250, 100);
		client.addAccount(ba1);
		client.addAccount(ba2);
		Assert.assertEquals(600f, client.totalMoney(), 0);
	}
}

So, we have a simple inheritance hierarchy and a Client class with the bankAccounts polymorphic association. Next, we are going to map them into a relational model. I will first add an Id instance variable for entities. Note that the code below will only contain the necessary instance variables and mapping annotations to show our goal (you will also have to add setters and getters and the default constructor as required by Hibernate).

Single Table

I will start showing the way of persisting an inheritance hierarchy in a relational model called Single Table:

@Entity
@Inheritance
@DiscriminatorColumn(name="account_type")
public abstract class BankAccount {

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

	@Column
	protected float amount;

	...
}

@Entity
@DiscriminatorValue("SA")
public class SavingsAccount extends BankAccount {
	...
}

@Entity
@DiscriminatorValue("CA")
public class CheckingAccount extends BankAccount {

	@Column
	private float overdraft;

	...
}

And the Client mapping that follows:

@Entity
public class Client {

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

	@Column
	private String name;

	@OneToMany
	@JoinColumn(name = "client_id", referencedColumnName = "id")
	private Set bankAccounts = new HashSet<>();

	...
}

The bankAccounts mapping is defining a one-to-many association of BankAccounts that can be SavingsAccount or CheckingAccount, hence a polymorphic persisted association.

Note that the beauty here is that my original code did not change after introducing persistence to the model. This is valid for all the ways of persisting inheritance of course. My test remain exactly the same and that should be the case as I’m testing business logic and ideally, the addition of persistence should not interfere with that.

This mapping will generate a single table, in this case called: bankaccount, for the entire hierarchy. It is using a column called account_type to distinguish between the two kinds of bank accounts. Based on that value, Hibernate will create the instances appropriately.
As an example, the code below creates two accounts, a client and assign the accounts to the client.

// Code snippet (1)
EntityManagerFactory emf = Persistence.
		createEntityManagerFactory("bd_name");
EntityManager em = null;
EntityTransaction tx = null;

try {
	em = emf.createEntityManager();
	tx = em.getTransaction();
	tx.begin();

	Client client = new Client("aClient");
	BankAccount ba1 = new SavingsAccount(350);
	BankAccount ba2 = new CheckingAccount(250, 100);
	client.addAccount(ba1);
	client.addAccount(ba2);

	em.persist(ba1);
	em.persist(ba2);
	em.persist(client);

	tx.commit();
} catch (Exception e) {
	if (tx != null) {
		tx.rollback();
	}
	throw new RuntimeException(e);
} finally {
	em.close();
}

The execution of the code snippet (1) above, will generate a single table like below:

account_type id amount overdraft client_id
CA acount1 250 100 client1
SA acount2 350 null client1

Suppose the code snippet (2) below that retrieve the client1 from the database and print the total amount of money:

//Code Snippet (2)
EntityManagerFactory emf = Persistence.
		createEntityManagerFactory("bd_name");
EntityManager em = null;
EntityTransaction tx = null;
try {
	em = emf.createEntityManager();
	tx = em.getTransaction();
	tx.begin();

	Client client1 = em.find(Client.class, "client1");
	System.out.println(client1.totalMoney());

	tx.commit();
} finally {
	if (em != null) {
		em.close();
	}
}

That snippet will provoke the initialization of the bankAccounts collection by executing a sql statement like the following:

    select
        cliente_id,
        id,
        amount,
        overdraft,
        account_type
    from
        BankAccount
    where
        cliente_id = "client1"

A very simple and best performing sql statement with the drawback of a denormalized relational model with nullable columns.

Joined

For this way of mapping inheritance, each class in the inheritance hierarchy (including abstract classes) will have a table, sharing the primary key which represent the object id. The following code snippet shows how to map the inheritance in this way. Note that the difference in the mapping with the previous way is in the abstract class, in the @Inheritance annotation. The discriminator column is not needed here.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class BankAccount {

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

	@Column
	protected float amount;
	...
}

@Entity
public class SavingsAccount extends BankAccount {
	...
}

@Entity
public class CheckingAccount extends BankAccount {

	@Column
	private float overdraft;
	...
}

Now executing the code snippet (1) with our classes with the Joined mapping, it will generate the following 3 tables:

bankaccount:

id amount client_id
acount1 250 client1
acount2 350 client1

savingsaccount:

id
acount2

checkingaccount:

id overdraft
acount1 100

And executing the code snippet (2), the initialization of the bankAccounts collection will generate a sql statement like the following:

    select
        bankaccoun0_.cliente_id,
        bankaccoun0_.id,
        bankaccoun0_.amount,
        bankaccoun0_2_.overdraft,
        case
            when bankaccoun0_1_.id is not null then 1
            when bankaccoun0_2_.id is not null then 2
            when bankaccoun0_.id is not null then 0
            else -1
        end as clazz_1_
    from
        BankAccount bankaccoun0_
    left outer join
        SavingsAccount bankaccoun0_1_
            on bankaccoun0_.id = bankaccoun0_1_.id
    left outer join
        CheckingAccount bankaccoun0_2_
            on bankaccoun0_.id = bankaccoun0_2_.id
    where
        bankaccoun0_.cliente_id = 'client1'

We now have a good normalized relational model with an additional cost when retrieving the the polymorphic association, due to the join sql operation that is required.

Table per Concrete Class

In this way of mapping inheritance, there will be a table per concrete class containing all their instance variables plus the ones that belongs to their super classes. It supports polymorphic associations only if the association is bidirectional.

Let see how to map this. In order to make the association bidirectional, the BankAccount class now contains a @ManyToOne association with the Client. The @OneToMany association defined on the Client has the mappedBy attribute to tell Hibernate which is the owning side.

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

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

	@Column
	protected float amount;

	@ManyToOne
	@JoinColumn(name = "client_id")
	private Client client;

	...
}

@Entity
public class SavingsAccount extends BankAccount {
	...
}

@Entity
public class CheckingAccount extends BankAccount {

	@Column
	private float overdraft;
	...
}

@Entity
public class Client {

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

	@Column
	private String name;

	@OneToMany(mappedBy = "client")
	private Set bankAccounts = new HashSet<>();

	...
}

Executing the code snippet (1) will generate two tables, one for CheckingAccount and another for SavingsAccount, as shown next:

checkingaccount:

id amount overdraft client_id
acount1 250 100 client1

savingsaccount:

id amount client_id
acount2 350 client1

And when executing the code snippet (2), the initialization of the bankAccounts collection will generate a sql statement like this:

    select
        bankaccoun0_.client_id as client_i3_5_0_,
        bankaccoun0_.id as id1_0_0_,
        bankaccoun0_.id as id1_0_1_,
        bankaccoun0_.amount as amount2_0_1_,
        bankaccoun0_.client_id as client_i3_0_1_,
        bankaccoun0_.overdraft as overdraf1_3_1_,
        bankaccoun0_.clazz_ as clazz_1_
    from
        (select
            id,
            amount,
            client_id,
            overdraft,
            1 as clazz_
        from
            checkingaccount

        union

        all select
            id,
            amount,
            client_id,
            nullif(0,
            0) as overdraft,
            2 as clazz_
        from
            savingsaccount
        ) bankaccoun0_
        where
                bankaccoun0_.client_id = 'client1'

Note that it is performing a union with the two bank account tables. Union is probably the most expensive operation for a database engine.

I have just presented the three ways of mapping inheritance that supports polymorphic associations. My choice would be the Joined one. Because it generates a normalized relational model. In addition, note that doing operations that require traversing a persisted collection will make the entire collection to be loaded in memory. So, you can do this only if you know that the collection will be small. And if the collection is small the sql joins operation will not be expensive.

Other Way of Persisting Inheritance

The @MappedSupperclass annotation give us another way of persisting inheritance, without supporting polymorphic associations. The @MappedSupperclass is good in case you want to move a bunch of duplicated instance variables into one super class. The following sample code shows how to use it.

@MappedSuperclass
public abstract class PersistentObject {

	@Id
	@GeneratedValue(generator = "system-uuid")
	@GenericGenerator(name = "system-uuid", strategy = "uuid")
	private String id;

	protected String getId() {
		return id;
	}

	//needed by Hibernate
	private void setId(String id) {
		this.id = id;
	}
}

@Entity
public class SomeEntity extends PersistentObject {

	@Column
	private String name;

	...
}

@Entity
public class SomeOtherEntity extends PersistentObject {

	@Column
	private String otherName;

	...
}

This mapping will generate a relational model similar to the one that Table per Concrete Class generates. In this case, the tables generated will be:

someentity:

id name

someotherentity:

id othername

Note that the id instance variable, the mapping of it into a column and their get/set methods are only defined in the PersistentObject class. However, the id column will exist in each table generated by each Entity class extending PersistentObject.

Posted in Persistence Tagged with: , , , ,