Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

NPI – JPA Buddy – JPA (partner)
announcement - icon

JPA is huge! It covers nearly every aspect of communication between relational databases and the Java application and is deeply integrated into all major frameworks.

If you're using IntelliJ, JPA Buddy is super helpful. The plugin gently guides you through the subtleties of the most popular JPA implementations, visually reminds you of JPA features, generates code that follows best practices, and integrates intelligent inspections to improve your existing persistence code.

More concretely, it provides powerful tooling to generate Spring Data JPA repositories and methods, Flyway Versioned Migrations, Liquibase Differential Changelogs, DDL and SQL statements, DTO objects, and MapStruct interfaces.

Oh, and it actually generates JPA entities from an existing database and gradually update the data model as the database evolves! Yeah.

>> Become a lot more productive with JPA Buddy

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until the end of this week:


November Discount Launch 2022 – TEMP TOP (NPI)
We’re finally running a Black Friday launch. All Courses are 30% off until the end of this week:


1. Introduction

In this tutorial, we'll learn about Composite Primary Keys and the corresponding annotations in JPA.

Further reading:

Spring JPA @Embedded and @EmbeddedId

Learn how to use the @EmbeddeId and @Embeddable annotations to represent composite keys in JPA entities.

When Does JPA Set the Primary Key

Learn about the different strategies JPA uses to generate the primary key for an entity and at which moment each strategy sets the key value during persistence.

Returning an Auto-Generated Id with JPA

In this tutorial, we'll discuss how we can handle auto-generated ids with JPA.

2. Composite Primary Keys

A composite primary key, also called a composite key, is a combination of two or more columns to form a primary key for a table.

In JPA, we have two options to define the composite keys: the @IdClass and @EmbeddedId annotations.

In order to define the composite primary keys, we should follow some rules:

  • The composite primary key class must be public.
  • It must have a no-arg constructor.
  • It must define the equals() and hashCode() methods.
  • It must be Serializable.

3. The IdClass Annotation

Let's say we have a table called Account and it has two columns, accountNumber and accountType, that form the composite key. Now we have to map it in JPA.

As per the JPA specification, let's create an AccountId class with these primary key fields:

public class AccountId implements Serializable {
    private String accountNumber;

    private String accountType;

    // default constructor

    public AccountId(String accountNumber, String accountType) {
        this.accountNumber = accountNumber;
        this.accountType = accountType;

    // equals() and hashCode()

Next let's associate the AccountId class with the entity Account.

In order to do that, we need to annotate the entity with the @IdClass annotation. We must also declare the fields from the AccountId class in the entity Account and annotate them with @Id:

public class Account {
    private String accountNumber;

    private String accountType;

    // other fields, getters and setters

4. The EmbeddedId Annotation

@EmbeddedId is an alternative to the @IdClass annotation.

Let's consider another example where we have to persist some information of a Book, with title and language as the primary key fields.

In this case, the primary key class, BookId, must be annotated with @Embeddable:

public class BookId implements Serializable {
    private String title;
    private String language;

    // default constructor

    public BookId(String title, String language) {
        this.title = title;
        this.language = language;

    // getters, equals() and hashCode() methods

Then we need to embed this class in the Book entity using @EmbeddedId:

public class Book {
    private BookId bookId;

    // constructors, other fields, getters and setters

5. @IdClass vs @EmbeddedId

As we can see, the difference on the surface between these two is that with @IdClass we had to specify the columns twice, once in AccountId and again in Account; however, with @EmbeddedId we didn't.

There are some other trade offs though.

For example, these different structures affect the JPQL queries that we write.

With @IdClass, the query is a bit simpler:

SELECT account.accountNumber FROM Account account

With @EmbeddedId, we have to do one extra traversal:

SELECT book.bookId.title FROM Book book

Also, @IdClass can be quite useful in places where we are using a composite key class that we can't modify.

If we're going to access parts of the composite key individually, we can make use of @IdClass, but in places where we frequently use the complete identifier as an object, @EmbeddedId is preferred.

6. Conclusion

In this brief article, we explored composite primary keys in JPA.

As always, the complete code for this article can be found over on Github.

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until the end of this week:


Persistence footer banner
Comments are closed on this article!