Hibernate Inheritance Table per Class Hierarchy using JPA Annotations
An simple tutorial on Hibernate Inheritance Table per Class Hierarchy using JPA Annotations.
1. Introduction
Java is an Object Oriented Language and supports Inheritance which is one of the most visible paradigms of Object-Relational mismatch. Object Oriented Systems can model both “is a”and “has a” relationship whereas Relational Model supports only “has a” relationship between two entities. Hibernate can help us map such Objects with relational tables.
Basically there are three ways in which we can implement Inheritance in Hibernate. We shall use one of the ways here Table per Class Hierarchy.
2. Example
Consider the below Object Model where in we have Base Class Payment and its subclasses OnlineTransfer, ChequePayment, CashPayment which are basically specific forms of Payments.
3. Database Table Design
In Table per Class Hierarchy method, we save the entire Class hierarchy in a single Database table. A discriminator is a key to uniquely identify the Class type of the Class hierarchy. The Table Design would look like this for the above Class Diagram.
As we mentioned earlier only one Table i.e. PAYMENT is created having all the attributes of subclasses also.
4. Project Setup
Lets create a simple Maven project with the below pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.heapcode.jpa</groupId> <artifactId>jpa-table-perclass</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>jpa-table-perclass</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>3.6.10.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.6.10.Final</version> </dependency> </dependencies> </project> |
In the above pom.xml we added dependencies for Hibernate 3.6.10, MySQL Connector 5.1.25, Javassist and Sl4J.
5. Database Table Creation
1 2 3 4 5 6 7 8 9 10 |
CREATE TABLE `heapcode`.`PAYMENT` ( `PAYMENT_ID_PK` INT NOT NULL AUTO_INCREMENT , `AMOUNT` DECIMAL(10,2) NOT NULL , `DISC` VARCHAR(45) NOT NULL , `CASH_DEPOSIT_DATE` DATETIME NULL , `CHEQUE_NO` VARCHAR(45) NULL , `CHEQUE_DATE` DATETIME NULL , `TRANSACTION_NO` VARCHAR(45) NULL , PRIMARY KEY (`PAYMENT_ID_PK`) , UNIQUE INDEX `PAYMENT_ID_PK_UNIQUE` (`PAYMENT_ID_PK` ASC) );SET=latin1$$ |
6. JPA Entity Classes
Lets us create a JPA Entity Class for Payment.java, CashPayment.java, ChequePayment.java, OnlineTransfer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.heapcode.jpa.entity; import java.io.Serializable; import java.math.BigDecimal; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorValue; 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; /** * @author Manjunath Sampath * */ @Entity @Table(name="PAYMENT") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn ( name="DISC", discriminatorType=DiscriminatorType.STRING ) @DiscriminatorValue(value="OTHERS") public class Payment implements Serializable { private static final long serialVersionUID = 2846136545845608776L; private int paymentIdPk; private BigDecimal amount; @Id @Column(name="PAYMENT_ID_PK") @GeneratedValue(strategy=GenerationType.IDENTITY) public int getPaymentIdPk() { return paymentIdPk; } public void setPaymentIdPk(int paymentIdPk) { this.paymentIdPk = paymentIdPk; } @Column(name="AMOUNT",nullable=false,precision=2) public BigDecimal getAmount() { return amount; } public void setAmount(BigDecimal amount) { this.amount = amount; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.heapcode.jpa.entity; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.Temporal; import javax.persistence.TemporalType; /** * @author Manjunath Sampath * */ @Entity @DiscriminatorValue(value="CASH") public class CashPayment extends Payment implements Serializable { private static final long serialVersionUID = 4121977299960400816L; private Date cashDepositDate; @Temporal(TemporalType.DATE) @Column(name="CASH_DEPOSIT_DATE") public Date getCashDepositDate() { return cashDepositDate; } public void setCashDepositDate(Date cashDepositDate) { this.cashDepositDate = cashDepositDate; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.heapcode.jpa.entity; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.Temporal; import javax.persistence.TemporalType; /** * @author Manjunath Sampath * */ @Entity @DiscriminatorValue(value="CHEQUE") public class ChequePayment extends Payment implements Serializable { private static final long serialVersionUID = 7937552346048391271L; private String chequeNo; private Date chequeDate; @Column(name="CHEQUE_NO",length=45) public String getChequeNo() { return chequeNo; } public void setChequeNo(String chequeNo) { this.chequeNo = chequeNo; } @Temporal(TemporalType.DATE) @Column(name="CHEQUE_DATE") public Date getChequeDate() { return chequeDate; } public void setChequeDate(Date chequeDate) { this.chequeDate = chequeDate; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.heapcode.jpa.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; /** * @author Manjunath Sampath * */ @Entity @DiscriminatorValue(value="ONLINE") public class OnlineTransfer extends Payment implements Serializable { private static final long serialVersionUID = 3106562148880925301L; private String transactionNo; @Column(name="TRANSACTION_NO",length=45) public String getTransactionNo() { return transactionNo; } public void setTransactionNo(String transactionNo) { this.transactionNo = transactionNo; } } |
@Inheritance:
In JPA for implementing Inheritance @Inheritance annotation is used.It defines inheritance strategy to be implement for entity class hierarchy.For one table per class hierarchy,we have used SINGLE_TABLE as inheritance strategy.This annotation is defined at Root Class where different strategy is to be applied.
@DiscriminatorColumn:
This annotation is used to define discriminator column for SINGLE_TABLE and joined strategy.It is used to distinguish between different class instances.This annotation is defined at Root Class and Sub Class where inheritance Strategy is to be applied. If @DiscriminatorColumn annotation is not specified,then hibernate will create a column named as ‘DType’ and DiscriminatorType will be string.
@DiscriminatorValue:
This annotation defines value in discriminator column for that class.For example,If Entity will be of Payment instance, in PAYMENT Table then ‘OTHERS’ will be value for that row in discriminator column. If Entity of CashPayment is used then “CASH” will be used for this column and so on for others.
7. JPA Configuration
Below is the JPA Configuration File persistence.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="HEAPCODE" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>com.heapcode.jpa.entity.Payment</class> <class>com.heapcode.jpa.entity.CashPayment</class> <class>com.heapcode.jpa.entity.ChequePayment</class> <class>com.heapcode.jpa.entity.OnlineTransfer</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/> <property name="hibernate.configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> <property name="hibernate.bytecode.use_reflection_optimizer" value="true"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.password" value="password" /> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/heapcode" /> <property name="hibernate.connection.username" value="root" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.max_fetch_depth" value="3"/> </properties> </persistence-unit> </persistence> |
7. Main Program
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.heapcode.jpa.main; import java.math.BigDecimal; import java.util.Date; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import com.heapcode.jpa.entity.CashPayment; import com.heapcode.jpa.entity.ChequePayment; import com.heapcode.jpa.entity.OnlineTransfer; import com.heapcode.jpa.entity.Payment; /** * @author Manjunath Sampath * */ public class JPAMain { public static void main( String[] args ){ EntityManagerFactory emf = Persistence.createEntityManagerFactory("HEAPCODE"); EntityManager em = emf.createEntityManager(); Payment payment = new Payment(); payment.setAmount(new BigDecimal(122.89)); CashPayment cashPayment = new CashPayment(); cashPayment.setAmount(new BigDecimal(872.98)); cashPayment.setCashDepositDate(new Date()); ChequePayment chequePayment = new ChequePayment(); chequePayment.setAmount(new BigDecimal(456.98)); chequePayment.setChequeNo("BCGD93939"); chequePayment.setChequeDate(new Date()); OnlineTransfer onlineTransfer = new OnlineTransfer(); onlineTransfer.setAmount(new BigDecimal(342.22)); onlineTransfer.setTransactionNo("NEFT22200222"); EntityTransaction tx = null; try{ tx = em.getTransaction(); tx.begin(); em.persist(payment); em.persist(cashPayment); em.persist(chequePayment); em.persist(onlineTransfer); tx.commit(); }catch(RuntimeException re){ tx.rollback(); re.printStackTrace(); } em.close(); emf.close(); } } |
8. Output
From the above Console Output we could see that irrespective of whatever class we persist all the data is inserted into PAYMENT Table.
9. Project Structure
Download JPA Inheritance Table per Class Hierarchy Example
I hope this has been useful for you and I’d like to thank you for reading. If you like this article, please leave a helpful comment and share it with your friends.