#15 — Relacje jeden-do-wielu, wiele-do-jednego

By 4 December 2014 Kurs Javy

W tej lekcji poz­namy sposób obsłu­gi relacji jeden-do-wielu za pomocą JPA, różne rodza­je tych relacji oraz pewne ograniczenia i to, co się z nimi wiąże.
Sytu­ac­ja, którą do tej pory omaw­ial­iśmy (mamy tylko jeden typ obiek­tu) w rzeczy­wis­toś­ci wys­tępu­je bard­zo rzad­ko — najczęś­ciej mamy do czynienia z wielo­ma obiek­ta­mi pow­iązany­mi między sobą wielo­ma relac­ja­mi jeden-do wielu. Taką sytu­ację moż­na rozłożyć na poje­dyncze ele­men­ty — relac­je jeden-do-wielu — i nauczyć się radz­ić sobie z poje­dynczą relacją. Więcej takich relacji w sys­temie wyma­ga jedynie powie­le­nia tego schematu :)

Lekcja

UML vs ERD

Częs­to będziemy mieli do czynienia z doku­men­tacją w określonej postaci, ważne jest, żeby umieć z niej korzys­tać. Najczęś­ciej infor­ma­c­je o relac­jach pomiędzy obiek­ta­mi będziemy czer­pać z dia­gra­mu — albo będzie to dia­gram klas (UML) albo będzie to dia­gram bazy danych (ERD). Ważne jest, żeby wiedzieć jakie są róznice pomiędzy nimi:

  • w przy­pad­ku dia­gra­mu ERD, relac­ja ta będzie reprezen­towana przez kolum­nę po stron­ie ‘wiele’, której typ będzie taki sam jak typ klucza głównego tabeli po stron­ie ‘jeden’, a nazwa najczęś­ciej ma postać {nazwa_tabeli_po_stronie_jeden}_id lub podobną
  • w przy­pad­ku dia­gramów klas (UML), po stron­ie ‘jeden’ będziemy mieli kolekcję (Listę lub Set — patrz niżej) obiek­tów, a po stron­ie ‘wiele’ relacji będziemy mieli pole określonego typu

Dowol­na z powyższych infor­ma­cji jest wskaźnikiem, że mamy do czynienia z relacją jeden-do-wielu. Najczęś­ciej taka relac­ja powin­na być także oznac­zona lin­ią na dia­gramie (oraz opcjon­al­nie, opisana).

Relacja JDW w JPA — mapowanie

W JPA relac­je takie mapu­je­my za pomocą 2 adno­tacji — @OneToMany oraz @ManyToOne. Adno­tację @ManyToOne może­my połączyć dodatkowo z adno­tacją @JoinColumn. Zobaczmy na przykładach:


@Entity
public class StronaJeden {
    
    @Id
    Long id;
    
    @OneToMany(mappedBy="stronaJeden")
    List stronyWiele;
}

@Entity
public class StronaWiele {
    
    @Id
    Long id;
    
    @ManyToOne
    @JoinColumn("strona_jeden_id")
    StronaJeden stronaJeden;
}

To, na co powin­niśmy zwró­cić uwagę, to para­me­ter mapped­By — może­my go wskazać zarówno dla adno­tacji @ManyToOne jak i dla adno­tacji @OneToMany, ale dla jed­nej relacji, powinien on być uży­ty tylko raz. Para­metr ten mówi, które pole w drugiej klasie odpowia­da tej samej relacji. Jest to uży­wane do budowa­nia obiek­tów na pod­staw­ie danych z bazy danych.

W powyższym przykładzie widz­imy też adno­tację @JoinColumn — jest ona opcjon­al­na, może­my jej użyć do określe­nia, jak ma wyglą­dać kolum­na w bazie danych, która odpowia­da za tą relację (dzi­ała to podob­nie jak adno­tac­ja @Column)

Ta relac­ja jest dwukierunk­owa (bi-direc­tion­al) tzn. obie klasy mają pola, które reprezen­tu­ją tą relację. Możli­we jest opisanie jej tylko po jed­nej stron­ie, ale nie będziemy się na chwilę obec­ną zaj­mować tego typu przykładem.

Rodzaje kolekcji

Po stron­ie ‘jeden’ relacji mamy do wyboru dwa rodza­je kolekcji — Set, który jest nieu­porząd­kowany oraz List, która jest uporząd­kowana. W przy­pad­ku listy, może­my wskazać dodatkowy para­metr — nazwę pola — na pod­staw­ie którego ma być sor­towana (para­metr order­By w adno­tacji @OneToMany), dzię­ki czemu móże­my sor­tować np. na pod­staw­ie nazwy albo jakiegoś innego atrybutu

FetchType — ładowanie kolekcji

Obie te adno­tac­je (@OneToMany oraz @ManyToOne) mają jeszcz jed­ną opcję — fetch . Jest to bard­zo ważny ele­ment, który domyśl­nie ma wartość LAZY dla @OneToMany oraz EAGER dla @ManyToOne.
Para­metr ten decy­du­je, kiedy obiekt/obiekty w tym polu powin­ny zostać pobrane:

  • EAGER — pobierz w momen­cie wykony­wa­nia zapytania
  • LAZY — pobierz dopiero kiedy będzie pier­wsze odwołanie do tego obiektu

Każ­da z opcji ma swo­je wady i zale­ty. Należy bard­zo uważać z EAGER, ponieważ może­my przez przy­padek spowodowac pobranie całej bazy danych w jed­nym zapy­ta­niu. W przy­pad­ku LAZY, pobieranie ‘na żądanie’ zadzi­ała tylko, jeżeli jesteśmy w ramach transakcji (metody z adno­tacją @Transactional, a enc­ja jest zarządzana — najczęś­ciej jest to po pros­tu miejsce, w którym pobier­amy tą encję z bazy danych). Nalezy więc bard­zo dokład­nie prze­myśleć jak będziemy korzys­tać z danych oraz jaki ma to wpływ na ilosć danych, które pobier­amy. Najczęś­ciej domyślne ustaw­ienia są w zupełnoś­ci wystarczające

Zadanie

Dodaj do swo­jej aplikacji obsługę ‘zabawek’ kota

  • dodawanie i usuwanie powin­no być dostęp­ne na ekranie szczegółów kota
  • dla uproszczenia nie zakładamy edy­cji zabaw­ki — może­my ją usunąć i dodać ponownie
  • zarówno dodawanie jak i usuwanie powin­no odby­wać się w osob­nym kon­trol­erze (Zabaw­ki­Con­troller) i po wyko­na­niu czyn­noś­ci wracać do ekranu szczegółów kota

progres

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!