🎯 Objective
Understand how to model relationships between entities in a Spring Boot application using JPA annotations, including:
- One-to-One
- One-to-Many
- Many-to-One
- Many-to-Many
🧱 Example Domain
Let’s model a simple domain with the following:
User
can have oneProfile
User
can have manyOrders
- Each
Order
has oneUser
Order
can have multipleProduct
entries and vice-versa
1️⃣ One-to-One: User and Profile
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private Profile profile;
// Getters & Setters
}
@Entity
public class Profile {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String address;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
// Getters & Setters
}
🔍 Notes:
@OneToOne
: Defines a 1-1 relation.mappedBy
: MakesUser
the parent side.cascade = CascadeType.ALL
: Saves profile automatically when user is saved.
2️⃣ One-to-Many and Many-to-One: User and Orders
@Entity
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNumber;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
// Getters & Setters
}
@Entity
public class User {
// ... previous fields ...
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
// Getters & Setters
}
🔍 Notes:
@OneToMany
withmappedBy
points touser
field inOrder
.- Always use
@JoinColumn
on the owning side (@ManyToOne
).
3️⃣ Many-to-Many: Order and Product
@Entity
public class Product {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "products")
private List<Order> orders = new ArrayList<>();
// Getters & Setters
}
@Entity
public class Order {
// ... previous fields ...
@ManyToMany
@JoinTable(name = "order_product",
joinColumns = @JoinColumn(name = "order_id"),
inverseJoinColumns = @JoinColumn(name = "product_id")
)
private List<Product> products = new ArrayList<>();
// Getters & Setters
}
🔍 Notes:
@JoinTable
defines the mapping table.- Use
mappedBy
to indicate inverse side.
🔄 CascadeType Explained
Cascade Type | Description | Use Case |
---|---|---|
ALL | Applies all cascading operations (PERSIST , MERGE , REMOVE , REFRESH , DETACH ) | Full lifecycle control |
PERSIST | Saves associated child entities when parent is saved | Add new children with parent |
MERGE | Merges state of associated child entities when parent is updated | Used with EntityManager.merge() |
REMOVE | Deletes associated child entities when parent is deleted | Cascade delete |
REFRESH | Reloads associated child entities from the database | Keep in sync with DB |
DETACH | Detaches child entities when parent is detached from persistence context | Useful in detached sessions |
🧭 FetchType Strategy
EAGER
: Load immediately with parent (default for @OneToOne & @ManyToOne)LAZY
: Load only when accessed (default for @OneToMany & @ManyToMany)
➡️ Best practice: Use LAZY
and fetch explicitly with queries.
➡️ Next Up: Part 7 – Custom Queries with Spring Data JPA