In the Java Persistence API (JPA) The entities support inheritance, polymorphic, polymorphic associations, and polymorphic queries. Both abstract and concrete classes can be entities, and both of them can be annotated with the @Entity annotation, mapped as entities and queried for as entities.
Entities can extend non-entity classes and non-entity classes can extend entity classes, so you will find samples for entities classes inherit from a non-entity classes and non-classes entities inherit from entities classes. The purpose of this tutorial is to cover the inheritance in an entity class hierarchy; which means that the super class and its sub classes will be marked as entities.
The Mapping of class hierarchies has different implementation techniques, that’s depends on the Java Persistence API (JPA) vendor. It is important to know that this tutorial consider the EclipseLink – JPA as a JPA provider, so be sure that you are willing enough to use the EclipseLink.(See EclipseLink Maven Installation and EclipseLink Installation).
To implement an inheritance you’ve different implementations that might be used:
Single Table Inheritance Strategy:
In the single table inheritance, the entire hierarchy is represented by a single table, in that the entity of super class defines set of mapped states and the entity of subclass inherits most of its mapped states from its super class entity.The discriminator column (TYPE) is added to the table to distinguish between the stored instances. That’s column will determine what class in the hierarchy the stored object belongs to. Also, additional columns will be added into that table for those newly defined attributes in the sub classes entities.
Joined Table Inheritance Strategy:
In the joined table inheritance, each class shares data from the root table. In addition, each subclass defines its own table that adds its extended state. Where the shared data is stored in a single table while the newly defined fields are stored in separate table.
Table per Concrete Strategy:
This strategy is an optional, based on the specification “Support for the table per concrete class inheritance mapping strategy is optional”. In table per class inheritance a table is defined for each concrete class in the inheritance hierarchy to store all the attributes of that class and all of its super classes.
At this tutorial, we will cover the Joined Table Strategy for achieving the inheritance mapping, the next coming tutorial shall cover other strategies.
Anatomy of Inheritance Hierarchy (Classes Design)
Let’s assume an inheritance hierarchy, in that Project entity represents the root of it and set of entities inherit the Project like GlobalProject and LocalProject entities.
Let’s see a class design for the suggested inheritance relationship at the Figure 1.1:
Figure 1.1
As you’ve noticed in the Figure 1.1, the Project entity does associate another entities, but it’s now has a special relationship with the either GlobalProject and LocalProject. Both of GlobalProject and LocalProject entities (Is-A) Project, and once we are coming to persist both of them, we are doing a persist for Project and its children.
The figure 1.1 shows you a different kinds of association that already made before, so refer to @OneTOne, @OneToMany and @ManyToMany to see more details about achieved associations that depicted at the Figure 1.1.
Anatomy of Database Design
If you’ve reviewed the Single Table Strategy, then you’ve surely seen all classes hierarchy are persisted at the same table. Joined Table Strategy is the most logical inheritance solution because it reflects the object model while persistence. In joined inheritance a table is defined for each class in the inheritance hierarchy to store only the local attributes of that class. Figure 1.2, Figure 1.3 and Figure 1.4 depicts the required tables.
Figure 1.2
Figure 1.3
Figure 1.4
For achieving the Join Inheritance you should follow the given steps below:
- As you’ve noticed the project, globalproject and localproject represents the the Project, GlobalProject and LocalProject respectively.
- The project table contains a new mandatory column for achieving the inheritance, it is a ProjectType column. That column will be used as a Discriminator Value. The Discriminator column will be used to determine which the class that the row inserted belongs to.
- Unlike a Single Table Strategy, the globalproject and localproject tables contains the same primary key that’s used by the project table.
- The sub classes tables contains their columns that represents the sub classes attributes.
How Should The Project, GlobalProject and LocalProject Looks Like?
// Project Class package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.ManyToMany; @Entity(name="Project") @Inheritance(strategy=InheritanceType.JOINED) @DiscriminatorColumn(name="ProjectType") @DiscriminatorValue(value="Proj") public class Project { @Id private int projectId; private String projectName; @ManyToMany(mappedBy="projects",cascade=CascadeType.ALL) private List<Employee> employees; public int getProjectId() { return projectId; } public void setProjectId(int projectId) { this.projectId = projectId; } public String getProjectName() { return projectName; } public void setProjectName(String projectName) { this.projectName = projectName; } public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
GlobalProject.java
package net.javabeat.eclipselink.data; import java.math.BigDecimal; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @Entity @DiscriminatorValue(value="GlobProj") public class GlobalProject extends Project { private String projectCountry; private BigDecimal projectBudget; public String getProjectCountry() { return projectCountry; } public void setProjectCountry(String projectCountry) { this.projectCountry = projectCountry; } public BigDecimal getProjectBudget() { return projectBudget; } public void setProjectBudget(BigDecimal projectBudget) { this.projectBudget = projectBudget; } }
LocalProject.java
package net.javabeat.eclipselink.data; import java.math.BigDecimal; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @Entity @DiscriminatorValue(value="LocProj") public class LocalProject extends Project { private BigDecimal projectBudget; public BigDecimal getProjectBudget() { return projectBudget; } public void setProjectBudget(BigDecimal projectBudget) { this.projectBudget = projectBudget; } }
Notice the following points from the classes list above:
- The Project class represents the root of the inheritance tree.
- The Project class should contains the @Inheritance annotation.
- Unlike Single Table Inheritance, Join Inheritance uses the strategy of JOINED.
- The Project class contains the @DiscriminatorColumn annotation.
- The Project class specifies its DiscriminatorValue via @DiscriminatorValue annotation.
- All sub classes of Project class should provid their DiscriminatorValue through using of @DiscriminatorValue.
- All sub classes of Project class is an entity bounded directly into their tables. That’s bind should be done automatically via the JPA implementation.
- Be careful when you depends on the JPA implementation for naming the tables that aren’t mentioned explicitly through @Table.
Persistence Configuration Required
Nothing special that must be used for inheritance relationship, except that we should mention the entities that could be persisted into the persistence.xml.
</pre> <?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="EclipseLink-JPA-Installation" transaction-type="RESOURCE_LOCAL"> <class>net.javabeat.eclipselink.data.Employee</class> <class>net.javabeat.eclipselink.data.Developer</class> <class>net.javabeat.eclipselink.data.Address</class> <class>net.javabeat.eclipselink.data.Phone</class> <class>net.javabeat.eclipselink.data.Project</class> <class>net.javabeat.eclipselink.data.GlobalProject</class> <class>net.javabeat.eclipselink.data.LocalProject</class> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/JavaBeat"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.password" value="root"/> <property name="eclipselink.logging.level" value="OFF"/> </properties> </persistence-unit> </persistence> <pre>
Executable Application
For adding a new developer, project, global project and local project you could use the following snippet of code:
public static void main(String [] args){ em.getTransaction().begin(); createDeveloper(); em.getTransaction().commit(); } public static void createDeveloper(){ // Create an address entity Address address = new Address(); address.setAddressId(1); address.setAddressCountry("United Kingdom"); address.setAddressCity("London"); // Create an employee entity Developer developer = new Developer(); developer.setEmployeeId(1); developer.setEmployeeName("John Smith"); developer.setTitle("Senior Java Developer"); // Associate the address with the employee developer.setAddress(address); // Create a Phone entity Phone firstPhone = new Phone(); firstPhone.setPhoneId(1); firstPhone.setPhoneNumber("+221 4050 615"); firstPhone.setEmployee(developer); // Create a list of phone List<Phone> phones = new ArrayList<Phone>(); phones.add(firstPhone); // Create a Project entity Project project = new Project(); project.setProjectId(1); project.setProjectName("Project"); // Create a Global Project GlobalProject globalProject = new GlobalProject(); globalProject.setProjectId(2); globalProject.setProjectName("Global Project"); globalProject.setProjectCountry("Brazil"); globalProject.setProjectBudget(new BigDecimal(150000)); // Create a Local Project LocalProject localProject = new LocalProject(); localProject.setProjectId(3); localProject.setProjectName("Local Project"); localProject.setProjectBudget(new BigDecimal(50000)); // Create a list of projects List<Project> projects = new ArrayList<Project>(); // add the project into the list projects.add(project); projects.add(globalProject); projects.add(localProject); // Set the project into employee developer.setProjects(projects); // Set the phones into your employee developer.setPhones(phones); // Persist the employee em.persist(developer); }
Database Persisted Records
The Figure 1.5, Figure 1.6 and Figure 1.7 will show you the expected results that achieved by using Join Inheritance.
Figure 1.5
Figure 1.6
Figure 1.7
Summary
This tutorial covers the @Inheritance annotation and its used for achieving an inheritance relationship. Already we’ve established a Project entity and both of GlobalProject and LocalProject inherit it. When you are coming into inheritance, you have multiple choices to implement it. One of the major strategies that could be used is a Join Table Strategy which in all classes in the hierarchy persisted in their own tables.