Exploring the world , One line of code at a time.

Composite Key in Spring Data JPA: A Powerful Way to Model Relationships

Composite Key in Spring Data JPA: A Powerful Way to Model Relationships

Composite Key

How to Implement

  • @IdClass
  • @EmbeddedId (We will use this for this post.)
  • The composite primary key class must be public.
  • It must have a no-arg constructor.
  • It must define the equals() and hashCode() methods.

Define the Main Entities

@Entity
@Getter
@Setter
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Other fields...
}

@Entity
@Getter
@Setter
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    // Other fields...
}

@Getter
@Setter
@NoArgsConstructor
@Embeddable
public class StudentCourseId implements Serializable {

    private Long studentId;
    private Long courseId;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof StudentCourseId)) return false;
        StudentCourseId that = (StudentCourseId) o;
        return Objects.equals(studentId, that.studentId) &&
               Objects.equals(courseId, that.courseId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(studentId, courseId);
    }
}

This class holds the combination of studentId and courseId.

Enrollment Entity (Join Table with Composite Key)

@Entity
@Getter
@Setter
public class Enrollment {

    @EmbeddedId
    private StudentCourseId id;

    @ManyToOne
    @MapsId("studentId")
    @JoinColumn(name = "student_id")
    private Student student;

    @ManyToOne
    @MapsId("courseId")
    @JoinColumn(name = "course_id")
    private Course course;

    private String grade;        // Additional field
    private String enrolledDate; // Additional field
}

Annotation Used to create the composite Key

@Embeddable

This mark the class whose fields can be used into another entity. Here, StudentCourseId is embedded into the Enrollment entity.

@EmbeddedId

Its tells JPA that the annotated field is a composite primary key. We use it in Enrollment to indicate that StudentCourseId is the combined key.

@ManyToOne

Defines a many-to-one relationship between two entities. In the example many enrollments belong to one student and many enrollments belogs to one course.

@MapsId

Maps a part of the composite key to the ID of a related entity. It tells JPA: “Use the ID from this object (like student.getId()) to fill part of the composite key (studentId).”

Summary

Composite keys give us full control over join tables in JPA — especially useful when working with complex relationships and when we need to store additional metadata in the join table.

I wonder a question: “Why do I need to define the equals() and hashCode() methods? Doesn’t Spring handle this automatically?”

I will explore this in a future blog post, Insha’Allah. Stay tuned for that!

Until then — Happy Coding, and Happy Blogging! ✨

Oh, and one more thing if you’re curious about using @IdClass for defining composite keys instead of @EmbeddedId read here.

Tanvir Rifat

I am a software engineer passionate about software development, blogging, and community building. I enjoys exploring new technologies, writing tech articles, and actively contributing to developer meetups and open knowledge sharing.