Eclipselink / JPA – Abstract Entity And Non-Entity Classes In The Entity Inheritance

When we’ve discussed the inheritance and mapped superclasses concepts in the previous tutorials, we are avoiding concepts that could confuse the readers or makes misunderstanding for them.

Most of the clarified examples didn’t provide an Abstract Entity as a target entities that can be managed by the entity manager. On the contrary an Abstract Entity was only used in the tutorial of mapped superclass (See MappedSuperclass tutorial) in that the mapped superclass provides its children capability to inherit both of persistent entity states and the mapping information that belongs to. However, this isn’t the only concept that we would to discuss at this tutorial, but also we are going to discuss a non-entity class lies at the middle of the inheritance hierarchy for managed entities.

These relations might cause a confusion if they were coming in the middle of explanation. In this tutorial concepts like Abstract Entity, non-Entity class and Entity class will be used intensively.

Managed Entity

A managed entity instance is an instance with a persistent identity that’s currently associated with a persistent context. Meaning of managed entity is the contrary of Detached Entity, detached entity instance is an instance with a persistent identity that’s not (or no longer) associated with the persistent context. It is the basic term often used with the persistence technologies.

When an entity is being managed ?

In a brief we can define that, an entity is being managed by entity manager when it’s persisted to the database via an EntityManager’s persist method, which must be invoked within an active transaction. Also Entity objects retrieved from the database by an entity manager are also in the managed state, and if a managed entity instance is modified within an active transaction the changes that’s happened at that instance will be persisted into database once the transaction has been committed. for more information follow this link EntityManager.

Abstract Entity

An abstract entity can be specified as an entity, an abstract entity differs from a concrete entity only in that it cannot be directly instantiated. An abstract entity is mapped as an entity and can be the target of the queries (which will operate over and/or retrieve of its concrete sub-classes).

By returning to previous inheritance examples that implemented before, and by changing the Project entity from a normal entity into Abstract Entity. See Figure 1.1 that shows you the classes design for the whole system that’s being implemented.

Note : The examples in this tutorials are continued from the previous tutorials. For the better understanding of whole code, please read the previous tutorials (EclipseLink Tutorials).

ClassesDesign

Figure 1.1

  • The Project class displayed in an italic form. UML Design shows a class name by using an italic form if it was an abstract.
  • The GlobalProject and LocalProject are classes inherit from Project abstract entity.
  • Project is an abstract class, so it cannot be instantiated anymore.
  • The database can contains two types of Project instances, one for GlobalProject and the second for LocalProject. So no way to have a managed entity instance of type Project and consequently, the Project instance couldn’t be used for query results.

Anatomy of Project Abstract Entities Inheritance Hierarchy

Project.java

package net.javabeat.eclipselink.data;

import java.util.List;

import javax.persistence.CascadeType;
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.TABLE_PER_CLASS) // The same inheritance strategy that is used
public abstract 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.Entity;

@Entity
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.Entity;

@Entity
public class LocalProject extends Project {
 private BigDecimal projectBudget;

public BigDecimal getProjectBudget() {
 return projectBudget;
 }

public void setProjectBudget(BigDecimal projectBudget) {
 this.projectBudget = projectBudget;
 }

}

  • Although Project class is an abstract entity, but it contains an persistent entity states and mapping information.
  • The LocalPorject and GlobalProject are sub-classes from Project and they are inherit the persistent entity states and mapping information.
  • The strategy used for achieving the inheritance is Table Per Concrete Strategy.

Required Persistence Configuration


<?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>

<!-- Project Inheritance Hierarchy Entities-->
 <class>net.javabeat.eclipselink.data.Project</class>
 <class>net.javabeat.eclipselink.data.GlobalProject</class>
 <class>net.javabeat.eclipselink.data.LocalProject</class>

<!-- End -->
 <class>net.javabeat.eclipselink.data.License</class>
 <class>net.javabeat.eclipselink.data.DriverLicense</class>
 <class>net.javabeat.eclipselink.data.ICDLComputerLicense</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="FINEST"/>
 </properties>
 </persistence-unit>
</persistence>

Executable Application

The following JPAImpl class should show you how can we achieve a persistent operation for Project inheritance hierarchy.

JPAImp.java


package net.javabeat.eclipselink;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import net.javabeat.eclipselink.data.Address;
import net.javabeat.eclipselink.data.Developer;
import net.javabeat.eclipselink.data.DriverLicense;
import net.javabeat.eclipselink.data.Employee;
import net.javabeat.eclipselink.data.GlobalProject;
import net.javabeat.eclipselink.data.ICDLComputerLicense;
import net.javabeat.eclipselink.data.LocalProject;
import net.javabeat.eclipselink.data.Phone;
import net.javabeat.eclipselink.data.Project;

public class JPAImpl {
 static EntityManagerFactory factory = null;
 static EntityManager em = null;
 static {
 factory = Persistence.createEntityManagerFactory("EclipseLink-JPA-Installation");
 em = factory.createEntityManager();
 }
 public static void main(String [] args){
 // Begin a Transaction
 em.getTransaction().begin();
 // Create Employee
 createDeveloper();
 // Commit
 em.getTransaction().commit();

}</pre>
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);

 // Project is an abstract entity class, no way to instantiate it
 // Project project = new Project(); // That's getting compiler error

 // 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>();

 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.2 shows you, that how the project abstract entity sub-classes are persisted at the database, although the Project itself isn’t persisted.

Project

GlobalProject

LocalProject

Figure 1.2

  • The Project abstract entity does provides a persistent entity states and mapping information and it’s capable of inherit them into its sub-classes.
  • No records has been persisted for project abstract entity into database, although the GlobalProject and localProject are inserted.
  • The Table Per Concrete Strategy provide separate table for every sub-class of Project.
  • In case, we are using Single Table Strategy or Joined Table, the project database will contains those common attributes that shared between Project and its sub-classes.

Non-Entity Inherit from Entity & Entity Inherit from Non-Entity

Let’s have the a new additional classes added to our classes design listed above, in that the Employee is a root of inheritance tree. A Developer is a direct sub-class for Employee. Meanwhile the Developer has two sub-classes (ContractDeveloper and FreelanceDeveloper).

Employee is an entity, but Developer isn’t, the Developer is a mapped superclass (Not managed at all). ContractDeveloper and FreelanceDeveloper are another entities.

Developer.java


package net.javabeat.eclipselink.data;

import javax.persistence.MappedSuperclass;

// Developer extends Employee entity, but it's annotated using @MappedSuperclass
@MappedSuperclass
public class Developer extends Employee{

 private String title;

public String getTitle() {
 return title;
 }

public void setTitle(String title) {
 this.title = title;
 }

}

ContractDeveloper.java


package net.javabeat.eclipselink.data;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CDEV")
public class ContractDeveloper extends Developer {

}

FreelanceDeveloper.java


package net.javabeat.eclipselink.data;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="FDEV")
public class FreelanceDeveloper extends Developer {

}

  • You should add the ContractDeveloper and FreelanceDeveloper into above persistence.xml file.
  • The Developer is a mappedSuperclass, so it’s just a way to share the persistent entity states and mapping information that inherit them from the Employee plus those added inside it to its sub-classes.

Executable Application for persisting new Sub-classes


package net.javabeat.eclipselink;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import net.javabeat.eclipselink.data.Address;
import net.javabeat.eclipselink.data.ContractDeveloper;
import net.javabeat.eclipselink.data.Developer;
import net.javabeat.eclipselink.data.DriverLicense;
import net.javabeat.eclipselink.data.Employee;
import net.javabeat.eclipselink.data.FreelanceDeveloper;
import net.javabeat.eclipselink.data.GlobalProject;
import net.javabeat.eclipselink.data.ICDLComputerLicense;
import net.javabeat.eclipselink.data.LocalProject;
import net.javabeat.eclipselink.data.Phone;
import net.javabeat.eclipselink.data.Project;

public class JPAImpl {
 static EntityManagerFactory factory = null;
 static EntityManager em = null;
 static {
 factory = Persistence.createEntityManagerFactory("EclipseLink-JPA-Installation");
 em = factory.createEntityManager();
 }
 public static void main(String [] args){
 // Begin a Transaction
 em.getTransaction().begin();
 // Create Freelance Developer
 createFreelanceDeveloper();
 // create Contract Developer
 createContractDeveloper();
 // Commit
 em.getTransaction().commit();

}

 public static void createFreelanceDeveloper(){
 // 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 FreelanceDeveloper();
 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 Global Project
 GlobalProject globalProject = new GlobalProject();
 globalProject.setProjectId(1);
 globalProject.setProjectName("Global Project");
 globalProject.setProjectCountry("Brazil");
 globalProject.setProjectBudget(new BigDecimal(150000));

 // Create a Local Project
 LocalProject localProject = new LocalProject();
 localProject.setProjectId(2);
 localProject.setProjectName("Local Project");
 localProject.setProjectBudget(new BigDecimal(50000));

 // Create a list of projects
 List<Project> projects = new ArrayList<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);

 }

 public static void createContractDeveloper(){
 // Create an address entity
 Address address = new Address();
 address.setAddressId(2);
 address.setAddressCountry("United Kingdom");
 address.setAddressCity("London");
 // Create an employee entity
 Developer developer = new ContractDeveloper();
 developer.setEmployeeId(2);
 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(2);
 firstPhone.setPhoneNumber("+221 4050 615");
 firstPhone.setEmployee(developer);

 // Create a list of phone
 List<Phone> phones = new ArrayList<Phone>();
 phones.add(firstPhone);

 // Create a Global Project
 GlobalProject globalProject = new GlobalProject();
 globalProject.setProjectId(3);
 globalProject.setProjectName("Global Project");
 globalProject.setProjectCountry("Brazil");
 globalProject.setProjectBudget(new BigDecimal(150000));

 // Create a Local Project
 LocalProject localProject = new LocalProject();
 localProject.setProjectId(4);
 localProject.setProjectName("Local Project");
 localProject.setProjectBudget(new BigDecimal(50000));

 // Create a list of projects
 List<Project> projects = new ArrayList<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);

 }

}

  • If you’ve been trying to persist a Developer instance, you are almost getting an exception tells you that the Developer isn’t a known entity even it’s not an abstract.
  • The ContractDeveloper and FreelanceDeveloper is the only developer instances that could persisted.
  • The ContractorDeveloper and FreelanceDeveloper should provide their discriminator values.

Database Persisted Records

Figure 1.3 depicts you the records that inserted.

Employees

Figure 1.3

  • The Freelance and contractor developer has been inserted by using the persistent entity states which inherited from the Developer. However, the Developer mapped superclass by its turn inherit some persistent states and mapping information from the Employee.

What’s happened if we’ve created a Simple Developer class?

Let’s create a simple Developer class (i.e. not mapped superclass).

Developer.java


package net.javabeat.eclipselink.data;
public class Developer extends Employee{
private String title;
public String getTitle() {
 return title;
 }
public void setTitle(String title) {
 this.title = title;
 }
}

  • The executable application mentioned above executes successfully.

Impact of using Entity and non-Entity on The Entity Manager for Querying

What’s heppened if you’ve used a sample query code like

em.createQuery("select d from Developer d"); // Developer non-entity
em.createQuery("select p from Project p"); // Project Entity
  • The first query that used in the sample would be thrown an exception, cause the Developer isn’t an entity even if we’ve replaced the normal class to be mapped superclass. The Developer isn’t queryable and cannot be passed as an argument to EntityManager, also it couldn’t be a target of a persistent relationship.
  • The second query should executed successfully cause the Project is an entity, so it’s queryable and can be passed into EntityManager and it could be a target for persistent relationship.

Summary

What we should do if we have different type of classes in the same inheritance hierarchy? is JPA support that’s illusion between those entities and non-entities? This tutorial summarizes a different scenarios that could happen in the inheritance hierarchy, you’ve seen how can non-entity class inherit from an entity and vice versa. Also if you’ve decided to avoid the mapped superclass you are able to complete your inheritance hierarchy. Also, this tutorial gives you an examples of how we could differentiate between entity and non-entity once we are coming to make a query. The entity is queryable rather using of mappedsuper class or normal class.

Comments

comments

About Amr Mohammed

Speak Your Mind

*