Relacja One-to-many to powiązanie jednego wpisu w bazie danych do kilku innych, znajdujących się w innej tabeli. Przyjrzyjmy się temu na prostym przykładzie aplikacji rezerwującej pokoje dla kilku gości.
Zdarza się, że rezerwacja pokoju jest na więcej niż jednego gościa. W klasie Reservation przygotujmy się na taką ewentualność zmieniając jednego gościa na całą listę gości. Logika podpowiada, że relację @OneToOne zastępujemy @OneToMany.
Zatem poprawiony kod będzie wyglądał tak:
@Entity public class Reservation { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @OneToMany private List<Guest> guests; public Reservation(List<Guest> guest) { this.guests = guest; } }
Poprawa kodu
Do celu pokazania jak działa relacja One-to-many stwórz drugiego gościa w klasie App.
W metodzie createReservation() zmień argument na Arrays.asList() z naszymi goścmi jako argumentami.
W createReservation() zmień argumenty na listę gości.
Usuń wypisywanie na ekran komunikatu o gościu i jego ID – nie potrzebujemy na tym się skupiać w tym ćwiczeniu.
Wspólny kod będzie uproszczony. Za gości posłużą guest i guest2. Wygląda więc tak:
Uwaga: Pracując nad prawdziwą aplikacją nie nazywajmy w ten sposób zmiennych!
package pl.clockworkjava.hotelreservation.jpa; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import java.util.Arrays; public class App { private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit"); private static EntityManager em = factory.createEntityManager(); public static void main(String[] args) { GuestRepository guestRepository = new GuestRepository(em); ReservationRepository reservationRepository = new ReservationRepository(em); guestRepository.createNewGuest("Paweł", 34); guestRepository.createNewGuest("Kinga", 37); Guest guest = guestRepository.findById(1l); Guest guest2 = guestRepository.findById(2l); reservationRepository.createReservation(Arrays.asList(guest, guest2)); } }
package pl.clockworkjava.hotelreservation.jpa; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import java.util.Arrays; import java.util.List; public class ReservationRepository { private EntityManager em; public ReservationRepository(EntityManager em) { this.em = em; } public void createReservation(List<Guest> guests) { Reservation r = new Reservation(guests); EntityTransaction transaction = em.getTransaction(); transaction.begin(); em.persist(r); transaction.commit(); } }
Jeśli wszystko napisaliśmy jak należy to po odpaleniu zobaczymy między innymi takie linijki w konsoli:
W obecnej sytuacji potrzebna jest tabela pośrednicząca Reservation_Guest – zgodnie z zasadami budowy baz danych. Hibernate „pomyślał” o tym za nas i stworzył nie dwie, a trzy tabele.
Zatem przyjrzyjmy się obecnej sytuacji na grafie.
Tabela łączona Reservation_Guest przechowuje ID rezerwacji i łączy ją z ID gościa. Reservation_ID może się powtarzać, a Guest_ID musi być unikalne. Reservation_ID wskazuje na tabelę Reservation, która zostałą zredukowana do jednej kolumny. Guest_ID wskazuje na dane gościa w tabeli Guest.
Tabela, która łączy nasze tabele w relacji One-to-many jest naszą tabelą główną. To ona wskazuje nam odpowiednie pola w pozostałych dwóch tabelach.
Prześledzmy jeszcze raz działanie aplikacji i obecnego kodu.
Na początku tworzymy pierwszego i drugiego gościa oraz szukamy ich po ID. Następnie tworzymy rezerwację:
W pierwszym insert dajemy ID do tabeli Reservation, a dwa kolejne insert dodają dane do tabeli Reservation_Guest.
Podsumowanie
Relacja One-to-many to powiązanie jednej tabeli z kilkoma. Taka tabela wiążąca pozostałe jest naszą tabelą główną. Hibernate tworzy ją sam, dzięki czemu na tym etapie nie musimy zagłębiać się w szczegóły. Dodatkowo, dzięki temu rozwiązaniu jesteśmy w stanie zbudować bazę danych zgodne z zasadami dla relacyjnych baz danych.
Kod aplikacji znajdziesz w publicznym repozytorium GitHub.
By być na bieżąco i mieć realny wpływ na tematykę tworzonych artykułów zapraszam do dołączenia do mojego newslettera.
Informacje oparte zostały o materiał darmowego kursu wprowadzającego w świat JPA i Hibernate „Fundamenty JPA i Hibernate” dostępnego w formie wideo na platformie ClockworkJava.
Fajny, krótki wpis do kawy. To lubię 🙂
Można rozszerzyć wpis o to, że nie trzeba wcale wprowadzać kolejnej tabeli pośredniczącej, jeżeli nie ma takiej potrzeby. Można dodać adnotację @JoinColumn i oprzeć się na dwóch tabelach.
Dzięki za cenną uwagę.
Cykl jest dedykowany do omówienie podstaw i na prostych przypadkach lepiej się je przedstawia. To jeszcze nie koniec w tym temacie więc – zanotowałam.