In this Hibernate article, I’d like to share with you guys about how to map composite primary key in database to ID field in Java entity class, with various working code examples. In other words, you will learn how to design relational database table with composite primary key and how to implement that in Java code with Hibernate ORM framework.

 

1. Why Composite Primary Key?

Normally, we design a table with single-column primary key, e.g. column ID of type integer, whose values uniquely identify different rows in the table. In some cases, there can be two or more columns which, when combined together, can uniquely identify rows in the table, as shown in the below example:

composite primary key example 

Here, you can see, the airports table has a composite primary key which consists of 2 columns: country_code and city_code - these two columns can uniquely identify rows in the table so no need a numeric primary key column as usual.

Similarly, the phone_contacts table has a composite primary key which consists of 2 columns: area_code and number. These two columns are part of the table and they uniquely identify rows so no need a separate numeric ID primary key as usual.

Having said that, a composite primary key is a combination of 2 more columns whose values when combined together uniquely identify rows in the table. In such case, a separate ID primary key is not needed - thus reducing the data needs to be stored in the table.


2. Requirements for ID class

In order to map a composite primary key with Hibernate, you need to create a composite primary key class - or ID class that meets the following requirements:

  • The ID class must be public
  • It must implement Serializable interface
  • It must have a no-argument constructor
  • It must override equals() and hashCode() methods.
Now, let’s see some code examples about mapping composite primary key as described below.


3. Hibernate Composite Primary Key Example with @IdClass Annotation

Let’s go through an example that uses the @IdClass annotation to map a composite primary key in the airports table as shown below:



airports table

Code the AirportID class as follows (getters, setters, equals and hashCode are not shown for brevity):

package net.codejava;

import java.io.Serializable;

public class AirportID implements Serializable {

	private String countryCode;
	
	private String cityCode;

	// getters & setters
	
	// override equals()
	
	// override hashCode()
	
}
Then code the Airport entity class as follows:

package net.codejava;

import jakarta.persistence.*;

@Entity
@Table(name = "airports")
@IdClass(AirportID.class)
public class Airport {

	@Id @Column(name = "country_code")
	private String countryCode;
	
	@Id @Column(name = "city_code")
	private String cityCode;
	
	private String name;

	
	// getters & setters
	
	// override toString()

	
}
As you can see, in this entity class we specify the ID class using the @IdClass annotation:

@IdClass(AirportID.class)
public class Airport { … }
And note that we also need to redefine the ID class’ fields in the entity class.

Given a Hibernate session, the following code snippet persists an Airport object into the database:

Transaction transaction = session.beginTransaction();

Airport airport = new Airport(); 
airport.setCountryCode("VN");
airport.setCityCode("HAN"); 
airport.setName("Noi Bai International Airport");

session.persist(airport);

transaction.commit();
And the following code example shows how to execute a Hibernate query (HQL) against a class that has composite primary ID field:

String hql = "FROM Airport a WHERE a.countryCode='USA'"; 
Query<Airport> query = session.createQuery(hql, Airport.class);

List<Airport> airportsInUSA = query.list();
So as you can see, with @IdClass annotation, we access the property/field in the composite ID just like any other fields in the entity class.


4. Hibernate Composite Primary Key Example with @Embeddable and @EmbeddedId Annotations

Next, let’s see another way to map a composite primary key using the @Embeddable and @EmbeddedId annotations, and how it differs from the @IdClass one.

Suppose that we need to map the following table in database:

phone contacts table

You see, this table has a composite primary key that consists of two columns: area_code and number. So code the ID class as follows:

package net.codejava;

import java.io.Serializable;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;

@Embeddable
public class PhoneID implements Serializable {
	
	@Column(name = "area_code")
	private int areaCode;
	
	@Column(length = 12)
	private String number;

	public PhoneID() { }
	
	public PhoneID(int areaCode, String number) {
		this.areaCode = areaCode;
		this.number = number;
	}

	// getters and setters...
	
	// override equals and hashCode...
}
Then use this ID class in the entity class as follows:

package net.codejava;

import jakarta.persistence.*;

@Entity
@Table(name = "phone_contacts")
public class PhoneContact {

	@EmbeddedId
	private PhoneID id;

	@Column(name = "first_name", nullable = false, length = 20)
	private String firstName;
	
	@Column(name = "last_name", nullable = false, length = 20)
	private String lastName;

	// getters and setters are not shown for brevity
	
}
You see, using the @Embeddable and @EmbeddedId annotations, we don’t have to repeat the composite ID’s fields in the entity class.

And given a Hibernate session, the following code snippet illustrates how to persist a PhoneContact object:

PhoneContact contact = new PhoneContact(); 
contact.setId(new PhoneID(1, "12027933129")); 
contact.setFirstName("John");
contact.setLastName("Kellyson");

Transaction transaction = session.beginTransaction();

session.persist(contact);

transaction.commit();
And the below code shows how to execute a HQL against an entity class that has a composite ID field annotated with @EmbeddedId annotation:

String hql = "FROM PhoneContact p WHERE p.id.areaCode=1"; 
Query<PhoneContact> query = session.createQuery(hql, PhoneContact.class);

List<PhoneContact> contacts = query.list();
You see, in the WHERE clause, we refer to a specific field in the composite ID field like this:

FROM PhoneContact p WHERE p.id.areaCode=1
 

5. Hibernate Composite Key with Foreign Key Reference Example

It’s also common that the composite primary key consists of foreign keys, as shown in this entity relationship diagram:

composite primary key with foregin keys

Here, the order_details table realizes many-to-many relationship between products and orders tables. The foreign keys product_id and order_id are enough to identify unique rows so they can be set as a composite primary key.

Below is code of the Product entity class that maps to the products table:

package net.codejava;

import jakarta.persistence.*;

@Entity
@Table(name = "products")
public class Product {

	@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(nullable = false, length = 200)
	private String name;
	
	private float price;

	
	// getters and setters...
	
	// equals() and hashCode()...
}
Then code of the Order class that maps to the orders table:

package net.codejava;

import jakarta.persistence.*;

@Entity
@Table(name = "orders")
public class Order {

	@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(name = "customer_name", length = 50, nullable = false)
	private String customerName;
	
	@Column(length = 20, nullable = false)
	private String status;

	// getters and setters...
	
	// equals() and hashCode()...
}
And following is code of the ID class that maps the composite primary key in the order_details table:

package net.codejava;

import java.io.Serializable;

import jakarta.persistence.*;

@Embeddable
public class OrderDetailID implements Serializable {
	
	@ManyToOne @JoinColumn(name = "order_id")
	private Order order;
	
	@ManyToOne @JoinColumn(name = "product_id")
	private Product product;

	// getters and setters...
	
	// equals() and hashCode()...
	
}
So we use this ID class in the OrderDetail entity class that maps to the order_details table as follows:

package net.codejava;

import jakarta.persistence.*;

@Entity
@Table(name = "order_details")
public class OrderDetail {

	@EmbeddedId
	private OrderDetailID id = new OrderDetailID();
	
	private int quantity;
	
	@Column(name = "unit_price")
	private float unitPrice;
	
	private float subtotal;

	// getters and setters...
	
	// equals() and hashCode()...
}
With such mapping, the following code example shows how to persist an OrderDetail object in a session:

Transaction transaction = session.beginTransaction();

Product product = new Product();
product.setName("iPhone 15");
product.setPrice(1199);

session.persist(product);

Order order = new Order();
order.setCustomerName("Nam Ha Minh");
order.setStatus("In Progress");

session.persist(order);

OrderDetail orderDetail = new OrderDetail();

OrderDetailID orderDetailId = new OrderDetailID();
orderDetailId.setOrder(order);
orderDetailId.setProduct(product);

orderDetail.setId(orderDetailId);
orderDetail.setQuantity(2);
orderDetail.setUnitPrice(1199);
orderDetail.setSubtotal(2398);

session.persist(orderDetail);

transaction.commit();
And the following code example shows how to write a HQL that refers to the composite ID field:

String hql = "FROM OrderDetail o WHERE o.id.product.id = 2 AND o.subtotal > 2000";
Query<OrderDetail> query = session.createQuery(hql, OrderDetail.class);
List<OrderDetail> listOrderDetails = query.list();
That’s how to map composite primary key in Hibernate with various practical code examples (download the sample project in the Attachments section below). To see the coding in action, watch the following video:

I hope you found this article helpful. Also check other Hibernate tutorials below:

 

Other Hibernate Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (HibernateCompositeKeyExample.zip)HibernateCompositeKeyExample.zip[Sample Hibernate project]26 kB

Add comment