PedroGeoGISdev wiki
  • Home
  • Linux OS
    • Linux: concepts
    • Linux: basic concepts
    • Linux: Bash
    • Linux: su and sudo
    • Linux: pipes
    • Linux: File System
    • Linux: Virtual Machines

    • Linux: distros
    • Linux Distros: Ubuntu
    • Linux Distros: Mint
    • Linux Distros: Debian
    • Linux Distros: openSuse
    • Linux Distros: Manjaro
    • Linux Distros: Red Hat Enterprise

    • Linux: laboratories
    • Linux Lab#LI01-1: Choose Linux
    • Linux Lab#LI01-2: Install at least three distributions
    • Linux Lab#LI01-3: Adjust user permissions
    • Linux Lab#LI02-1: Export env user with grep and pipe
    • Linux Lab#LI03-1: Manage users and groups
    • Linux Lab#LI03-2: Manage files
    • Linux Lab#LI03-3: Manage software
    • Linux Lab#LI03-4: Manage hardware
    • Linux Lab#LI04-1: Bash scripting, qtool
    • Linux Lab#LI04-2: Bash scripts as terminal tool
    • Linux Lab#LI04-3: Distribute the terminal app

    • Linux readings
    • Linux Resources
  • DevOps
    • What is DevOps
    • DevOps: Introduction
    • DevOps: Agile and Microservices
    • Infrastructure as code (IaC)
    • Immutable Infrastructure
    • Software Lifecycle

    • Documentation
    • How to document: Quarto and Obsidian

    • Network protocols
    • Network: Basics
    • Network: Client-server
    • Network Protocols
    • Network: DNS
    • Network: API Rest
    • Network: gRPC
    • Network: Websocket
    • Network: SMTP
    • Network: Ping
    • Network: UDP
    • Network: webhook
    • Network: SOAP
    • Network: graphQL

    • Version Control
    • Git
    • GitHub
    • Idea and GitHub 2023
    • Git and GitHub 2023 CLI

    • IDEs
    • IDE: Visual Code
    • IDE: IntellJIdea

    • DevOps tools
    • Amazon Web Services AWS
    • Docker
    • Jenkins pipelines
    • Kubernetes k8s
    • Digital Ocean
    • Nagios
    • Ansible

    • DevOps Laboratories
    • Lab 1: chat App
    • Lab 2: Spring Boot AWS AEB manually
    • Lab 3: Spring Boot and AWS S3 publisher
    • Lab 4: Spring Boot Docker/Jenkins
    • Lab 5: k8s on Digital Ocean
    • Lab 6: Spring Boot AWS codecommit

    • DevOps readings
    • DevOps Resources
  • MarkUp
    • MarkUp Languages
    • Introduction Markup
    • HTML Markup
    • Markdown Markup
    • Markdown and HTML working together, good idea?

    • Quarto Markdown
    • Quarto Markdown: basics
    • Quarto Markdown: creating
    • Quarto Markdown: publishing
    • Quarto Markdown: code & data
    • Quarto Markdown: api rest call
    • Quarto Markdown: OJS Cells
    • Quarto Markdown: cheat-sheet

    • Styling: CSS
    • Cascade Style Sheet
    • Cascade Style Sheet: Box Model and Containers
    • CSS: W3.css

    • MarkUp Languages Laboratories
    • Lab#MD01-1: Create and publish by Quarto

    • MarkUp Languages readings
    • MarkUp Languages Resources
  • Java SE
    • What is Java SE
    • Java Standard Edition: Basics
    • Java Standard Edition: Principles
    • Java MOOC Helsinki
    • Java MOOC Helsinki Syllabus

    • Java Create Project
    • Java SE: Maven
    • Java SE: Create Maven Project
    • Java SE: Project push GitHub
    • Java SE: JUnit and TDD

    • Java Concepts
    • Java SE: Class and Objects
    • Java SE: Scope
    • Java SE: static modifier
    • Java SE: Coupling and DDD
    • Java SE: Packages
    • Java SE: Abstract/Interface
    • Java SE: Java 8

    • Java Principles
    • Java SE: Encapsulation
    • Java SE: Abstraction
    • Java SE: Inherence
    • Java SE: Polymorphism

    • Java Design Patterns
    • Java Patterns: UML
    • Java Patterns: Types
    • Singleton
    • Factory
    • Abstract Factory
    • Builder
    • Facade
    • Bridge
    • Decorator
    • Composite
    • Observer
    • Strategy
    • State
    • Commander

    • Java SE Laboratories
    • Lab#SE00-1: Maven Person
    • Lab#SE00-2: Maven Clinic
    • Lab#SE00-3: Library Model
    • Lab#SE00-4: Abstract/Interface Human
    • Lab#SE01-1: Maven/Gradle Person and Account
    • Lab#SE01-2: Maven/Gradle Person and Account stored in JSON
    • Lab#SE02-1: Movie/Review, Model
    • Lab#SE02-2: Movie/Review, CRUD Operations
    • Lab#SE02-3: Movie/Review, factory
    • Lab#SE02-4: Movie/Review, interactivity and coupling
    • Lab#SE02-5: Movie/Review, simulate interactivity by console
    • Lab#SE03-1: Library/Book, Core-Model
    • Lab#SE03-2: Library/Book, Sprint Zero
    • Lab#SE03-3: Library/Book, Expand Model
    • Lab#SE04-1: healthyFood Restaurant, Core Model

    • Java SE readings
    • Java SE Resources
  • Python
    • Python Basics
    • Python: Basic Concepts
    • Python: Tips
  • JavaScript
    • JavaScript Basics
    • JavaScript: Basic Concepts
    • JavaScript: Tips
  • Spring
    • Spring Legacy
    • Spring Framework
    • Spring MVC
    • Springs Servlets

    • Spring Boot Basics
    • Spring Boot: fundamentals
    • Spring Boot: create a Project
    • Spring Boot: H2 DB and Thymeleaf
    • Spring Boot: cycle

    • Spring Boot Concepts
    • Spring Boot: Dependency Injection
    • Spring Boot: Annotations
    • Spring Boot: Controller
    • Spring Boot: View
    • Spring Boot: Thymeleaf
    • Spring Boot: Vaadin Flow
    • Spring Boot: Vaadin Hilla
    • Spring Boot: Model
    • Spring Boot: Rest
    • Spring Boot: Data & DB
    • Spring Boot: JPA & DI
    • Spring Boot: JPA Mappings
    • Spring Boot: JPA Relationships
    • Spring Boot: JPA Queries
    • Spring Boot: JPA Inherence
    • Spring Boot: Scaling

    • Spring Boot Laboratories
    • Lab#SB00-1: Library UML
    • Lab#SB00-2: CRUD User
    • Lab#SB00-3: LibraryManagement
    • Lab#SB00-4: API Rest
    • Lab#SB00-5: Rest & JPA-H2
    • Lab#SB00-6: Rest & MongoDB
    • Lab#SB00-7: Styling
    • Lab#SB01-1: DataBase
    • Lab#SB02-1: JPA Relationships
    • Lab#SB03-1: APIs & cloud
    • Lab#SB04-1: JPA Inherence
    • Lab#SB05-1: API Rest
    • Lab#SB06-1: employeeCourse
    • Lab#SB07-1: monitor Book
    • Lab#SB08-1: Restaurant UML
    • Lab#SB08-2: Vaadin
    • Lab#SB08-3: H2 and API Rest
    • Lab#SB08-4: JPA
    • Lab#SB08-5: Test API Rest
    • Lab#SB09-1: SpringIO Conference

    • Spring Boot readings
    • Spring Boot Resources
  • ReactJS
    • ReactJS: Principles
    • React JS: Introduction
    • React JS: render virtual DOM
    • React JS: Create a React project
    • React JS: Components
    • React JS: JSX
    • React JS: props and state

    • JavaScript: web scripting
    • JavaScript: basics
    • JavaScript: functions
    • JavaScript: objects
    • JavaScript: variables
    • JavaScript: flux control

    • ES6: ECMAScript 6
    • React JS ES6: arrow functions
    • React JS ES6: import modules
    • React JS ES6: array, data and key
    • React JS ES6: destructuring
    • React JS ES6: spread operator

    • ReacJS 18: Hooks
    • React JS: Rules of Hooks
    • ReactJS: useState
    • React JS: useReducer
    • React JS: useRef
    • React JS: useEffect
    • React JS: useContext
    • ReactJS: useMemo
    • ReactJS: custom hooks

    • ReactJS: Designing an App
    • React JS App: async
    • React JS App: events
    • React JS App: router
    • React JS App: conditional render
    • React JS App: styling

    • React JS: Laboratories
    • Lab#RE01-1: API Rest Axios
    • Lab#RE02-1: Router & Hooks
    • Lab#RE03-1: to-do app
    • Lab#RE03-2: HighCharts
    • Lab#RE03-3: API Rest Mono
    • Lab#RE03-4: API Rest Domains
    • Lab#RE03-5: data management
    • Lab#RE04-1: todo & server
    • Lab#RE04-2: Spring Boot & ReactJS
    • Lab#RE05-1: chat & websockets
    • Lab#RE05-2: chat: backend
    • Lab#RE05-3: chat & AWS
    • Lab#RE05-4: chat: test ws AWS
    • Lab#RE05-5: chat & front
    • Lab#RE05-6: chat & ws: front
    • Lab#RE06-1: healthyFood Restaurant
    • Lab#RE06-1-PR: create a pull request
    • Lab#RE07-1: traffic lights simulation

    • React JS readings
    • ReactJS Resources
  • Learning
    • Vocabulary
    • General Vocabulary
    • SCRUM Vocabulary
    • DevOps Vocabulary
    • Java SE Vocabulay
    • Spring Boot Vocabulary
    • DataBase Vocabulary
    • ReactJS Vocabulary
    • Web Vocabulary

    • Learning
    • Useful Questions
    • Learning: tips
    • Writing
    • Taking Notes
    • Comments
    • Document
    • Auto-Evaluate

    • Books & Articles
    • Books
    • Articles

    • What is SCRUM
    • SCRUM Agile Methodology
    • Agile Manifesto & Values
    • SCRUM Guide

    • Scrum Steps
    • Meetings, Impediments and Iterations
    • User stories, Tasks and Habits
    • Delivering Value & Communication
    • ScrumMaster, how it works
    • Mindset, the key to everything
    • Product Owner, how it works
    • Managing Time & Mind
    • Team & the Specialist
    • Albertus’ Dilemma
    • Before SCRUM
    • Team Dynamics
    • Emotions and Thoughts
    • Decision Making and Intuition
    • Beyond SCRUM
    • Balances, atmosphere and tools

    • Resources
    • SCRUM Resources
  • QGIS
    • QGIS basics
    • QGIS: basic concepts

    • QGIS laboratories
    • QGIS Laboratory 1: Introduction to Open Source GIS
  • ArcGIS Pro
    • ArcGIS Pro basics
    • ArcGIS Pro: basic concepts

    • ArcGIS Pro laboratories
    • ArcGIS Pro Laboratory 1: Getting Started
  • Bookmarks
    • Online Resources
    • Online Resources
  • About
    • About me and this site
    • About me
    • About this site
    • About images credit
  • Email
  • GitHub
  • LinkedIn
  1. Spring Boot Concepts
  2. Spring Boot: JPA Relationships
  • Spring

  • Spring Legacy
    • Spring Framework
    • Spring MVC
    • Springs Servlets

  • Spring Boot Basics
    • Spring Boot: fundamentals
    • Spring Boot: create a Project
    • Spring Boot: H2 DB and Thymeleaf
    • Spring Boot: cycle

  • Spring Boot Concepts
    • Spring Boot: Dependency Injection
    • Spring Boot: Annotations
    • Spring Boot: Controller
    • Spring Boot: View
    • Spring Boot: Thymeleaf
    • Spring Boot: Vaadin Flow
    • Spring Boot: Vaadin Hilla
    • Spring Boot: Model
    • Spring Boot: Rest
    • Spring Boot: Data & DB
    • Spring Boot: JPA & DI
    • Spring Boot: JPA Mappings
    • Spring Boot: JPA Relationships
    • Spring Boot: JPA Queries
    • Spring Boot: JPA Inherence
    • Spring Boot: Scaling

  • Spring Boot Laboratories
    • Lab#SB00-1: Library UML
    • Lab#SB00-2: CRUD User
    • Lab#SB00-3: LibraryManagement
    • Lab#SB00-4: API Rest
    • Lab#SB00-5: Rest & JPA-H2
    • Lab#SB00-6: Rest & MongoDB
    • Lab#SB00-7: Styling

    • Lab#SB01-1: DataBase
    • Lab#SB02-1: JPA Relationships
    • Lab#SB03-1: APIs & cloud
    • Lab#SB04-1: JPA Inherence
    • Lab#SB05-1: API Rest

    • Lab#SB06-1: employeeCourse

    • Lab#SB07-1: monitor Book

    • Lab#SB08-1: Restaurant UML
    • Lab#SB08-2: Vaadin
    • Lab#SB08-3: H2 and API Rest
    • Lab#SB08-4: JPA
    • Lab#SB08-5: Test API Rest

    • Lab#SB09-1: SpringIO Conference

  • Spring Boot readings
    • Spring Boot Resources

On this page

  • 1 JPA Relationships
    • 1.1 OneToMany and ManyToOne
      • 1.1.1 @OneToMany attributes
      • 1.1.2 Casting
    • 1.2 ManyToMany
      • 1.2.1 OrphanRemoval and Cascade
        • 1.2.1.1 Example: Student and Course Entities
      • 1.2.2 ManyToMany with Join Table @Entity
      • 1.2.3 When two objects are equal? Object Identity
      • 1.2.4 Using AssertJ with Comparator
  • 2 Labs
    • 2.1 @OneToMany
    • 2.2 @ManyToOne
    • 2.3 @ManyToMany
  • Edit this page
  • Report an issue
  1. Spring Boot Concepts
  2. Spring Boot: JPA Relationships

Spring Boot: JPA Relationships

Spring Boot JPA Relationships

Spring-Boot
JPA
Spring Boot JPA
Author

albertprofe

Published

Tuesday, June 1, 2021

Modified

Sunday, August 10, 2025

📘 JPA Relationships

Spring Boot provides an implementation of the Java Persistence API (JPA) to simplify database access: ORM (Object-Relational Mapping)

In JPA, entity classes represent tables in the database, and relationships between entities are mapped using annotations.

There are three basic relationships:

  • OneToOne: Represents a single-valued association
  • OneToMany / ManyToOne: Represents a multi-valued association
  • ManyToMany: Represents a multi-valued association where multiple instances


1 JPA Relationships

JPA (Java Persistence API) provides several types of relationships to model associations between entities :

  1. OneToOne: Represents a single-valued association where an instance of one entity is related to a single instance of another entity.

  2. OneToMany: Represents a multi-valued association where an instance of one entity can be related to multiple instances of another entity.

  3. ManyToOne: The inverse of OneToMany, where multiple instances of an entity can be related to a single instance of another entity.

  4. ManyToMany: Represents a multi-valued association where multiple instances of one entity can be related to multiple instances of another entity.

These relationships can be either unidirectional or bidirectional:

  • Unidirectional: Only one entity has a reference to the other.
  • Bidirectional: Both entities have references to each other.

Relationships are typically annotated in entity classes using @OneToOne, @OneToMany, @ManyToOne, or @ManyToMany. Additional annotations like @JoinColumn and mappedBy are used to specify the joining strategy and the owning side of the relationship.

1.1 OneToMany and ManyToOne

Entity @oneToMany

Entity @oneToMany

OneToMany Unidirectional

  • One entity has a collection of another entity
  • Only the owning side (the “One” side) has a reference to the other entity
  • Example: One Department has many Employees
@Entity
public class Department {
    @OneToMany
    private List<Employee> employees;
}

@Entity
public class Employee {
    // No reference to Department
}

1.1.1 @OneToMany attributes

Eager loading fetches all required data upfront when an object is first loaded. It immediately initializes and loads related entities or resources, ensuring everything is readily available.

This approach can improve performance for frequently accessed data but may increase initial load times and memory usage.

Lazy loading, conversely, defers data loading until it’s explicitly requested. It retrieves only the essential data initially, loading related entities or resources on-demand when accessed.

This method can enhance initial performance and reduce memory consumption, particularly for large datasets or infrequently used resources.

However, it may introduce slight delays when accessing lazy-loaded data for the first time

  • fetch: Specifies whether to lazily or eagerly load the related entities. Default is FetchType.LAZY.
  • cascade: Specifies which operations should cascade to child entities. Options include ALL, PERSIST, MERGE, REMOVE, etc.
  • orphanRemoval: If true, removes child entities when they are removed from the collection. Default is false.
  • mappedBy: Specifies the field that owns the relationship in the child entity.
  • optional: If false, a non-null relationship must always exist.
NoteEager vs Lazy

Eager loading fetches all related data immediately, making everything available upfront. It can be faster for frequent access but may use more memory.

Lazy loading, on the other hand, loads related data only when requested, initializing it on-demand. This approach saves memory but might cause slight delays on first access.

Key Points

  • For @OneToMany and @ManyToMany, the default fetch type is LAZY.
  • For @ManyToOne and @OneToOne, the default fetch type is EAGER5.
  • Using FetchType.LAZY is generally recommended to avoid performance issues, especially for collections.
  • The cascade attribute determines which operations should be cascaded from parent to child entities.
  • The orphanRemoval attribute is useful for automatically removing child entities when they are no longer referenced by the parent.

ManyToOne Unidirectional

ManyToOne Unidirectional example: Order and Customer

  • Many entities are associated with one entity
  • Only the owning side (the “Many” side) has a reference to the other entity
  • Example: Many Employees belong to one Department
@Entity
public class Employee {
    @ManyToOne
    private Department department;
}

@Entity
public class Department {
    // No reference to Employee
}

Bidirectional Relationships

Bidirectional Relationships example:

  • Library2: API Rest Author Book @OneToMany CRUD with Swagger
  • TestCreateBookingTable() with JPA, TableRestaurant 1:n Booking bidirectional relationship
  • Both entities have references to each other
  • The “Many” side is usually the owning side
  • Example: One Department has many Employees, and each Employee belongs to one Department
@Entity
public class Department {
    @OneToMany(mappedBy = "department")
    private List<Employee> employees;
}

@Entity
public class Employee {
    @ManyToOne
    private Department department;
}

In bidirectional relationships, use mappedBy on the non-owning side to indicate the owning side’s field name.

NoteWhy Serialization is Needed?

Serialization is the process of converting an object or class into a byte stream. This byte stream can then be easily saved to a file, sent over a network, or stored in a database.

Hibernate uses serialization to create deep copies of entity objects for various purposes, such as detached entities, Session Management or caching.

For example: Collections within entities (like an ArrayList<Menu> menus within a Order entity ) are often serialized to store them efficiently in the database or to manage state changes.

Implementing Serializable is not always the best solution. In some cases, it might be better to adjust your entity relationships (@OneToMany) or use different mapping strategies (@ElementCollection for simple collections).

1.1.2 Casting

The original code avoids these issues by declaring orderToSave directly as TakeAwayOrder, eliminating the need for casting. This approach is generally preferred when possible, as it’s safer and more straightforward.

// Assume OrderRestaurant is a superclass of TakeAwayOrder
OrderRestaurant orderToSave = new TakeAwayOrder(
    "T11", new Date(), "Alice", 1, 10.99,
    true, new ArrayList<>(Arrays.asList(menu1)), null );

// We need to cast here
((TakeAwayOrder) orderToSave).setCustomerTakeAway(customer1);

// We might need to cast here too, depending on the repository's type parameter
takeAwayOrderRepository.save((TakeAwayOrder) orderToSave);
  • Upcasting: When we assigned a TakeAwayOrder object to an OrderRestaurant variable, we performed an implicit upcast. This is always safe because a TakeAwayOrder is an OrderRestaurant.
  • Downcasting: When we cast orderToSave back to TakeAwayOrder, we’re performing a downcast. This is potentially risky because not all OrderRestaurant objects are TakeAwayOrder objects.
NoteWhy casting can be problematic?
  • Type safety: Downcasting can lead to runtime errors if the object isn’t actually of the type you’re casting to.
  • Code readability: Excessive casting can make code harder to read and understand.
  • Performance: While minor, casting does involve a runtime check.

1.2 ManyToMany

Many to many example

Many to many example
  • Library3: API Rest Library Book @ManyToMany @OneToOne CRUD with Swagger

ManyToMany Unidirectional

  • Multiple entities are associated with multiple entities of another type
  • Only one side has a reference to the other entity
  • Example: Many Students can enroll in many Courses
  • @ManyToMany RestaurantManger: ORDER
  • @ManyToMany RestaurantManger: MENU
@Entity
public class Student {
    @ManyToMany
    @JoinTable(name = "STUDENT_COURSE",
        joinColumns = @JoinColumn(name = "STUDENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "COURSE_ID"))
    private Set<Course> courses;
}

@Entity
public class Course {
    // No reference to Student
}

ManyToMany Bidirectional

  • Both entities have references to each other
  • One side is designated as the owning side, the other the inverse side
    • Example: Many Students can enroll in many Courses, and each Course can have many Students
@Entity
public class Student {
    @ManyToMany
    @JoinTable(name = "STUDENT_COURSE",
        joinColumns = @JoinColumn(name = "STUDENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "COURSE_ID"))
    private Set<Course> courses;
}

@Entity
public class Course {
    @ManyToMany(mappedBy = "COURSES")
    private Set<Student> students;
}

In bidirectional ManyToMany relationships, use mappedBy on the non-owning side to indicate the owning side’s field name. The @JoinTable annotation is used to specify the join table details.

NoteManyToMany Considerations
  • ManyToMany relationships often require a join table in the database
  • Consider using an intermediate entity for complex relationships or when additional attributes are needed for the relationship
  • Be cautious of performance implications with large datasets

1.2.1 OrphanRemoval and Cascade

Cascade propagates operations from parent to child entities, while orphanRemoval automatically deletes child entities no longer associated with a parent.

Cascade affects specified actions (e.g., PERSIST, REMOVE), whereas orphanRemoval only deals with removing disassociated children.

TipCascade vs OrphanRemoval

The main differences between cascade and orphanRemoval in JPA are:

  1. Scope of operation:

    • Cascade applies to all operations specified (e.g. PERSIST, MERGE, RE`MOVE, etc.) and propagates them from parent to child entities<.
    • OrphanRemoval only deals with removing child entities that are no longer associated with the parent .
  2. When they take effect:

    • Cascade operations occur when the specified action is performed on the parent entity .
    • OrphanRemoval occurs when a child entity is disassociated from its parent, even without explicitly calling remove.
  3. Use cases:

    • Cascade is useful for propagating operations like persist or remove from parent to children.
    • OrphanRemoval is useful for automatically deleting child entities that are no longer referenced by a parent.
  4. Behavior:

    • CascadeType.REMOVE will only delete child entities when the parent is explicitly removed.
    • OrphanRemoval will delete child entities as soon as they are disassociated from the parent, even if the parent is not removed
  5. Combining them:

    • They can be used together. CascadeType.ALL with orphanRemoval=true provides the most comprehensive cascading behavior.

1.2.1.1 Example: Student and Course Entities

classDiagram
direction RL
    class Student {
        -Long id
        -String name
        -Set<Course> courses
        +enrollInCourse(Course course)
        +dropCourse(Course course)
    }
    class Course {
        -Long id
        -String title
        -Set<Student> students
    }
    class SchoolService {
        -StudentRepository studentRepository
        -CourseRepository courseRepository
        +manageEnrollment()
    }

    Student "*" -- "*" Course : enrolls in
    SchoolService --> Student : uses
    SchoolService --> Course : uses
    SchoolService <-- StudentRepository : uses
    SchoolService <-- CourseRepository : uses

    
    

Let’s see an example involving Student and Course entities in a school system, where orphan removal is meaningful.

Student @Entity owner-side

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, 
    orphanRemoval = true)
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "STUDENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "COURSE_ID")
    )
    private Set<Course> courses = new HashSet<>();

    // Constructors, getters, setters, and utility methods

    public void enrollInCourse(Course course) {
        courses.add(course);
        course.getStudents().add(this); 
        // Maintain bidirectional relationship
    }

    public void dropCourse(Course course) {
        courses.remove(course);
        course.getStudents().remove(this); 
        // Maintain bidirectional relationship
    }
}

Course @Entity inverse-side

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;

    @ManyToMany(mappedBy = "COURSES")
    private Set<Student> students = new HashSet<>();

    // Constructors, getters, setters, and utility methods
}

Here’s how we use these entities in a service or test:

public class SchoolService {

    @Autowired
    private StudentRepository studentRepository;

    @Autowired
    private CourseRepository courseRepository;

    public void manageEnrollment() {
        // Create some courses
        Course math = new Course("Mathematics");
        Course science = new Course("Science");

        // Save courses
        courseRepository.save(math);
        courseRepository.save(science);

        // Create a student and enroll in courses
        Student issac = new Student("Isaac Boncodi");
        issac.enrollInCourse(math);
        issac.enrollInCourse(science);

        // Save the student (this will also
        // save the relationships)
        studentRepository.save(issac);

        // Drop the Science course
        issac.dropCourse(science);

        // Now if we save issac again, the Science course
        // will be removed from the database
        // if no other students are enrolled in it.
        studentRepository.save(issac);
        
        // The Science course will be removed
        // if it's no longer associated with any students.
    }
}
  1. Entities: Student and Course are related through a many-to-many relationship with a join table (student_course).

  2. Orphan Removal: The orphanRemoval = true attribute in the Student class means that if a Student drops a Course, and no other students are enrolled in that course, it will be removed from the database.

  3. Methods:

    • enrollInCourse: Adds a course to a student’s list and maintains the bidirectional relationship.
    • dropCourse: Removes a course from a student’s list and maintains the bidirectional relationship.
  4. Usage: When you drop a course and save the Student, if that course is no longer associated with any other students, it will be deleted from the database.

1.2.2 ManyToMany with Join Table @Entity

@Entity @ManyToMany with Join Table: in this particular case we will use two @OneToMany relationships to create a many-to-many, centered and owned by the join table.

  • Represents a many-to-many relationship using an intermediate entity
  • The join table becomes an entity itself, with two one-to-many relationships
  • Provides more flexibility and allows additional attributes on the relationship
  • Example: Students enrolled in Courses, with additional enrollment information
@Entity
public class Student {
    @OneToMany(mappedBy = "student")
    private List<Enrollment> enrollments;
}

@Entity
public class Course {
    @OneToMany(mappedBy = "course")
    private List<Enrollment> enrollments;
}

@Entity
public class Enrollment {
    @ManyToOne
    private Student student;

    @ManyToOne
    private Course course;

    private LocalDate enrollmentDate;
    private String grade;
}

In this approach:

  • The Enrollment entity serves as the join table
  • It has two @ManyToOne relationships: one to Student and one to Course
  • Additional fields like enrollmentDate and grade can be added to the Enrollment entity
  • Both Student and Course have @OneToMany relationships to Enrollment
  • The mappedBy attribute in @OneToMany indicates the owning side of the relationship

This structure allows for more detailed modeling of the relationship between students and courses, enabling the storage of relationship-specific data and easier querying of the association.

Key Points

This structure allows you to:

  • Add additional fields to the relationship (e.g., enrollmentDate)
  • Easily query the relationship from both sides
  • Maintain better control over the lifecycle of the relationship

1.2.3 When two objects are equal? Object Identity

The difference between comparing objects based on object identity and comparing them based on field values lies in how equality is determined:

  • by identity, that is, by using their memory addresses
  • by their field values

Object Identity

  • Object Identity refers to comparing objects using their memory addresses (i.e., whether they are the same instance in memory).
  • In Java, this is done using the == operator.
  • Two objects are considered equal based on object identity if they refer to the same memory location.

Field Values

  • Field Values refer to comparing objects based on the values of their fields.
  • In Java, this is typically done using the equals method.
  • Two objects are considered equal based on field values if their corresponding fields have the same values, even if they are different instances in memory.

Example:

Consider the following MenuRestaurant class:

public class MenuRestaurant {
    private int id;
    private String name;

    // Constructors, getters, and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MenuRestaurant that = (MenuRestaurant) o;
        return id == that.id && Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

Comparing Based on Object Identity

MenuRestaurant menu1 = 
    new MenuRestaurant(1, "Pizza");
MenuRestaurant menu2 = 
    new MenuRestaurant(1, "Pizza");

System.out.println(menu1 == menu2);
// false, because they are different instances

Comparing Based on Field Values

MenuRestaurant menu1 =
    new MenuRestaurant(1, "Pizza");
MenuRestaurant menu2 =
    new MenuRestaurant(1, "Pizza");

System.out.println(menu1.equals(menu2));
// true, because their fields are equal

1.2.4 Using AssertJ with Comparator

  • public void testAddingMenusToOrder()
  • Heap and Stack

usingElementComparator(Comparator.comparing(MenuRestaurant::getId))

When you use the above expression, you are specifying that the comparison should be based on the id field of the MenuRestaurant objects:

import static org.assertj.core.api.Assertions.assertThat;

List<MenuRestaurant> menuList = Arrays.asList(menu1, menu2, menu3);

assertThat(menuList)
    .usingElementComparator(Comparator.comparing(MenuRestaurant::getId))
    .containsExactlyInAnyOrder(menu1, menu2, menu3);

In this case, the comparison is based on the id field, not the object identity or the default equals method. This allows you to verify that the collection contains the expected elements based on their IDs, regardless of their memory addresses.

2 Labs

Here are some of the most common JPA mappings and relationships used in Spring Boot

2.1 @OneToMany

@OneToMany: This annotation is used to represent a one-to-many relationship between two entities.

For example, if a customer can have multiple orders, you can define a @OneToMany relationship between the Customer entity and the Order entity.

Tip

Example:

API Rest Author-Book @OneToMany CRUD with Swagger

2.2 @ManyToOne

@ManyToOne: This annotation is used to represent a many-to-one relationship between two entities.

For example, if an order belongs to a customer, you can define a @ManyToOne relationship between the Order entity and the Customer entity.

Tip

Example:

Library-Book @ManyToMany and @OneToOne CRUD

2.3 @ManyToMany

@ManyToMany: This annotation is used to represent a many-to-many relationship between two entities.

For example, if a book can have multiple authors and an author can have written multiple books, you can define a @ManyToMany relationship between the Book entity and the Author entity.

TipExample:

API Rest Library-Book-Author @ManyToMany @OneToMany CRUD with Swagger and Inherence

Back to top
Spring Boot: JPA Mappings
Spring Boot: JPA Queries

This website is built with Quarto.

Difficulties are just things to overcome, after all. Ernest Shackleton

  • Edit this page
  • Report an issue