Relationship Behavior

Back

Loading concept...

Data Persistence Basics: Relationship Behavior 🔗

The Friendship Analogy

Imagine you have friends. Some friendships go both ways — you know them, and they know you. Other friendships are one-way — maybe you follow a celebrity who doesn’t know you exist!

Database relationships work exactly the same way. Let’s explore how entities (like tables) become friends in Jakarta EE!


🔄 Bidirectional Relationships

What Is It?

A bidirectional relationship means BOTH sides know about each other.

Think of it like best friends:

  • You can call your best friend
  • Your best friend can call you back
  • Both of you have each other’s phone number!

Simple Example

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

    @OneToMany(mappedBy = "author")
    private List<Book> books;
}

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

    @ManyToOne
    private Author author;
}

What’s happening here?

  • Author knows about their books
  • Book knows about its author
  • Both sides can find each other!

The Golden Rule 🌟

One side must be the “owner” of the relationship. The owner is the side WITHOUT mappedBy.

graph TD A["Author"] -->|knows about| B["Books"] B -->|knows about| A B -.->|Owner - has FK| DB["&#35;40;Database&#35;41;"]

➡️ Unidirectional Relationships

What Is It?

A unidirectional relationship means only ONE side knows about the other.

Think of it like following a celebrity:

  • You know who Taylor Swift is
  • Taylor Swift doesn’t know you exist
  • Only you have her “phone number” (not the other way around!)

Simple Example

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

    @ManyToOne
    private School school;
}

@Entity
public class School {
    @Id
    private Long id;
    private String name;
    // No reference to students!
}

What’s happening here?

  • Student knows which School they attend
  • School has NO IDEA which students are enrolled
  • Information flows in ONE direction only
graph LR S["Student"] -->|knows about| SC["School"] SC -.->|doesn't know| S

When to Use Each?

Use This When…
Bidirectional You need to navigate both ways
Unidirectional You only query from one direction

🌊 Cascade Operations

What Is It?

Cascade means “when something happens to me, the same thing happens to my friends!”

Think of it like dominoes:

  • You push one domino
  • All connected dominoes fall too!

The Cascade Types

Type What It Does
PERSIST Save me → Save my friends too
REMOVE Delete me → Delete my friends too
MERGE Update me → Update my friends too
REFRESH Reload me → Reload my friends too
ALL Everything above!

Simple Example

@Entity
public class Order {
    @Id
    private Long id;

    @OneToMany(cascade = CascadeType.ALL)
    private List<OrderItem> items;
}

What’s happening here?

  • When you save an Order, all OrderItems get saved automatically!
  • When you delete an Order, all OrderItems vanish too!
graph TD A["Save Order"] -->|CASCADE| B["Save Item 1"] A -->|CASCADE| C["Save Item 2"] A -->|CASCADE| D["Save Item 3"]

⚠️ Be Careful!

CascadeType.REMOVE can be dangerous! Deleting a parent might delete things you wanted to keep!


🎒 Fetch Types

What Is It?

Fetch type decides WHEN to load related data.

Think of it like packing for a trip:

  • EAGER: Pack EVERYTHING now, just in case!
  • LAZY: Pack only what you need, grab more later if needed

The Two Types

// EAGER: Load books immediately with author
@OneToMany(fetch = FetchType.EAGER)
private List<Book> books;

// LAZY: Load books only when accessed
@OneToMany(fetch = FetchType.LAZY)
private List<Book> books;

Visual Comparison

graph TD subgraph EAGER E1["Load Author"] --> E2["Load ALL Books"] E2 --> E3["Return Everything"] end subgraph LAZY L1["Load Author"] --> L2["Return Author Only"] L2 -.->|Later, if needed| L3["Load Books"] end

Default Fetch Types

Relationship Default
@OneToOne EAGER
@ManyToOne EAGER
@OneToMany LAZY
@ManyToMany LAZY

😱 Lazy Loading Pitfalls

The Session Closed Problem

The #1 mistake with lazy loading!

The Problem:

// Inside a transaction
Author author = authorRepo.findById(1L);
// Transaction ends here...

// Outside transaction - BOOM! 💥
author.getBooks(); // LazyInitializationException!

Why does this happen?

  • LAZY means “I’ll load later”
  • But “later” needs a database connection
  • The connection closed when the transaction ended!

Think of it like this:

You order pizza for “later delivery”. But when “later” arrives, the pizza shop is CLOSED! 🍕❌

Solutions

1. Fetch eagerly when you need it:

@Query("SELECT a FROM Author a " +
       "JOIN FETCH a.books WHERE a.id = :id")
Author findWithBooks(@Param("id") Long id);

2. Keep the session open:

@Transactional
public void processAuthor(Long id) {
    Author author = authorRepo.findById(id);
    // Access lazy collections INSIDE transaction
    author.getBooks().size(); // Works!
}

🐌 The N+1 Query Problem

What Is It?

The N+1 problem is when you accidentally make WAY more database calls than needed!

The Story

Imagine you’re a teacher with 30 students. You want to know each student’s grade.

Bad Way (N+1):

  1. Ask the office for the list of 30 students (1 query)
  2. Walk to each student’s desk, one by one (30 queries!)
  3. Total: 31 queries! 😰

Good Way:

  1. Ask the office for all students WITH their grades (1 query)
  2. Total: 1 query! 😊

Code Example

The Problem:

List<Author> authors = authorRepo.findAll();
// 1 query for authors

for (Author author : authors) {
    author.getBooks(); // 1 query PER author!
}
// If 100 authors = 101 queries! 💀

The Solution - JOIN FETCH:

@Query("SELECT a FROM Author a " +
       "JOIN FETCH a.books")
List<Author> findAllWithBooks();
// Just 1 query! 🚀

Visual of N+1

graph TD A["Query: Get 5 Authors"] --> B["Author 1"] A --> C["Author 2"] A --> D["Author 3"] A --> E["Author 4"] A --> F["Author 5"] B --> B1["Query: Get Books"] C --> C1["Query: Get Books"] D --> D1["Query: Get Books"] E --> E1["Query: Get Books"] F --> F1["Query: Get Books"] style A fill:#ff6b6b style B1 fill:#ffd93d style C1 fill:#ffd93d style D1 fill:#ffd93d style E1 fill:#ffd93d style F1 fill:#ffd93d

6 queries instead of 1! That’s the N+1 problem.


🎯 Quick Summary

Concept One-Line Summary
Bidirectional Both sides know each other
Unidirectional Only one side knows
Cascade Actions ripple to related entities
Fetch EAGER Load everything now
Fetch LAZY Load later when needed
Lazy Pitfall Session closes before you access data
N+1 Problem Too many queries, use JOIN FETCH

🌟 You’ve Got This!

Remember:

  • Relationships are like friendships — some go both ways, some don’t
  • Cascades are like dominoes — push one, others follow
  • Fetch types are like packing — eager packs everything, lazy packs light
  • Always keep your session open for lazy loading
  • Use JOIN FETCH to avoid the N+1 trap

Now you understand how entities relate to each other in Jakarta EE! 🎉

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.