Configuring Composite Primary key in JPA applications

Composite keys are a group of columns in the database, whose values together make a unique value. When using Hibernate or JPA applications there are two main strategies to map a Composite Primary Key.

Mapping a Composite key with an @IdClass

The name of the class is indicated as the value of the class attribute of the IdClass element, as
shown in this example:

@Entity
@IdClass(PersonId.class)
public class Person {

	@Id
	String name;
	@Id
	String surname;
	String address;
	String email;

    // Getter Setters and Constructors
}

As you can see, the Compound keys are annotated with @Id and we need to provide an @IdClass:

public class PersonId implements Serializable {
    String name;
    String surname;

   // Constructors
   // Getters /Setters equals and hashcode

    public PersonId(String name, String surname) {
        this.name = name;
        this.surname = surname;
    }
}

If using a Repository pattern to access your data, the compound key will be used as Type (instead of the Integer id field):

public interface PersonRepository extends CrudRepository<Person, PersonId> {

    List<Person> findBySurname(String surname);
}

Mapping a Composite key with an EmbeddedId

An embedded type is marked as such by adding the @Embeddable annotation to the class definition.
This annotation serves to distinguish the class from other regular Java types. Once a class has been
designated as embeddable, then its fields and properties will be persistable as part of an entity.

Here is a Class which uses an @Embeddable annotation:

@Entity
public class Customer {
	
    @EmbeddedId
    private CustomerEmbeddable customerPK;
 
    String address;
    String email;

   // Getter Setters and Constructors
   
}
What is the Embeddable annotation in JPA ? 

In Java Persistence API (JPA), the @Embeddable annotation is used to define a class that can be embedded within an entity class. It indicates that the annotated class is intended to be a value type and can be used as a component of another entity.

When you mark a class with @Embeddable, it means that instances of that class will be stored as part of the owning entity's table in the database. The attributes of the embeddable class are mapped to the columns of the entity's table. Essentially, it allows you to create reusable, composite value types that can be embedded within multiple entity classes.

As you can see, the compound key fields are not included in the Entity class. The CustomerEmbeddable class follows here:

@Embeddable
public class CustomerEmbeddable implements Serializable {
    String name;
    String surname;

   // Constructors
   // Getters /Setters  equals and hashcode

    public CustomerEmbeddable(String name, String surname) {
        this.name = name;
        this.surname = surname;
    }
}

You should as well adapt your Repository class to use the Embeddable primary key:

public interface CustomerRepository extends CrudRepository<Customer, CustomerEmbeddable> {

    List<Customer> findByEmail(String email);
}

So, which one should you choose? from the Database point of view, no changes are required when choosing one strategy or another

The @EmbeddedId strategy does not include the primary key fields in the Entity and thus communicates more clearly that the key is a composite key. Therefore, if you are using directly your Entity in your Controllers it makes more sense to use it. On the other hand, if using a DTO layer to hide the complexity of the Entity you can opt for the @IdClass which has the advantage to be less verbose when using HQL queries.

For example, compare:

select p.name from Person p

With:

select c.customerEmbeddable.name from Customer c

In conclusion, we have covered the two main strategies to map a compound primary key in a Hibernate / JPA application.

Source code for this example: https://github.com/fmarchioni/masterspringboot/tree/master/jpa/composite-pk