Module 16: Database Integration and Persistence
Entity Relationships, Fetch Plans, and N+1 Queries
Map entity relationships carefully so your object graph and query behavior remain understandable. You will cover one-to-many and many-to-one mappings, owning sides, fetch strategies, and the common N+1 query problem that appears when relationships are used carelessly.
Author
Java Learner Editorial Team
Reviewer
Technical review by Java Learner
Last reviewed
2026-04-17
Java version
Java 25 LTS
Learning goals
- Model simple entity relationships with clear ownership
- Understand the trade-offs between lazy and eager loading
- Recognize why relationship mapping can create accidental extra queries
Before you start
- You are comfortable with classes, exceptions, and writing small multi-class Java programs
Lesson roadmap: Start with the mental model, then follow the design choices, common pitfalls, and the practical workflow you should apply in a real project.
Entity relationships mirror data relationships: Orders have items, users have profiles, departments have employees.
Owning side and join column details matter: The mapping is not just decoration; it defines how the ORM writes and reads relationships.
Lazy vs eager loading changes runtime behavior: Fetching too much data hurts performance, but fetching too little at the wrong time can cause extra queries or missing-session issues.
Advanced habit: Treat relationship mapping and fetch behavior as part of performance design, not only as annotations to “make it work.”
Relationship mapping is design work: The annotations are small, but the choices behind them affect query count, transaction behavior, serialization, and how easy the domain model is to reason about.
Owning side matters: In bidirectional relationships, one side is responsible for updating the foreign-key relationship in the database. If you do not know which side owns the mapping, updates become confusing fast.
Lazy vs eager: Lazy loading keeps initial queries smaller but may trigger additional queries later. Eager loading can simplify some reads but may load more data than the use case needs.
N+1 warning: Loading a list of parent entities and then touching a lazily loaded child collection for each one can silently explode the number of queries. Advanced persistence work includes thinking about fetch plans, joins, and data shape deliberately.
How to study this module: Keep a simple domain in mind, such as users, orders, or tasks. It is easier to understand JDBC, transactions, and ORM mapping when every SQL statement serves a clear business action.
Code review mindset: Database code is not only about “does it work.” Reviewers also look for resource safety, transaction boundaries, predictable query counts, and whether the persistence layer leaks too much SQL detail into higher layers.
Production habit: Measure query behavior early. Slow queries, long transactions, and leaked connections usually hurt real systems more than syntax mistakes do.
Runnable examples
One user can have many orders
@OneToMany(mappedBy = "user")
private List<Order> orders = new ArrayList<>();Expected output
The user entity now models a one-to-many relationship with orders.
A many-to-one link from order to user
@ManyToOne
private User user;Expected output
Each order points to one user, while the user side can choose whether it also exposes a collection of orders.
Common mistakes
Adding bidirectional mappings everywhere by default
Only expose both directions when the domain and use cases genuinely need both directions.
Serializing entities with lazy relationships directly from controllers
Prefer DTOs or explicit fetch strategies so the API contract does not accidentally trigger extra database work.
Mini exercise
Model a simple `Author` and `Book` relationship. Decide which side owns the relationship, whether both sides need navigation, and what fetch behavior is safer by default.
Summary
- Relationships are part of the object model and query behavior.
- Fetch strategy changes runtime performance.
- Mapping decisions should match real access patterns.
- Relationship mapping affects both object design and query behavior.
- Lazy loading is useful, but you must still plan query shape to avoid N+1 surprises.
Next step
Close the module by applying JDBC or JPA in a CRUD-focused service project.
Sources used