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