Hibernate Composite Primary Key Examples
- Details
- Written by Nam Ha Minh
- Last Updated on 08 November 2022   |   Print Email
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: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.
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: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: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: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:
- Java Hibernate JPA Annotations Tutorial for Beginners
- Hibernate Hello World Tutorial for Beginners with Eclipse and MySQL
- Hibernate One-to-Many Using Join Table XML Mapping Example
- Hibernate Many-to-Many Association with Extra Columns in Join Table Example
- Hibernate Enum Type Mapping Example
- Hibernate Binary Data and BLOB Mapping Example
- Hibernate Query Language (HQL) Example
- Java Hibernate Reverse Engineering Tutorial with Eclipse and MySQL
- Hibernate Basics - 3 ways to delete an entity from the datastore
- Hibernate Forward Engineering - Create Tables from Entity Classes
Comments