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).
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.
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.
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.