Hibernate/JPA Joined Table Inheritance Example
In this tutorial, we show how to map entity hierarchies onto database tables. There are several ways of mapping inheritance to the database. Here, we’ll look into Hibernate/JPA joined table inheritance.
The base class and all the subclasses have their own database table. Fetching a subclass entity requires a join with the parent table as well.
The JOINED table inheritance strategy addresses the data integrity concerns because every subclass is associated with a different table. Polymorphic queries or @OneToMany base class associations don’t perform very well with this strategy. However, polymorphic @ManyToOne associations are fine, and they can provide a lot of value.
Maven Dependencies
We use Apache Maven to manage the projects dependencies.
<?xml version="1.0" encoding="UTF-8"?> <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.memorynotfound.db.hibernate.configuration</groupId> <artifactId>entity-manager</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>war</packaging> <name>HIBERNATE - ${project.artifactId}</name> <url>https://memorynotfound.com</url> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.4</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.3.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.2.3.Final</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>
Creating Models + Hibernate/JPA Mappings
Each subclass is mapped to its own table. This is also called table-per-subclass mapping strategy. An inherited state is retrieved by joining with the table of the superclass. A discriminator column is not required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier.
The inheritance strategy is defined on the abstract super class, using the @Inheritance annotation. In this example, we used InheritanceType.JOINED. This means, all concrete subclasses and superclass will be stored their own table. You can optionally specify a discriminator column name. This column is registered by the @DiscriminatorColumn, if omitted no default is used.
package com.memorynotfound.hibernate; import javax.persistence.*; @Entity @Inheritance(strategy = InheritanceType.JOINED) public abstract class Shape { @Id @Column(name = "vehicle_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; }
package com.memorynotfound.hibernate; import javax.persistence.Entity; @Entity public class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } @Override public String toString() { return "Circle{" + "radius=" + radius + '}'; } }
The primary key of the concrete subclass is also a foreign key to the superclass table. If the @PrimaryKeyJoinColumn is not set, the primary key / foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.
package com.memorynotfound.hibernate; import javax.persistence.Entity; @Entity public class Rectangle extends Shape { private double width; private double length; public Rectangle(double width, double length) { this.width = width; this.length = length; } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } @Override public String toString() { return "Rectangle{" + "width=" + width + ", length=" + length + '}'; } }
If you prefer XML over Annotations, you can use the equivalent JPA XML mapping. This file is located in the src/main/resources/META-INF folder and is named orm.xml.
<?xml version="1.0" encoding="UTF-8" ?> <entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_0.xsd" version="2.0"> <!-- abstract shape --> <entity class="com.memorynotfound.hibernate.Shape"> <inheritance strategy="JOINED"/> <attributes> <id name="id"> <generated-value strategy="IDENTITY"/> <column name="shape_id"/> </id> </attributes> </entity> <!-- circle concrete subclass --> <entity class="com.memorynotfound.hibernate.Circle"> <attributes> <basic name="radius"/> </attributes> </entity> <!-- rectangle concrete subclass --> <entity class="com.memorynotfound.hibernate.Rectangle"> <attributes> <basic name="length"/> <basic name="width"/> </attributes> </entity> </entity-mappings>
Hibernate/JPA Configuration
We configure the JPA Persistence Unit using the persistence.xml file, which is located in the src/main/resources/META-INF directory.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="mnf-pu" transaction-type="RESOURCE_LOCAL"> <mapping-file>META-INF/orm.xml</mapping-file> <properties> <!-- Configuring JDBC properties --> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memorynotfound?serverTimezone=Europe/Brussels"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.password" value=""/> <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/> <!-- Hibernate properties --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.show_sql" value="true"/> </properties> </persistence-unit> </persistence>
Create, Run and Test application
package com.memorynotfound.hibernate; import javax.persistence.*; public class App { public static void main (String...args) throws InterruptedException { EntityManagerFactory emf = Persistence.createEntityManagerFactory("mnf-pu"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Circle circle = new Circle(13.05); em.persist(circle); em.getTransaction().commit(); em.getTransaction().begin(); Rectangle rectangle = new Rectangle(5.02, 10.45); em.persist(rectangle); em.getTransaction().commit(); Listaccounts = em .createQuery("select a from Shape a") .getResultList(); emf.close(); } }
Since we used the JOINED inheritance strategy, all subclasses and superclass have their own table.
create table Circle ( radius double precision not null, shape_id integer not null, primary key (shape_id) ) create table Rectangle ( length double precision not null, width double precision not null, shape_id integer not null, primary key (shape_id) ) create table Shape ( shape_id integer not null auto_increment, primary key (shape_id) ) alter table Circle add constraint FK2nshngrop6dt5amv1egecvdnn foreign key (shape_id) references Shape (shape_id) alter table Rectangle add constraint FKh3gkuyk86e8sfl6ilsulitcm5 foreign key (shape_id) references Shape (shape_id)
Here is the SQL INSERT statement.
insert into Shape values( ) insert into Circle (radius, shape_id) values (?, ?) insert into Shape values ( ) insert into Rectangle (length, width, shape_id) values (?, ?, ?)
When using polymorphic queries, the base class table must be joined with all subclass tables to fetch every associated subclass instance. The joined table inheritance polymorphic queries can use several JOINS which might affect performance when fetching a large number of entities.
select shape0_.shape_id as shape_id1_2_, shape0_1_.radius as radius1_0_, shape0_2_.length as length1_1_, shape0_2_.width as width2_1_, case when shape0_1_.shape_id is not null then 1 when shape0_2_.shape_id is not null then 2 when shape0_.shape_id is not null then 0 end as clazz_ from Shape shape0_ left outer join Circle shape0_1_ on shape0_.shape_id=shape0_1_.shape_id left outer join Rectangle shape0_2_ on shape0_.shape_id=shape0_2_.shape_id
Downloads
From:一号门
COMMENTS