JavaBeat

  • Home
  • Java
    • Java 7
    • Java 8
    • Java EE
    • Servlets
  • Spring Framework
    • Spring Tutorials
    • Spring 4 Tutorials
    • Spring Boot
  • JSF Tutorials
  • Most Popular
    • Binary Search Tree Traversal
    • Spring Batch Tutorial
    • AngularJS + Spring MVC
    • Spring Data JPA Tutorial
    • Packaging and Deploying Node.js
  • About Us
    • Join Us (JBC)
  • Privacy
  • Contact Us

Polymorphic Association Mapping with Any Relationship in Hibernate

November 4, 2013 by Salil Verma Leave a Comment

Polymorphic association, supported by Hibernate, is the solution of such mapping requirement as an instance of entity needs to be associated with one of multiple possible entities. This can be implemented with Any relationship. In this relationship, the table holding the relationship would have two columns for relationship management. One to identify the primary key value of record being associated and another to identify the table to which the associated record belongs.

As the relationship is between one table to multiple other tables, foreign key relationship would not exist in database.One such real life example is sale scenario, where a vehicle seller sells truck and bus both. The requirement is to design sales system to manage the data of vehicles being sold. As bus and truck are completely different vehicles, a developer might design the system as mentioned in following class and ER diagram.

  • What is Hibernate ORM Framework?
  • Hibernate Interview Questions

PolymorphicAssociationERDiagram

PolymorphicAssociationClassDiagram

In this case, during each sale transaction, one vehicle will be sold. This vehicle can either be bus or truck. On the database level, SaleTransaction table would have two attributes vehicleType and vehicleId to manage the relationship. Vehicle type would represent whether corresponding vehicleId belongs to bus or truck.

As the relationship is between one table to multiple tables, foreign key constraint wouldn’t exist in the database layer.

Let us see the implementation of this scenario.

Class maintaining the relationship between SaleTransaction to Bus and Truck entity

SaleTransaction.java

[code lang=”java”]
package com.anytest;

import java.util.Date;

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

import lombok.Data;

import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.MetaValue;

@Data
@Entity
public class SaleTransaction {

@Id
@GeneratedValue
private Long Id;
private Date saleDate;
private Long salePrice;

@Any (metaColumn = @Column(name = "vehicleType"))
@AnyMetaDef(idType = "long", metaType = "string",
metaValues = {
@MetaValue(targetEntity = Bus.class, value = "bus"),
@MetaValue(targetEntity = Truck.class, value = "truck")
})
@Cascade( { org.hibernate.annotations.CascadeType.ALL})
@JoinColumn(name = "vehicleId")
private Vehicle vehicle;

public SaleTransaction() {
}

public SaleTransaction(final Date saleDate, final Vehicle vehicle,final Long salePrice) {
this.saleDate = saleDate;
this.vehicle = vehicle;
this.salePrice=salePrice;
}
}
[/code]

In this case, the developer has defined that SaleTransaction would be managing the relationship using vehicleType and vehicleId columns in database. If the transaction is of bus sale then vehicleType column in SaleTransaction table would be ‘bus’ and vehicleId would be the value of primary key of corresponding bus. In the same way, if the transaction is of Truck sale then the vehicleType column in SaleTransaction would be ‘truck’ and vehicleId would be the value of primary key of corresponding truck.

Query to create table structure in H2 database

[code]
create table Bus (
id bigint generated by default as identity,
acAvailable boolean not null,
buyingPrice bigint,
engineCapacity bigint,
modelNo varchar(255),
seatCount integer not null,
primary key (id)
);
create table SaleTransaction (
Id bigint generated by default as identity,
saleDate timestamp,
salePrice bigint,
vehicleType varchar(255),
vehicleId bigint,
primary key (Id)
);
create table Truck (
id bigint generated by default as identity,
buyingPrice bigint,
engineCapacity bigint,
modelNo varchar(255),
trunkVolumeCapacity bigint,
primary key (id)
);
[/code]

Vehicle Interface

[code lang=”java”]
package com.anytest;

public interface Vehicle {

Long getBuyingPrice();
String getModelNo();
Long getEngineCapacity();
}
[/code]

Bus Class

[code lang=”java”]
package com.anytest;

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

import lombok.Data;

@Data
@Entity
public class Bus implements Vehicle {

@Id
@GeneratedValue
Long id;
Long buyingPrice;
String modelNo;
Long engineCapacity;
int seatCount;
boolean acAvailable;

public Bus(){}

public Bus(final Long buyingPrice, final String modelNo, final Long engineCapacity, final int seatCount, final boolean acAvailable)
{
this.buyingPrice=buyingPrice;
this.modelNo=modelNo;
this.engineCapacity=engineCapacity;
this.seatCount=seatCount;
this.acAvailable=acAvailable;
}
}
[/code]

Truck Class

[code lang=”java”]
package com.anytest;

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

import lombok.Data;

@Data
@Entity
public class Truck implements Vehicle {

@Id
@GeneratedValue
Long id;
Long buyingPrice;
String modelNo;
Long engineCapacity;
Long trunkVolumeCapacity;

public Truck(){}

public Truck(final Long buyingPrice, final String modelNo, final Long engineCapacity, final Long trunkVolumeCapacity)
{
this.buyingPrice=buyingPrice;
this.modelNo=modelNo;
this.engineCapacity=engineCapacity;
this.trunkVolumeCapacity=trunkVolumeCapacity;
}
}
[/code]

hibernate.cfg.xml – hibernate configuration file

[code lang=”xml”]
<?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:~/anyMappingDemo</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>

<!– JDBC connection pool (use the built-in) –>
<!–<property name="hibernate.hbm2dll.auto">create-drop</property> –>
<property name="connection.pool_size">1</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>

<mapping class="com.anytest.Bus"></mapping>
<mapping class="com.anytest.Truck"></mapping>
<mapping class="com.anytest.SaleTransaction"></mapping>

</session-factory>
</hibernate-configuration>
[/code]

HibernateInterface – Class to deal with Hibernte interaction

[code lang=”java”]
package com.anytest;

import java.util.List;

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

@SuppressWarnings("deprecation")
public class HibernateInterface {

final static Configuration CONFIGURATION = new Configuration().configure();
final static SessionFactory SESSION_FACTORY = CONFIGURATION.buildSessionFactory();
static
{
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

@Override
public void run() {
if(SESSION_FACTORY!=null && !SESSION_FACTORY.isClosed())
{
SESSION_FACTORY.close();
}

}
}));
}

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 {
List<Object> resultList = session.createCriteria(classObject).list();
resultList.toString();
return resultList;
}
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;
}
}
[/code]

PaymentActoinPerfomer – Class to perform Implicit Polymorphism test

[code lang=”java”]
package com.anytest;

import java.util.Date;
public class ActionPerformer {

public static void main(String[] args) {

final Bus bus = new Bus(600000L, "ABCDBusModel", 100L, 35, true);
final Truck truck = new Truck(700000L, "ABCDTruckModel", 300L, 30000L);

final SaleTransaction busSaleTransaction = new SaleTransaction(new Date(), bus,650000L);
final SaleTransaction truckSaleTransaction = new SaleTransaction(new Date(), truck,780000L);

HibernateInterface.exectueQuery("delete from com.anytest.SaleTransaction");
HibernateInterface.exectueQuery("delete from com.anytest.Vehicle");

HibernateInterface.saveEntities(busSaleTransaction);
HibernateInterface.saveEntities(truckSaleTransaction);

System.out.println("SaleTransaction records =" + HibernateInterface.getRecordsOfType(SaleTransaction.class));
}

}
[/code]

In this scenario, the developer has set cascade type as ALL. So while saving the transient entry of busSaleTransaction and truckSaleTransaction, hibernate at first stores the transient records of Bus into BUS table or Truck into TRUCK table and then corresponding transaction entry in SaleTransaction table.

This behaviour can be seen in the query fired hibernate. While saving bus transaction hibernate fires following query –

[code lang=”java”]
Hibernate:
insert
into
Bus
(id, acAvailable, buyingPrice, engineCapacity, modelNo, seatCount)
values
(null, ?, ?, ?, ?, ?)
Hibernate:
insert
into
SaleTransaction
(Id, saleDate, salePrice, vehicleType, vehicleId)
values
(null, ?, ?, ?, ?)
[/code]

While retrieving the records from SaleTransaction table, hibernate fires the query on SaleTransaction table. Then depending on the value of vehicleType hibernate fires the query in Bus or Truck table to get the associated records. In this Sales scenario hibernate fires following queries to get SaleTransaction records –

[code lang=”java”]
Hibernate:
select
this_.Id as Id1_1_0_,
this_.saleDate as saleDate2_1_0_,
this_.salePrice as salePric3_1_0_,
this_.vehicleType as vehicleT4_1_0_,
this_.vehicleId as vehicleI5_1_0_
from
SaleTransaction this_
Hibernate:
select
bus0_.id as id1_0_0_,
bus0_.acAvailable as acAvaila2_0_0_,
bus0_.buyingPrice as buyingPr3_0_0_,
bus0_.engineCapacity as engineCa4_0_0_,
bus0_.modelNo as modelNo5_0_0_,
bus0_.seatCount as seatCoun6_0_0_
from
Bus bus0_
where
bus0_.id=?
Hibernate:
select
truck0_.id as id1_2_0_,
truck0_.buyingPrice as buyingPr2_2_0_,
truck0_.engineCapacity as engineCa3_2_0_,
truck0_.modelNo as modelNo4_2_0_,
truck0_.trunkVolumeCapacity as trunkVol5_2_0_
from
Truck truck0_
where
truck0_.id=?
[/code]

The result of retrieving all sales transactions by firing query on SaleTransaction.class is as following –

[code lang=”java”]
SaleTransaction records =[SaleTransaction(Id=5, saleDate=2013-10-27 18:35:09.088, vehicle=Bus(id=3, buyingPrice=600000, modelNo=ABCDBusModel, engineCapacity=100, seatCount=35, acAvailable=true), salePrice=650000),
SaleTransaction(Id=6, saleDate=2013-10-27 18:35:09.088, vehicle=Truck(id=3, buyingPrice=700000, modelNo=ABCDTruckModel, engineCapacity=300, trunkVolumeCapacity=30000), salePrice=780000)]
[/code]

Limitation of Any Hibernate Mapping

Any relationship cannot be implemented as bidirectional association. It will be unidirectional relationship managed by entity holding the relationship data.

The second limitation of Any relationship is that one can not set foreign key relationship on database layer to manage integrity.

The working project can be accessed from here in github.

I hope this article helped you to understand polymorphic association implementation in hibernate in a better way. Do share your experience about the article and way you are planning to use polymorphic association 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.

Filed Under: Hibernate Tagged With: Hibernate

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.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Follow Us

  • Facebook
  • Pinterest

As a participant in the Amazon Services LLC Associates Program, this site may earn from qualifying purchases. We may also earn commissions on purchases from other retail websites.

JavaBeat

FEATURED TUTORIALS

Answered: Using Java to Convert Int to String

What is new in Java 6.0 Collections API?

The Java 6.0 Compiler API

Copyright © by JavaBeat · All rights reserved