JPA Entity Primary Key Using @Id and @IdClass

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.

Missing Identity

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.

Phone Table Uses composite primary key

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.

Comments

comments

About Amr Mohammed

Speak Your Mind

*