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