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.
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.
- Delete all the records of CardPayment class
- Delete all the records of CashPayment class
- Insert a record of CardPayment and CashPayment in database
- Extract the records of all payments using Payment interface
- 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.