EclipseLink / JPA Annotations – @AttributeOverride And @AssociationOverride

At this tutorial we’ll introduce using of @AttributeOverride and @AssocationOverride that mentioned intentionally at the @MappedSuperclass tutorial. As you’ve remembered at @MappedSuperclass annotation tutorial, we’ve mentioned at the Database Design section the possibility of using @AttributeOverride and @AssociationOverride to override the attributes and associations that inherited from the mapped superclass. An entity can inherit a persistent entity state and mapping information using different annotations, one of them was using of @MappedSuperclass, but even if you were achieving such that inheritance using @Inheritance or @MappedSuperclass annotations or by using an  abstract entity, you have sometimes to override a superclass’s attribute(s) or association(s) to be compliant the sub classes mapping. That’s what you are going to do; override the mapping information that inherited from the inheritance hierarchy.

Anatomy of @AttributeOverride Annotation

The @AttributeOverride annotation is used to override the mapping of property or field or Id property or field.

  • Target: Type, Method, Field
  • Uses: @AttributeOverride
  • Arguments: The @AttributeOverride annotation takes multiple arguments when it used and these are:
  1. name (Required): This argument specify the name of the property whose mapping is being overridden if property-based access is being used, or the name of the field if field-based access is used.
  2. column (Required): This argument specify the column that is being mapped to persistent attribute. The mapping type will remain the same as is defined in the embeddable class or mapped superclass.

Anatomy of @AssociationOverride Annotation

The @AssociationOverride annotation is used to override a many-to-one or one-to-one mapping of property or field for an entity relationship.

  • Target: Type, Method, Field
  • Uses: @AssociationOverride
  • Arguments: The @AssociationOverride annotation takes multiple arguments when it used and these are:
  1. name (Required): This argument specify the name of the relationship property whose mapping is being overridden if property-based access is being used, or the name of the relationship field if field-based access is used.
  2. joinColumns (Required): This argument specify the join column that is being mapped to the persistent attribute. The mapping type will remain the same as is defined in the mapped superclass.

Classes Design

Let’s see the design of classes that’s being used for defining a proper use of @AttributeOverride and @AssociationOverride annotations. See Figure 1.1.

ClassesDesign

Figure 1.1

Pay attention at the following notes:

  • The License entity does define a licenseId and employee as protected attributes.
  • The licenseId defines an attribute.
  • The employee defins as association.
  • DriverLicense entity defined as a sub-class of the License.
  • ICDLComputerLicense entity defined as a sub-class of the license.

Database Design

In the database design we’ve used two tables (cause the mapped superclass doesn’t refer any table) one for the DriverLicense entity and the second one for ICDComputerLicense. See Figure 1.2.

DriverLicenseTable

icdlLicenseTable

Figure 1.2

Pay attention at the following points:

  • The driverlicense table defines two major attributes, one for licenseId and the second one for association with the employee.
  • The icdlcomputerlicense defins two major attributes, one for licenseId called (icdlLicenseId) and the second for association with the employee called (icdlEmployeeId).
  • The columns in both tables are named differently cause such that case could lead us to use @AttributeOverride and @AssociationOverride.
  • These tables are defined a foreign key for employee association.

Persistence Confication

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">
 <!-- Entities Created Before -->
 <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>
 <class>net.javabeat.eclipselink.data.License</class>
 <class>net.javabeat.eclipselink.data.DriverLicense</class>
 <!-- New Entity Added -->
 <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>

Anatomy of Mapped Superclass

License.java

package net.javabeat.eclipselink.data;

import javax.persistence.CascadeType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class License {

@Id
 protected int licenseId;

@ManyToOne(cascade = CascadeType.ALL)
 @JoinColumn(name = "employeeId")
 private Employee employee;

public Employee getEmployee() {
 return employee;
 }

public void setEmployee(Employee employee) {
 this.employee = employee;
 }

public int getLicenseId() {
 return licenseId;
 }

public void setLicenseId(int licenseId) {
 this.licenseId = licenseId;
 }
}

The license mapped superclass will be used for sharing an persistent entity states (licenseId and employee) and mapping infotrmation for those attributes and associations that could be inherited from it. Let’s take a look at these entities that doing that inheritance.

Anatomy of DriverLicense And ICDLComputerLicense Entities Before Overriding

DriverLicense.java

package net.javabeat.eclipselink.data;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="driverlicenses")
public class DriverLicense extends License{

 private String driverLicenseName;
 @Temporal(TemporalType.DATE)
 private Date driverLicenseExpiryDate;
 @Temporal(TemporalType.DATE)
 private Date driverLicenseIssueDate;

 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;
 }
}

ICDLComputerLicense.java

package net.javabeat.eclipselink.data;

import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name="icdlcomputerlicense")
public class ICDLComputerLicense extends License{
 private String icdlLicenseDegree;

 public String getIcdlLicenseDegree() {
 return icdlLicenseDegree;
 }

public void setIcdlLicenseDegree(String icdlLicenseDegree) {
 this.icdlLicenseDegree = icdlLicenseDegree;
 }

}

The following points you should take care of:

  • The DriverLicense inherit the License mapped superclass, so it should define a table for it.
  • The table that the DriverLicense mapped into called (driverlicenses).
  • The driverlicenses table defines a columns that identical to these attributes defined in the License mapped superclass.
  • No need for overriding any persistent states or association in the DriverLicense entity, cause the mapping information that provided by the mapped superclass License does correspond those columns that’s provided by the driverlicenses table.
  • The ICDLComputerLicense inherit the License mapped superclass, so it should define a table for it.
  • The table that the ICDLComputerLicense mapped into called (icdlcomputerlicense).
  • The icdlcomputerlicense table defines a columns that vary differ from that attributes defined in the License mapped superclass.
  • The ICDLComputerLicense entity will not persisted anymore.

Executable Sample for Creating DriverLicense Entity

JPAImpl.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
 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);
 // Create an ICDLComputerLicense
 createICDLComputerLicense(employee);
 // Commit
 em.getTransaction().commit();

}

 public static void createICDLComputerLicense(Employee employee){
 ICDLComputerLicense license = new ICDLComputerLicense();
 license.setEmployee(employee);
 license.setLicenseId(2);
 license.setIcdlLicenseDegree("A");
 em.persist(license);
 }

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

}

If we’ve used the same code that provided previously for creating an ICDLComputerLicense without any modifications on the mapping information, we are about getting such that exception:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'LICENSEID' in 'field list'
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
 at com.mysql.jdbc.Util.getInstance(Util.java:386)
 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
 at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
 at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
 at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815)
 at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
 at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
 at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
 at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
 at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890)
 ... 32 more

The exception says licenseId isn’t defined, that’s because the licenseId attribute that defined in the mapped superclass doesn’t mapped properly in the sub-class.

ICDLComputerLicense Entity Does Override the Mapped Superclass mapping information

Having ICDLComputerLicense entity full functional requires to override both of inherited attribute and association. The provided ICDLComputerLicense update code shows you the difference required.

ICDLComputerLicense.java

package net.javabeat.eclipselink.data;

import javax.persistence.AssociationOverride;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="icdlcomputerlicense")
@AttributeOverride(name="licenseId",column=@Column(name="icdlLicenseId"))
@AssociationOverride(name="employee",joinColumns={@JoinColumn(name="icdlEmployeeId")})
public class ICDLComputerLicense extends License{
 private String icdlLicenseDegree;

 public String getIcdlLicenseDegree() {
 return icdlLicenseDegree;
 }

public void setIcdlLicenseDegree(String icdlLicenseDegree) {
 this.icdlLicenseDegree = icdlLicenseDegree;
 }

}

ICDLComputerLicense Row Instance Has Persisted

By following the previous updated ICDLComputerLicense code, you should be able to persist a new instance of both DriverLicense and ICDLComputerLicense entities. See Figure 1.3 shows you the records that’s inserted into database after the required changes has made.

ICDLComputerLicenseEntity

Figure 1.3

@AttributeOverrides and @AssociationOverrides

If you’ve multiple persistent entity states or associations you would be overridden, let your code use the @AttributeOverrides and @AssociationOverrides, that can provide an override for the mappings of multiple properties or fields and for mapping of multiple one-to-one and many-to-one associations. When it comes to use them, nothing changed except the @AttributeOverrides and @AssociationOverrides accpet multiple @AttributeOverride and @AssociationOverride.

Download Source Code For This Example

Summary

When you have an inheritance hierarchy, it is possible to have a mapping information provided in the root entity or mapped super class differs from the remaining mapping that possibly occurred at the sub-classes. The JPA implementation take care of such that cases, so an important provided annotations could be used for overriding the mapping that already established. @AttributeOverride and @AssociationOverride override the mapping information for attribute (persistent entity states) and association respectively. This tutorial should clarify using of such those annotations.

Comments

comments

About Amr Mohammed

Speak Your Mind

*