Relacje jeden-do-wielu i wiele-do-wielu
Proszę mapować @OneToMany, @ManyToMany i @JoinTable, konfigurując typy kaskadowania oraz strategie pobierania.
Relacje jeden-do-wielu i wiele-do-wielu to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Typy relacji w JPA
JPA obsługuje cztery typy relacji: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. Każdy z nich odwzorowuje ograniczenia kluczy obcych lub tabel pośredniczących.
@ManyToOne: Strona właścicielska
Relację wiele-do-jednego (np. wiele zamówień należy do jednego użytkownika) mapuje się za pomocą @ManyToOne oraz @JoinColumn dla klucza obcego.
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
}@OneToMany: Strona odwrotna
Strona odwrotna (użytkownik ma wiele zamówień) korzysta z @OneToMany(mappedBy = "user"). Atrybut mappedBy wskazuje pole po stronie właścicielskiej, które przechowuje klucz obcy.
@Entity
public class User {
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
}Pomocnicza metoda zapewniająca spójność dwukierunkową
W przypadku powiązań dwukierunkowych należy aktualizować obie strony relacji w pomocniczej metodzie, aby przed wykonaniem flushowania graf obiektów był spójny.
public void addOrder(Order order) {
orders.add(order);
order.setUser(this); // keep both sides in sync
}
public void removeOrder(Order order) {
orders.remove(order);
order.setUser(null);
}Typy kaskadowania
CascadeType.ALL propaguje wszystkie operacje (persist, merge, remove, refresh, detach) z encji nadrzędnej do podrzędnej. PERSIST i MERGE to często bezpieczne wybory. REMOVE usuwa encje podrzędne po usunięciu encji nadrzędnej.
@OneToMany(mappedBy = "user",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
orphanRemoval = true)
private List<Order> orders;orphanRemoval: Automatyczne usuwanie encji podrzędnych
orphanRemoval = true usuwa encję podrzędną, gdy zostanie usunięta z kolekcji encji nadrzędnej — nawet bez jawnego wywołania entityManager.remove().
user.getOrders().remove(order); // triggers DELETE for the orphaned order on flush@ManyToMany z @JoinTable
Relacja wiele-do-wielu (np. między studentem a kursem) korzysta z tabeli pośredniej. Należy zdefiniować ją po stronie właścicielskiej za pomocą @JoinTable; po stronie odwrotnej używa się mappedBy.
@Entity
public class Student {
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private Set<Course> courses = new HashSet<>();
}Dodatkowe kolumny w tabeli pośredniej
Gdy tabela pośrednia wymaga dodatkowych kolumn (np. daty zapisania na kurs), należy zastąpić @ManyToMany dwiema relacjami @OneToMany / @ManyToOne i utworzyć nową encję reprezentującą tabelę pośrednią.
@Entity
public class Enrollment {
@ManyToOne Student student;
@ManyToOne Course course;
private LocalDate enrolledAt;
}FetchType: LAZY a EAGER
Domyślne pobieranie danych: @ManyToOne i @OneToOne są typu EAGER (dane są ładowane natychmiast). @OneToMany i @ManyToMany są typu LAZY (dane są ładowane przy dostępie). W przypadku kolekcji należy zawsze używać LAZY, aby uniknąć zapytań N+1.
// Always override to LAZY:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;Problem zapytań N+1
Dostęp do leniwej kolekcji wewnątrz pętli powoduje wykonanie jednego zapytania dla każdej encji (N+1). Problem można rozwiązać za pomocą JOIN FETCH w JPQL lub @EntityGraph, aby załadować kolekcję jednym zapytaniem.
// N+1 problem:
List<User> users = repo.findAll();
users.forEach(u -> u.getOrders().size()); // 1 + N queries!
// Fix:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();Set a List w kolekcjach
Należy używać Set dla @ManyToMany (zapobiega to powstawaniu zduplikowanych wierszy w tabeli pośredniej). Należy używać List dla @OneToMany, gdy kolejność ma znaczenie. Nie wolno używać List z @ManyToMany — powoduje to usunięcie i ponowne wstawienie wszystkich wierszy przez Hibernate.
Szybkie sprawdzenie
Co oznacza mappedBy w adnotacji @OneToMany?
Podsumowanie
Należy używać @ManyToOne+@JoinColumn po stronie właścicielskiej (z kluczem obcym). Po stronie odwrotnej należy używać @OneToMany(mappedBy=...). W przypadku kolekcji należy zawsze stosować LAZY. Problem N+1 należy rozwiązywać za pomocą JOIN FETCH. Dla relacji wiele-do-wielu z dodatkowymi kolumnami należy użyć encji pośredniej.
Często zadawane pytania
Czy lekcja „Relacje jeden-do-wielu i wiele-do-wielu” jest bezpłatna?
Tak — pełny tekst „Relacje jeden-do-wielu i wiele-do-wielu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Relacje jeden-do-wielu i wiele-do-wielu”?
Proszę mapować @OneToMany, @ManyToMany i @JoinTable, konfigurując typy kaskadowania oraz strategie pobierania. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Relacje jeden-do-wielu i wiele-do-wielu”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Mapowanie encji za pomocą adnotacji JPA
- Repozytoria Spring Data i metody zapytań
- Relacje jeden-do-wielu i wiele-do-wielu
- Paginacja, sortowanie i projekcje