Entity Relationships in Spring Data JPA

In real-world applications, data is often related. For example:

  • A user may have one profile
  • A customer can place many orders
  • A student can enroll in many courses

Entity Relationships in Spring Data JPA (with Hibernate) allows you to map these relationships easily using annotations.

This guide will explain:

  • What are entity relationships?
  • @OneToOne mapping
  • @OneToMany and @ManyToOne mapping
  • @ManyToMany mapping
  • Examples for each with code
Entity Relationships in Spring Data JPA

๐Ÿ”— What Are Entity Relationships in Spring Data JPA?

In databases, relationships describe how tables are connected:

TypeDescriptionExample
One-to-OneOne record relates to one other recordOne user has one profile
One-to-ManyOne record relates to many recordsOne customer has many orders
Many-to-ManyMany records relate to many recordsA student enrolls in many courses and a course has many students

1๏ธโƒฃ OneToOne Mapping

๐Ÿ’ก Example: A User has one Profile

๐Ÿ”ง User Entity


@Entity
public class User {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    private Profile profile;

    // getters and setters
}

๐Ÿ”ง Profile Entity


@Entity
public class Profile {
    @Id @GeneratedValue
    private Long id;
    private String bio;

    @OneToOne
    @JoinColumn(name = "user_id")
    private User user;

    // getters and setters
}

โœ… Notes:

  • mappedBy: used on the non-owning side
  • @JoinColumn: used on the owning side to define the foreign key

2๏ธโƒฃ OneToMany and ManyToOne Mapping

๐Ÿ’ก Example: A Customer has many Orders

๐Ÿ”ง Customer Entity


@Entity
public class Customer {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private List orders = new ArrayList<>();

    // getters and setters
}

๐Ÿ”ง Order Entity


@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;
    private String product;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    // getters and setters
}

โœ… Notes:

  • @OneToMany is typically placed on the parent side
  • @ManyToOne defines the foreign key in the child

3๏ธโƒฃ ManyToMany Mapping

๐Ÿ’ก Example: A Student can enroll in many Courses and a Course can have many Students

๐Ÿ”ง Student Entity


@Entity
public class Student {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private List courses = new ArrayList<>();

    // getters and setters
}

๐Ÿ”ง Course Entity


@Entity
public class Course {
    @Id @GeneratedValue
    private Long id;
    private String title;

    @ManyToMany(mappedBy = "courses")
    private List students = new ArrayList<>();

    // getters and setters
}

โœ… Notes:

  • @JoinTable creates a third (join) table to hold many-to-many relationships
  • mappedBy is used on the inverse side

๐Ÿ” How to Test These Relationships

You can create sample data in a CommandLineRunner or using a controller:


@Bean
CommandLineRunner run(StudentRepository studentRepo, CourseRepository courseRepo) {
    return args -> {
        Course java = new Course("Java Basics");
        Course spring = new Course("Spring Boot");

        Student student = new Student("Alice");
        student.getCourses().add(java);
        student.getCourses().add(spring);

        java.getStudents().add(student);
        spring.getStudents().add(student);

        studentRepo.save(student);
    };
}

๐Ÿงผ Cascading and Fetch Types

Cascade Types:

  • CascadeType.PERSIST: Propagates save
  • CascadeType.REMOVE: Deletes child when parent is deleted
  • CascadeType.ALL: All operations are cascaded

Fetch Types:

  • FetchType.LAZY: Loads only when needed
  • FetchType.EAGER: Loads immediately

๐Ÿ’ก Prefer LAZY for large data sets to avoid performance issues.

โœ… Summary

AnnotationDescription
@OneToOneOne-to-one mapping
@OneToManyOne-to-many mapping
@ManyToOneMany-to-one mapping
@ManyToManyMany-to-many mapping

๐Ÿ“Œ Best Practices

  • Always define mappedBy on the non-owning side
  • Use @JoinColumn to customize foreign key column names
  • Avoid FetchType.EAGER unless really necessary
  • Use DTOs for large/complex data instead of directly exposing entities

References

Spring JPA Docs