Implicit Polymorphism in Hibernate

Implicit polymorphism in Hibernate is one of the inheritance strategies supported in Hibernate. Implicit polymorphism means if a class or interface is used in HQL, criteria or named queries, hibernate fetches the records from the table mapped to the used class along with all the tables mapped to its subclasses, at any hierarchy level. This is one of the great advantage for using hibernate. If you are not familiar with hibernate framework, please read our articles titles What is Hibernate ORM Framework?. Also read interview questions on hibernate.

Advantage of Implicit Polymorphism

The advantage of this behaviour is that we can get the data at generalized level without investing any effort in writing union query or making changes in mapping. One of such example is payment use-case as mentioned in following class and ER diagram.

HibernateImplicitPolymorphism-diag

ERDiagram

Here we have implemented the mapping using table per concrete class approach, where CashPayment class is mapped to CASHPAYMENT table and CardPayment table is mapped to CARDPAYMENT table.

If we want to retrieve the list of all the payments made via cash or card, below query would do the magic.

session.createCriteria(Payment.class).list();

While processing this criteria query, hibernate perform following operations –

  • Identify all the subclasses of payment, which are mapped to any table
  • Fire individual query to all of the tables
  • Combine the result of all the queries into list and return from hibernate layer

Let us see the above scenario in actual code execution.

Query to create table structure in H2 database

create table CardPayment (paymentId bigint primary key  , paymentAmount bigint  , paymentCurrency varchar (30), payingAccountNumber varchar(30), transferringBankName varchar(30));
create table CashPayment(paymentId bigint primary key , paymentAmount bigint  , paymentCurrency varchar (30), payerName varchar(30));

Payment Interface

package com.salil.beans;
public interface Payment {
	Long getPaymentAmount();
	void setPaymentAmount(final Long paymentAmount);
	String getPaymentCurrency();
	void setPaymentCurrency(final String currency);
}

CardPayment Class

package com.salil.beans;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class CardPayment implements Payment {
	@Id
	@GeneratedValue(generator="increment")
	@GenericGenerator(name="increment", strategy = "increment")
	private Long paymentId;
	private Long paymentAmount;
	private String paymentCurrency;
	private String payingAccountNumber;
	private String transferringBankName;
	public CardPayment() {
	}

	public CardPayment(final Long paymentAmount, final String paymentCurrency, final String payingAccountNumber,final String transferringBankName)
	{
		this.paymentAmount = paymentAmount;
		this.paymentCurrency = paymentCurrency;
		this.payingAccountNumber = payingAccountNumber;
		this.transferringBankName= transferringBankName;
	}

	public Long getPaymentId() {
		return paymentId;
	}
	public void setPaymentId(Long paymentId) {
		this.paymentId = paymentId;
	}

	public Long getPaymentAmount() {
		return paymentAmount;
	}
	public void setPaymentAmount(Long paymentAmount) {
		this.paymentAmount = paymentAmount;
	}

	public String getPaymentCurrency() {
		return paymentCurrency;
	}
	public void setPaymentCurrency(String paymentCurrency) {
		this.paymentCurrency = paymentCurrency;
	}

	public String getPayingAccountNumber() {
		return payingAccountNumber;
	}
	public void setPayingAccountNumber(String payingAccountNumber) {
		this.payingAccountNumber = payingAccountNumber;

	}
	public String getTransferringBankName() {
		return transferringBankName;
	}
	public void setTransferringBankName(String transferringBankName) {
		this.transferringBankName = transferringBankName;
	}

	@Override
	public String toString() {
		ToStringBuilder toStringBuilder = new ToStringBuilder(this,
				ToStringStyle.DEFAULT_STYLE);
		toStringBuilder.append("paymentId",paymentId);
		toStringBuilder.append("paymentAmount",paymentAmount);
		toStringBuilder.append("paymentCurrency",paymentCurrency);
		toStringBuilder.append("payingAccountNumber",payingAccountNumber);
		toStringBuilder.append("transferringBankName",transferringBankName);
		return toStringBuilder.toString();
	}
}
}

CashPayment Class

package com.salil.beans;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class CashPayment implements Payment{

	@Id
	@GeneratedValue(generator="increment")
	@GenericGenerator(name="increment", strategy = "increment")
	private Long paymentId;
	private Long paymentAmount;
	private String paymentCurrency;
	private String payerName;

	public CashPayment() {
	}
	public CashPayment(final Long paymentAmount, final String paymentCurrency, final String payerName)
	{
		this.paymentAmount = paymentAmount;
		this.paymentCurrency = paymentCurrency;
		this.payerName = payerName;
	}

	public Long getPaymentId() {
		return paymentId;
	}
	public void setPaymentId(Long paymentId) {
		this.paymentId = paymentId;
	}
	public Long getPaymentAmount() {
		return paymentAmount;
	}
	public void setPaymentAmount(Long paymentAmount) {
		this.paymentAmount = paymentAmount;
	}
	public String getPaymentCurrency() {
		return paymentCurrency;
	}
	public void setPaymentCurrency(String paymentCurrency) {
		this.paymentCurrency = paymentCurrency;
	}
	public String getPayerName() {
		return payerName;
	}
	public void setPayerName(String payerName) {
		this.payerName = payerName;
	}
	@Override
	public String toString() {
		ToStringBuilder toStringBuilder = new ToStringBuilder(this,ToStringStyle.DEFAULT_STYLE);
		toStringBuilder.append("paymentId",paymentId);
		toStringBuilder.append("paymentAmount",paymentAmount);
		toStringBuilder.append("paymentCurrency",paymentCurrency);
		toStringBuilder.append("payerName",payerName);
		return toStringBuilder.toString();
	}
}

hibernate.cfg.xml – hibernate configuration file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">org.h2.Driver</property>
		<property name="hibernate.connection.url">jdbc:h2:~/test</property>
		<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
		<property name="hibernate.connection.username">sa</property>
		<property name="hibernate.connection.password"></property>

		<!-- JDBC connection pool (use the built-in) -->
		<property name="connection.pool_size">2</property>

		<property name="hibernate.show_sql">true</property>
		<property name="hibernate.format_sql">true</property>

		<mapping class="com.salil.beans.CardPayment"></mapping>
		<mapping class="com.salil.beans.CashPayment"></mapping>
	</session-factory>
</hibernate-configuration>

HibernateInterface – Class to deal with Hibernte interaction

package com.salil;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;

public class HibernateInterface {

	final static Configuration CONFIGURATION = new AnnotationConfiguration().configure();
	final static SessionFactory SESSION_FACTORY = CONFIGURATION.buildSessionFactory();

	public static void saveEntities(final Object entity)
	{
		final Session session = SESSION_FACTORY.openSession();
		session.beginTransaction();
		try {
			session.saveOrUpdate(entity);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
		finally{
			session.close();
		}
	}

	@SuppressWarnings("unchecked")
	public static List <Object> getRecordsOfType(final Class classObject)
	{
		final Session session = SESSION_FACTORY.openSession();
		try {
			return session.createCriteria(classObject).list();
		}
		finally{
			session.close();
		}
	}

	public static int exectueQuery(final String hqlQuery)
	{
		int effectedRecords=0;
		final Session session = SESSION_FACTORY.openSession();
		session.beginTransaction();
		try {
			effectedRecords=session.createQuery(hqlQuery).executeUpdate();
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
		finally{
			session.close();
		}
		return effectedRecords;
	}

}

PaymentActoinPerfomer – Class to perform Implicit Polymorphism test

package com.salil;

import com.salil.beans.CardPayment;
import com.salil.beans.CashPayment;
import com.salil.beans.Payment;

public class PaymentActoinPerfomer {

	public static void main(String[] args) {

		HibernateInterface.exectueQuery("delete from com.salil.beans.CardPayment");
		HibernateInterface.exectueQuery("delete from com.salil.beans.CashPayment");

		HibernateInterface.saveEntities(new CardPayment(600L, "IND", "123456789", "Salil Verma"));
		HibernateInterface.saveEntities(new CashPayment(200L, "USD", "Salil Verma"));

		// Attempt to retrieve all the payments (cash and card both) existing in the system
		System.out.println("\n\nList of Payments = "+HibernateInterface.getRecordsOfType(Payment.class));
	}
}

PaymentActoinPerfomer performs the follwing operations.

  1. Delete all the records of CardPayment class
  2. Delete all the records of CashPayment class
  3. Insert a record of CardPayment and CashPayment in database
  4. Extract the records of all payments using Payment interface
  5. While extracting the records of all the payments from payment interface hibernate fires following queries
Hibernate:
    select
        this_.paymentId as paymentId1_0_,
        this_.paymentAmount as paymentA2_1_0_,
        this_.paymentCurrency as paymentC3_1_0_,
        this_.payerName as payerName1_0_
    from
        CashPayment this_
Hibernate:
    select
        this_.paymentId as paymentId0_0_,
        this_.paymentAmount as paymentA2_0_0_,
        this_.paymentCurrency as paymentC3_0_0_,
        this_.payingAccountNumber as payingAc4_0_0_,
        this_.transferringBankName as transfer5_0_0_
    from
        CardPayment this_

The result is as following, which shows that by querying Payment interface, we have got the records from CardPayment and CashPayment both the classes.


List of Payments = [com.salil.beans.CardPayment@f9533ee[paymentId=1,paymentAmount=600,paymentCurrency=IND,payingAccountNumber=123456789,transferringBankName=Salil Verma],
 com.salil.beans.CashPayment@48517e3c[paymentId=1,paymentAmount=200,paymentCurrency=USD,payerName=Salil Verma]]

DisAdvantages of Using Implicit Polymorphism

While using implicit polymorphism user might fire a query at very generalized class, causing hibernate to extract lots of data resulting into out of memory error.

One such technically possible example could be the scenario, where user fires a query on object class as mentioned in following code. In this scenario, hibernate would bring result by firing query against all the tables mapped in application.

session.createCriteria(Object.class).list()

Let us enhance the payment example to see criteria query in object class bringing the records from all the tables mapped in hibernate.

Query to create student table

create table Student(studentId bigint primary key , firstName varchar(30)  , lastName varchar(30));

Student Class – Another class having no relationship with Payment class

package com.salil.beans;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.GenericGenerator;

@Entity
public class Student {

	@Id
	@GeneratedValue(generator="increment")
	@GenericGenerator(name="increment", strategy = "increment")
	private long studentId;
	private String firstName;
	private String lastName;

	public Student() {
	}

	public Student(final String firstName, final String lastName)
	{
		this.firstName=firstName;
		this.lastName=lastName;
	}

	public long getStudentId() {
		return studentId;
	}
	public void setStudentId(long studentId) {
		this.studentId = studentId;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Override
	public String toString() {
		ToStringBuilder toStringBuilder = new ToStringBuilder(this,
				ToStringStyle.DEFAULT_STYLE);
		toStringBuilder.append("studentId", studentId);
		toStringBuilder.append("fistName",firstName);
		toStringBuilder.append("lastName",lastName);
		return toStringBuilder.toString();
	}
}

ObjectActionPerformer Class – Class to perform query on Object class

package com.salil;

import com.salil.beans.Student;

public class ObjectActionPerformer {

	public static void main(String[] args) {

		HibernateInterface.exectueQuery("delete from com.salil.beans.Student");
		HibernateInterface.saveEntities(new Student("Salil", "Verma"));
		//This would retrieve all the rows in database which can be accessed via hibernate
		System.out.println("\n\nResult of querying on Object class = "+HibernateInterface.getRecordsOfType(Object.class));
	}
}

In this scenario, hibernate fires select query in all the tables mapped to hibernate.

Hibernate:
    select
        this_.paymentId as paymentId1_0_,
        this_.paymentAmount as paymentA2_1_0_,
        this_.paymentCurrency as paymentC3_1_0_,
        this_.payerName as payerName1_0_
    from
        CashPayment this_
Hibernate:
    select
        this_.paymentId as paymentId0_0_,
        this_.paymentAmount as paymentA2_0_0_,
        this_.paymentCurrency as paymentC3_0_0_,
        this_.payingAccountNumber as payingAc4_0_0_,
        this_.transferringBankName as transfer5_0_0_
    from
        CardPayment this_
Hibernate:
    select
        this_.studentId as studentId2_0_,
        this_.firstName as firstName2_0_,
        this_.lastName as lastName2_0_
    from
        Student this_

Result of ObjectActionPerformer class execution is as following:

Result of querying on Object class = [com.salil.beans.Student@5afca7cf[studentId=1,fistName=Salil,lastName=Verma],
com.salil.beans.CardPayment@2cadd45e[paymentId=1,paymentAmount=600,paymentCurrency=IND,payingAccountNumber=123456789,transferringBankName=Salil Verma],
com.salil.beans.CashPayment@366e740a[paymentId=1,paymentAmount=200,paymentCurrency=USD,payerName=Salil Verma]]

Here we can see that we fired a query on Object class and got all the records from all the tables mapped in Hibernate.

The working project can be accessed from here in github.

I hope this article helped you to understand implicit polymorphism in a better way. Do share your experience about the article and way you are planning to use implicit polymorphism in your application. I will come with the different new topics in future for sure. If you have any questions, please write it in the comments section.

Comments

comments

About Salil Verma

Salil is tech lead in BNP Paribas. He has got eight years of experience and have worked with JP Morgan and Tata Consultancy Services. During his tenure he worked in Water Fall model, Rational Unified process ,Scrum and Test Driven Development.

Speak Your Mind

*