Welcome to this lesson on Entity Relationships in Spring Data JPA. In the previous lessons, we've covered the basics of JPA repositories and entities, using derived queries for simple methods, and creating query methods for more complex queries. In this lesson, we're going to discuss how to work with relationships between entities in Spring Data JPA.
Before diving into implementation, it's essential to understand the different types of relationships in the context of data persistence with Spring Data JPA:
- One-to-One: For example, a person and their passport. Each person has one passport, and each passport belongs to one person.
- One-to-Many: For example, one person can be assigned to many different ToDos, but one ToDo cannot be assigned to multiple people.
- Many-to-One: This is just the previous example but viewed from the opposite direction — many ToDos are assigned to the same person.
- Many-to-Many: For example, Google Docs and collaborators. Each person can work on many docs, and each doc can have many collaborators.
Let’s start with the One-to-One relationship. Here is a diagram to illustrate the One-to-One relationship between a person and their passport.

Explanation:
- PERSON and PASSPORT tables are shown with a one-to-one relationship.
- Each
PERSONhas onePASSPORTand eachPASSPORTbelongs to onePERSON. - The
||--||notation indicates this one-to-one relationship. - Both tables contain an ID, and there are foreign keys (
passportIdinPERSONandpersonIdinPASSPORT) representing the relationship.
Let's see how we can define a One-to-One relationship using JPA annotations:
In the Person class, the @OneToOne annotation indicates a one-to-one relationship with the Passport class. The mappedBy attribute specifies the field in the Passport class that owns the relationship. The cascade = CascadeType.ALL attribute indicates that all JPA-related changes (like persist, merge, remove, etc.) made to the Person entity should cascade to the associated Passport entity. The fetch = FetchType.LAZY attribute indicates that the Passport entity should be lazily loaded, meaning it will be fetched from the database only when explicitly accessed.
In the Passport class, the @OneToOne annotation defines the other end of the relationship with the class. The annotation is used to specify the foreign key column () in the table that maps to the primary key of the table.
Next, let's explore the One-to-Many relationship. Here is a diagram to illustrate the One-to-Many relationship where many ToDos are assigned to the same person.

Explanation:
- PERSON and TODO_ITEM tables are shown with a one-to-many relationship.
- Each
PERSONcan have multipleTODO_ITEMentries, but eachTODO_ITEMbelongs to a singlePERSON. - The
||--o|notation indicates this one-to-many relationship. - The
PERSONtable includes a list ofTODO_ITEMIDs to show the relation, while eachTODO_ITEMincludes apersonIdrepresenting thePERSONit belongs to.
Here’s how we can define a One-to-Many relationship using JPA annotations:
In the Person class, @OneToMany indicates a one-to-many relationship with the TodoItem class, using lazy fetching and cascading all operations. In the TodoItem class, @ManyToOne defines the other end of the relationship, with a foreign key specified by @JoinColumn.
While implementing entity relationships, it's crucial to consider how related entities are fetched from the database. In the previous code snippets, you might have noticed the usage of fetch = FetchType.LAZY. This is an example of a fetching strategy. In JPA, fetching strategies determine how related entities are loaded from the database when their parent entity is retrieved. Fetching strategies can be applied to associations annotated with @OneToOne, @OneToMany, @ManyToOne, and @ManyToMany.
There are two primary fetching strategies:
-
Eager Fetching: When a parent entity is retrieved, all related entities are loaded simultaneously. This is specified using
fetch = FetchType.EAGER. This can result in immediate fetching of all the data necessary at the expense of potentially loading a lot of unnecessary data, leading to performance issues. Note that eager fetching is the default behavior for@ManyToOneand@OneToOnerelationships. -
Lazy Fetching: When a parent entity is retrieved, related entities are not loaded until they are explicitly accessed. This is specified using
fetch = FetchType.LAZY. This can improve performance by only loading the data that is actually needed at the moment but can lead to additional queries to the database. Lazy fetching is the default behavior for@OneToManyand@ManyToManyrelationships.
Finally, let's delve into the Many-to-Many relationship. Here is a diagram to illustrate the Many-to-Many relationship between Google Docs and collaborators.

Explanation:
- PERSON and DOCUMENT tables are shown with an intermediary join table called PERSON_DOCUMENT which implements the many-to-many relationship.
- Each
PERSONcan work on multipleDOCUMENTentries, and eachDOCUMENTcan have multiplePERSONcollaborators. - The
||--o|notation on both sides of the relationship indicates this many-to-many relationship. - The intermediary table
PERSON_DOCUMENTcontains foreign keyspersonIdanddocumentIdrepresenting the relationship.
Here's how we can define a Many-to-Many relationship using JPA annotations:
In the Person class, the @ManyToMany annotation defines a many-to-many relationship with the Document class. The fetch = FetchType.LAZY specifies that the relationship should be lazily loaded. The @JoinTable annotation is used to specify the join table used for this relationship. The name attribute of @JoinTable defines the name of the join table (person_document), while joinColumns and inverseJoinColumns define the foreign key columns that reference the PERSON and DOCUMENT tables, respectively.
In this lesson, we explored various types of entity relationships in Spring Data JPA, including One-to-One, One-to-Many and Many-to-Many relationships. We illustrated these relationships with diagrams and provided code examples to show how each relationship type can be implemented using appropriate JPA annotations. By understanding these relationships, you can design more complex and efficient database schemas for your Spring Boot applications. In the next section, you will have the opportunity to practice these concepts with hands-on exercises.
