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 Single 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 Employee entity represents the root of the tree and set of entities inherit the Employee like Developer entity. 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 Employee entity does associate another entities, but it’s now has a special relationship with the Developer. The Developer entity (Is-An) Employee, and once we are coming to persist the Developer, we are really persisting both of employee and developer states.
The figure 1.1 shows you a different kinds of association that already made before at this series of tutorial, so refer to @OneTOne, @OneToMany and @ManyToMany to see the whole associations that already made.
Anatomy of Inheritance Hierarchy (Database Design)
As you’ve mentioned before, the inheritance implementation that would be discussed here, it’s a Single Table Strategy, so you would see the Employee and Developer entities persisted into one Table called (Employee) as previous employee entity had done.
But when we are coming into implementation, we shall remember that the Single Table Strategy mapped all the classes in the hierarchy to a single table. That table has a column that serves as a (Discriminator column), that’s a column whose value identifies the specific subclass to which the instance that’s represented by the row belongs. Also, we should add a new column(s) for the those attributes defined in the sub classes, and not defined in the Employee itself. See Figure 1.2 that shows you what should Employee table looks like.
Figure 1.2
As you’ve noticed a new two columns are added, one for achieving the inheritance relationship and that’s discriminator column and the other is the title column that represent the developer subclass’s attribute.
Employee & Developer Entities
Let’s see what are the changes that should be made on the Employee entity for making an inheritance relation achievable.
// Employee Entity package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.Basic; 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.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) // The Inheritance annotation used for strategy determination @DiscriminatorColumn(name="EmployeeType") // Determine the discriminator column that will be used @DiscriminatorValue(value="EMP") // Determine the discriminator value that discriminator column should hold public class Employee { @Id private int employeeId; @Basic(optional=false) private String employeeName; @OneToMany(mappedBy="employee", cascade=CascadeType.ALL) private List<Phone> phones; @ManyToMany(cascade=CascadeType.ALL) @JoinTable( joinColumns={@JoinColumn(name="employeeId")}, inverseJoinColumns={@JoinColumn(name="projectId")} ) private List<Project> projects; @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name="AddressId") private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public List<Phone> getPhones() { return phones; } public void setPhones(List<Phone> phones) { this.phones = phones; } public List<Project> getProjects() { return projects; } public void setProjects(List<Project> projects) { this.projects = projects; } } // Developer Entity package net.javabeat.eclipselink.data; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @Entity @DiscriminatorValue(value="DEV") // Notice using of discriminatorValue again public class Developer extends Employee{ private String title; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
- As you’ve noticed at the entities above, the Employee uses an @Inheritance annotation that should be used to indicate a possibility existence for inheritance relationship.
- The Employee entity uses the @DiscriminatorColumn for specifying the column at the Employee table that being used for saving the suggested DiscriminatorValue.
- The Employee entity mentioned @DiscriminatorValue with an (EMP) value. That’s value will be used to indicate that the row is being persisted is an Employee instance.
- The Developer entity has no additional annotations that could be used with it for achieving the inheritance, cause the Employee entity mentioned the required information for such that relation.
- The only one annotation that was used with the Developer entity is @DiscriminatorValue. That’s value will be used to indicate that the row is being persisted is a Developer instance.
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.
<?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> <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>
Executable Application
For adding a new developer 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 new phone entity Phone secondPhone = new Phone(); secondPhone.setPhoneId(2); secondPhone.setPhoneNumber("+221 4050 619"); // Use the old employee entity secondPhone.setEmployee(developer); // Create a list of phone List<Phone> phones = new ArrayList<Phone>(); phones.add(firstPhone); phones.add(secondPhone); // Create a Project entity Project project = new Project(); project.setProjectId(1); project.setProjectName("Nasa Project"); // Create a list of projects List<Project> projects = new ArrayList<Project>(); // add the project into the list projects.add(project); // 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
Figure 1.3 shows you the record that have been persisted into database that represents the Developer instance.
Figure 1.3
Also, let’s see what if we are persisting an instance of Employee instance. See Figure 1.4
Figure 1.4
- Notice the impact of using the @DiscriminatorValue annotation inside the Employee and Developer on the records that are saved.
- Even though the title value has been mentioned beside the DEV employee, however the record of type EMP doesn’t have such that value. The Employee entity doesn’t have such that attribute.
Summary
This tutorial covers the @Inheritance annotation and its used for achieving an inheritance relationship. Already we’ve established an Employee entity and the Developer 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 Single Table Strategy which in all classes in the hierarchy tree are persisted in the same Table of the database.