This tutorial will explain the using of @MappedSuperclass, that could be considered as an alternative choice for sharing a persistent entity state and mapping information. An entity may inherit from a super class that provides a persistent entity state and mapping information as we’ve seen previously in the @Inheritance tutorials. Unlike using of @Inheritance, the @MappedSuperclass doesn’t involve in any persistent activity and it doesn’t involve in any operations that managed by the EntityManager. The main purpose of using a @MappedSuperclass is to define a common states and mapping information that could be shared by multiple entity classes.
A @MappedSuperclass annotated class is not persisted itself, but the sub-classes are persistent. So, this annotation is used for an abstract class or a non-persistent class. The equivalent XML element is mapped-superclass in the persistent configuration file.
Some of the characteristics of @MappedSuperclass is:
- Normally class is abstract, but it never be a persistence instance
- Mapped superclasses can not define a table, but can define mapping for attributes.
- This annotation doesn’t have any attributes.
Classes Design
Let’s see the classes design that would be formed the field MappedSuperclass annotation could provide a help inside it. See figure 1.1.
Figure 1.1
The License class is an abstract class contains one attribute named licenseId. Set of classes have a chance to inherit from License abstract class, but because of we are not aware of the number of classes that may inherit it we’ve denoted for any future coming classes with (AnyClass extends License).
We are supposed to have a new class called DriverLicense as an extended class and you will see it in the next section.
License.java
package net.javabeat.eclipselink.data; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class License { @Id protected int licenseId; public int getLicenseId() { return licenseId; } public void setLicenseId(int licenseId) { this.licenseId = licenseId; } }
DriverLicense.java
package net.javabeat.eclipselink.data; import java.util.Date; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @Entity // Annotate the DriverLicense as an Entity that must be persisted by the JPA @Table(name="driverlicenses") // The Table name that the instance of DriverLicense should be persisted there public class DriverLicense extends License{ // DriverLicense Inherited protected int private String driverLicenseName; @Temporal(TemporalType.DATE) // The temporal is mandatory when we are coming to deal with Date private Date driverLicenseExpiryDate; @Temporal(TemporalType.DATE) private Date driverLicenseIssueDate; @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name="employeeId") private Employee employee; public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public String getDriverLicenseName() { return driverLicenseName; } public void setDriverLicenseName(String driverLicenseName) { this.driverLicenseName = driverLicenseName; } public Date getDriverLicenseExpiryDate() { return driverLicenseExpiryDate; } public void setDriverLicenseExpiryDate(Date driverLicenseExpiryDate) { this.driverLicenseExpiryDate = driverLicenseExpiryDate; } public Date getDriverLicenseIssueDate() { return driverLicenseIssueDate; } public void setDriverLicenseIssueDate(Date driverLicenseIssueDate) { this.driverLicenseIssueDate = driverLicenseIssueDate; } }
Database Design
The License class annotated with MappedSuperclass, so no need for creating any database table for it, meanwhile and at the other side, any entities that inherit from License class and not annotated using MappedSuperclass need a database mapping table. See Figure 1.2 that shows you the final view for the database tables.
Figure 1.2
Notes:
- The License class doesn’t consider as a persisted entity, cause it’s annotated as a MappedSuperclass.
- The DriverLicense class considered as an entity, cause it’s annotated as @Entity and it’s mapped into DriverLicense table.
- The DriverLicense inherit a licenseId attribute from the License MappedSuperclass.
- The DriverLicense can override any inherited attributes or associations using @AttributeOverride and @AssociationOverride.
- The DriverLicense associate the Employee with an OneToMany unidirectional association.
- The DriverLicense table should hold all attributes that are defined by the entity and its MappedSuperclass (licenseId).
Persistence Configuration
The persistence.xml should mention the mapped sueprclass and its sub classes. If you’ve omit the License mapped superclass you are almost getting a compiler error tells you (The License is managed but is not listed in the persistence.xml file). Even if the License will not be part of any EntityManager’s operation, but it should be declared there for making the mapping sharing possible.
<?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"> <!-- The entities mentioned here covers previous examples --> <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> <!-- License & DriverLicense are required classes for this example --> <class>net.javabeat.eclipselink.data.License</class> <class>net.javabeat.eclipselink.data.DriverLicense</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
Look at the Java code that runs a JPA persistence for persisting a DriverLicense associated with an Employee.
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.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 createEmployee(); // Commit em.getTransaction().commit(); // Begin another Transaction em.getTransaction().begin(); // Find the Employee Employee employee = em.find(Employee.class, 2); // Create a Driver License createDriverLicense(employee); // Commit em.getTransaction().commit(); } public static void createDriverLicense(Employee employee){ DriverLicense license = new DriverLicense(); // Create a driver license license.setLicenseId(1);// Set license id license.setDriverLicenseName("All Vehicles License"); // Set License Name license.setDriverLicenseIssueDate(new Date()); // Anonymous date license.setDriverLicenseExpiryDate(new Date()); // Anonymous date license.setEmployee(employee); em.persist(license); } public static void createEmployee(){ // Create an address entity Address address = new Address(); address.setAddressId(2); address.setAddressCountry("United Kingdom"); address.setAddressCity("London"); // Create an employee entity Employee employee = new Employee(); employee.setEmployeeId(2); employee.setEmployeeName("John Smith"); // Associate the address with the employee employee.setAddress(address); // Create a Phone entity Phone firstPhone = new Phone(); firstPhone.setPhoneId(3); firstPhone.setPhoneNumber("+221 4050 615"); firstPhone.setEmployee(employee); // Create a new phone entity Phone secondPhone = new Phone(); secondPhone.setPhoneId(4); secondPhone.setPhoneNumber("+221 4050 619"); // Use the old employee entity secondPhone.setEmployee(employee); // 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(2); 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 employee.setProjects(projects); // Set the phones into your employee employee.setPhones(phones); // Persist the employee em.persist(employee); } }
Summary
This tutorial introduced a new annotation for sharing a mapping information rather using @Inheritance. @MappedSuperclass used to annotate a class whose instance won’t be persisted or participate in any persisting operation, while its sub classes will do. In the previous examples we’ve seen a License class that annotated with an @MappedSuperclass, even if it has no abstract non-access modifier, it’s will not considered in a any future persisting operation. However, all sub classes that inherit from the License and doesn’t marked as @MappedSuperclass will participate in the persisting operations such DriverLicense. Remember that – The main goal of @MappedSuperclass is to share the mapping information with the sub classes.