At this tutorial we’ve explained the concept of entity identification. Entity identification is the process in which the Java Persistence Implementation identify an entity uniquely from set of entities that belong to the same type on a memory level or a database level. Each entity that located in the persistence context must has a unique identifier called Primary Key.
The primary key must be defined on the entity that is form the root of the entity hierarchy or on mapped superclass of the entity hierarchy. The primary key must be defined once in an entity hierarchy, so if you’ve ever tried to create an entity without mentioning of its primary key (Entity Identifier), you are always getting a compiler error as you would be seeing in the following fragment below.
This fragment of code was extracted from a previous code that made before for Employee Entity (See EclipseLink Tutorial Examples). The identifier is only defined inside Employee entity; Employee is the root of the entity hierarchy that contains a Developer, ContractorDeveloper and FreelanceDeveloper as a sub-classes (entities).
A simple (i.e non-composite) primary key must correspond to a single entity persistent field or property of the entity class. The @Id annotation is used to denote a simple primary key. A composite primary key must correspond to a either a single persistent field or property or to a set of fields or properties.
The primary key (or field or property of a composite primary key) should be one of the following types:
- Java primitive types
- Java primitive wrapper types
- java.lang.String
- java.util.Date (note that the Temporal Type should be specified as DATE)
- java.sql.Date
No rules for simple primary key and few rules applied to composite primary key. The following are the rules applied to the composite primary key:
- The primary key class must be public and must have no-arg constructor.
- The primary key class must be serializable
- The primary key class must define the equals and hashCode methods.
- The composite primary key must either be represented and mapped as embeddable class or must be represented and mapped to multiple fields or properties of the entity class as you would be seeing in this tutorial.
- If the composite primary key class is mapped to multiple fields or properties of the entity class, the names of primary key field or properties in the primary class and those of the entity class must correspond and their types must be the same.
- If the property-access based is used, the properties of the primary key class must be public or protected.
@Id
The Id annotation specifies the primary key property or field of an entity. The Id annotation may be applied in an entity or mapped superclass. See the following fragment of code that shows you the Project entity. Project entity is the root of the entity inheritance.
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) public abstract class Project { @Id private int projectId; private String projectName; @ManyToMany(mappedBy="projects",cascade=CascadeType.ALL) private List 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 getEmployees() { return employees; } public void setEmployees(List employees) { this.employees = employees; } }
- The Project entity defines the entity identifier, so no need for defining it again the sub-classes that inherit from it.
Let’s see a GlobalProject entity that inherit the Project.
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; } }
- No entity identifier has been defined, cause it’s already defined in the Project.
Anatomy of @Id
- Target: Field or Methods
- Uses: @Id
- Argument: No argument provided
@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Id {}
@IdClass
The IdClass annotation is applied to an entity class or mapped superclass to specify a composite primary key class that’s mapped to multiple fields or properties of the entity.The names of primary key field or properties in the primary class and those of the entity class must correspond and their types must be the same. The Id annotation must also be applied to the corresponding fields or properties of the entity. The next coming lines will show you how to use the IdClass annotation.
Anatomy of @IdClass
- Target: Type
- Uses: @IdClass
- Argument: value that’s must be a class instance
@Target({TYPE}) @Retention(RUNTIME) public @interface IdClass { Class value; }
Database Phone Table
Figure 1.0 shows you the changes that happened at the Phone Table.
Figure 1.0
As you’ve noted in the Figure 1.0, the Phone entity now uses a composite primary key. Let’s see the primary key class.
Implementation of PhonePK
PhonePK.java
package net.javabeat.eclipselink.data; import java.io.Serializable; import javax.persistence.IdClass; // IdClass annotation used for composite primary key creation @IdClass(PhonePK.class) public class PhonePK implements Serializable{ private String phoneCountryKeyId; private int phoneId; public String getPhoneCountryKeyId() { return phoneCountryKeyId; } public void setPhoneCountryKeyId(String phoneCountryKeyId) { this.phoneCountryKeyId = phoneCountryKeyId; } public int getPhoneId() { return phoneId; } public void setPhoneId(int phoneId) { this.phoneId = phoneId; } // The override of equals, see the rules mentioned above for creating a composite primary key public boolean equals(Object obj){ if(obj instanceof PhonePK){ PhonePK phonePK = (PhonePK)obj; if(this.phoneId == phonePK.getPhoneId() && this.phoneCountryKeyId.equals(phonePK.getPhoneCountryKeyId())){ return true; } } else { return false; } return false; } // The override of hashCode, see the rules mentioned above for creating a composite primary key public int hashCode(){ return super.hashCode(); } }
Implementation of Phone Entity
Phone.java
package net.javabeat.eclipselink.data; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @Entity(name="Phones") // If you've removed the @IdClass, you've got a compiler error @IdClass(PhonePK.class) public class Phone { // By defining two @Id, the JPA assumes that you've defined a composite primary key /* The properties defined inside the Phone entity and annotated with the @Id have the same name and type of those properties defined in the PhonePK primary key */ @Id private int phoneId; @Id private String phoneCountryKeyId; private String phoneNumber; @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name="employeeId") private Employee employee; public int getPhoneId() { return phoneId; } public void setPhoneId(int phoneId) { this.phoneId = phoneId; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public String getPhoneCountryKeyId() { return phoneCountryKeyId; } public void setPhoneCountryKeyId(String phoneCountryKeyId) { this.phoneCountryKeyId = phoneCountryKeyId; } }
Executable Application
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.AddressPK; 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.EmployeePeriod; 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.PhonePK; 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(); createEmployee(); // Commit em.getTransaction().commit(); // Inquiry about Phone Using the PhonePK inquiryUsingPhonePK(); } public static void createEmployee(){ // Create an address entity Address address = new Address(); // Address Embeddable class (Type) instantiation AddressPK addressPK = new AddressPK(); addressPK.setAddressId(1); addressPK.setAddressCountryId(1); // addressPK.setAddressCityId(1); address.setAddressId(addressPK); address.setAddressCountry("United Kingdom"); address.setAddressCity("London"); // Create an employee entity Employee employee = new Employee(); employee.setEmployeeId(2); employee.setEmployeeName("John Smith"); // Create an Employee Period Instance EmployeePeriod period = new EmployeePeriod(); period.setStartDate(new Date()); period.setEndDate(new Date()); employee.setEmployeePeriod(period); // Associate the address with the employee employee.setAddress(address); // Create a Phone entity Phone firstPhone = new Phone(); // PhoneId and PhoneCountryKeyId is now the primary key for the phone entity firstPhone.setPhoneId(3); firstPhone.setPhoneCountryKeyId("+441"); firstPhone.setPhoneNumber("4050 615"); firstPhone.setEmployee(employee); // Create a list of phone List phones = new ArrayList(); phones.add(firstPhone); // Create a list of projects List projects = new ArrayList(); // Set the project into employee employee.setProjects(projects); // Set the phones into your employee employee.setPhones(phones); // Persist the employee em.persist(employee); } public static void inquiryUsingPhonePK(){ PhonePK pk = new PhonePK(); pk.setPhoneId(3); pk.setPhoneCountryKeyId("+441"); Object obj = em.find(Phone.class, pk); if(obj instanceof Phone){ Phone phone = (Phone)obj; System.out.println(phone.getPhoneId()); System.out.println(phone.getPhoneCountryKeyId()); System.out.println(phone.getPhoneNumber()); } System.out.println(obj); } }
- The Phone entity has a composite primary key
- The phone entity has been created successfully with its new primary key
- The PhonePK primary key used later for inquiring the Phone persisted instance.
@EmbeddedId
The @EmbeddedId is the second way that already used before for creating a primary key. See @Embeddable, Embeeded and @EmbeddedId example.
Summary
Java Persistence API provides you a various kind of primary keys. Primary key used to identify the entity that’s being persisted either in the memory or inside the database. @Id used to define a simple primary key, while the @IdClass and @EmbeddedId for composite.