JavaRush /Blog Java /Random-PL /Podmioty JPA i relacje DB
Nikita Koliadin
Poziom 40
Днепр

Podmioty JPA i relacje DB

Opublikowano w grupie Random-PL

Podmioty JPA i relacje DB

Dzień dobry, koledzy!
Podmioty JPA i relacje DB - 1
Ten materiał jest przeznaczony dla tych, którzy mają już wiedzę na temat organizacji baz danych (dalej po prostu DB - „Baza danych”), minimalną wiedzę na temat działania mapowania obiektowo-relacyjnego (zwanego dalej po prostu ORM ) i jego implementacji, takich jak Hibernate / JPA . Jeśli nie jesteś z tym zaznajomiony, radzę zacząć od JDBC , a dopiero potem przejść do modelu ORM. Ostrzegałem Cię i nie ponoszę odpowiedzialności za Twoją psychikę po przeczytaniu tego artykułu bez odpowiedniego przygotowania! :) Zacznijmy zajmować się wszystkim w porządku. Na początek zagłębimy się trochę w teorię, tylko trochę. Po drugie, wymyślimy, jak zrobić to gówno w ulubionej przez wszystkich Javie. Napiszemy także z Tobą ściągawkę projektową, która ugruntuje nasze zrozumienie tematu i posłuży jako szablon JAK powinno się mapować . Więc zróbmy to!

Co to jest byt?

Byt to obiekt z prawdziwego życia (na przykład samochód) posiadający atrybuty (drzwi, KOŁA , silnik). Jednostka DB: W tym przypadku nasza jednostka jest przechowywana w bazie danych, wszystko jest proste. Dlaczego i jak umieściliśmy samochód w bazie danych - sprawdzimy później.

Co to są relacje DB?

Dawno temu, w odległym królestwie, powstał relacyjny DB . W tym DB dane zaprezentowano w formie tabel. Ale nawet osioł ze Shreka zrozumiał, że konieczne jest stworzenie mechanizmu łączącego te stoły. W rezultacie pojawiły się 4 relacje DB :
  1. Jeden na jednego
  2. Jeden za dużo
  3. Wiele do jednego
  4. Wiele do wielu
Jeśli widzisz to wszystko po raz pierwszy, jeszcze raz ostrzegam – będzie coraz gorzej: pomyśl o wyjściu na spacer. Przeanalizujemy wszystkie te relacje na przykładzie i zrozumiemy różnicę między nimi.

Przykład horroru

Będziemy mieli jeden projekt, który będzie miał 5 gałęzi: master, w którym będzie opis projektu, oraz 1 gałąź dla każdej relacji DB. Każda gałąź będzie zawierać skrypty SQL służące do tworzenia bazy danych i wypełniania jej danymi testowymi, a także klasę Entity z mapowaniem adnotacji. Dla każdej gałęzi będzie również dostępny plik konfiguracyjny Hibernacji. W projekcie wykorzystam osadzoną bazę danych H2 , aby nie rozpraszać się indywidualnymi aspektami bazy danych w chmurze lub zewnętrznej bazy danych. Klikając w link, zainstaluj H2 DB w swoim odkurzaczu. Opiszę każdy krok w 1 gałęzi, reszta to tylko kluczowe punkty. Na koniec dokonamy podsumowania. Iść. To jest link do głównej gałęzi mojego projektu.

Relacja jeden do jednego

Link do oddziału tutaj .
  1. Musimy podłączyć H2 DB do naszego projektu. Tutaj musimy podkreślić, że potrzebujemy Ultimate IDEA, aby wygodnie pracować z DB i innymi rzeczami. Jeśli już go masz, przejdź bezpośrednio do połączenia DB. Przejdź do zakładki Baza danych i wykonaj czynności jak na zrzucie ekranu:

    Podmioty JPA i relacje DB - 2

    Następnie przechodzimy do ustawień DB. Możesz wprowadzić swoje dane, a nawet swój system DBMS; powtarzam, dla uproszczenia używam H2 DB.

    Podmioty JPA i relacje DB - 3

    Następnie skonfigurujmy obwód. Ten krok jest opcjonalny, ale zalecany, jeśli masz wiele schematów w bazie danych.

    Podmioty JPA i relacje DB - 4

    Zastosuj ustawienia, a na koniec powinniśmy otrzymać coś takiego:

    Podmioty JPA i relacje DB - 5
  2. Stworzyliśmy bazę danych i skonfigurowaliśmy dostęp do niej z poziomu IDEA. Teraz musisz utworzyć w nim tabele i wypełnić je pewnymi danymi. Na przykład wezmę dwa byty: Autor i Książka. Książka może mieć autora, wielu autorów lub może nie mieć jednego. W tym przykładzie utworzymy wszystkie typy połączeń. Ale w tym momencie - relacja jeden do jednego. Utwórzmy odpowiedni skrypt, który utworzy tabele DB :

    DROP TABLE IF EXISTS PUBLIC.BOOK;
    
    CREATE TABLE PUBLIC.BOOK (
      ID         INTEGER      NOT NULL AUTO_INCREMENT,
      NAME       VARCHAR(255) NOT NULL,
      PRINT_YEAR INTEGER(4)   NOT NULL,
      CONSTRAINT BOOK_PRIMARY_KEY PRIMARY KEY (ID)
    );
    
    DROP TABLE IF EXISTS PUBLIC.AUTHOR;
    
    CREATE TABLE PUBLIC.AUTHOR (
      ID          INTEGER      NOT NULL AUTO_INCREMENT,
      FIRST_NAME  VARCHAR(255) NOT NULL,
      SECOND_NAME VARCHAR(255) NOT NULL,
      BOOK_ID     INTEGER      NOT NULL UNIQUE,
      CONSTRAINT AUTHOR_PRIMARY_KEY PRIMARY KEY (ID),
      CONSTRAINT BOOK_FOREIGN_KEY FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID)
    );

    I wykonajmy to:

    Podmioty JPA i relacje DB - 6

    Wynik wykonania w konsoli:

    Podmioty JPA i relacje DB - 7

    Wynik w DB:

    Podmioty JPA i relacje DB - 8
  3. Spójrzmy na schemat naszych tabel. Aby to zrobić, RMB na naszej bazie danych:

    Podmioty JPA i relacje DB - 9

    Wynik:

    Podmioty JPA i relacje DB - 10

    Na diagramie UML widzimy wszystkie klucze podstawowe i obce, widzimy także powiązania pomiędzy naszymi tabelami.

  4. Napiszmy skrypt, który wypełni naszą bazę danych danymi testowymi:

    INSERT INTO PUBLIC.BOOK (NAME, PRINT_YEAR)
    VALUES ('First book', 2010),
           ('Second book', 2011),
           ('Third book', 2012);
    
    INSERT INTO PUBLIC.AUTHOR (FIRST_NAME, SECOND_NAME, BOOK_ID)
    VALUES ('Pablo', 'Lambado', 1),
           ('Pazo', 'Zopa', 2),
           ('Lika', 'Vika', 3);

    Co się dzieje? Relacja jeden do jednego jest potrzebna, gdy element jednej tabeli jest powiązany z elementem innej tabeli (lub w ogóle nie jest powiązany, jeśli z BOOK_ID usunięto wartość NOT NULL). W naszym przykładzie jedna książka MUSI mieć jednego autora. Żaden inny sposób.

  5. Teraz najciekawsze jest to, jak połączyć klasę Java z jednostkami DB? Bardzo prosta. Stwórzmy dwie klasy Book i Author. Na przykładzie przeanalizuję klasę 1 i kluczowe pola komunikacji. Weźmy jako przykład klasę Author :

    @Data
    @Entity
    @DynamicInsert
    @DynamicUpdate
    @Table(name = "AUTHOR")
    public class Author {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "ID", nullable = false)
        private Long id;
    
        @Column(name = "FIRST_NAME", nullable = false)
        private String firstName;
    
        @Column(name = "SECOND_NAME", nullable = false)
        private String secondName;
    
        @OneToOne
        @JoinColumn(name = "BOOK_ID", unique = true, nullable = false)
        private Book book;
    }
Rozwiążmy to po kolei:
  1. Wszystkie pola w klasie powtarzają atrybuty jednostki DB.
  2. @Data (z Lombok ) mówi, że dla każdego pola zostanie utworzony moduł pobierający i ustawiający, równa się, hashcode zostanie nadpisany i zostanie wygenerowana metoda toString.
  3. @Entity mówi, że dana klasa jest jednostką i jest powiązana z jednostką DB.
  4. @DynamicInsert i @DynamicUpdate mówią, że dynamiczne wstawienia i aktualizacje będą wykonywane w bazie danych. Są to głębsze ustawienia hibernacji, które będą dla Ciebie przydatne, abyś miał PRAWIDŁOWE grupowanie wsadowe.
  5. @Table (nazwa = „AUTHOR”) wiąże klasę Book z tabelą DB AUTHOR.
  6. @Id mówi, że to pole jest kluczem podstawowym.
  7. @GeneratedValue (strategy = GenerationType.IDENTITY) – strategia generowania klucza podstawowego.
  8. @Column (name = "ID", nullable = false) kojarzy pole z atrybutem DB, a także informuje, że dane pole DB nie może mieć wartości null. Jest to również przydatne podczas generowania tabel z encji. Proces odwrotny do tego, jak teraz tworzymy nasz projekt, jest to potrzebne w testowych bazach danych do testów jednostkowych.
  9. @OneToOne mówi, że dane pole jest polem relacji jeden do jednego.
  10. @JoinColumn (name = "BOOK_ID", unikatowy = true, nullable = false) - zostanie utworzona kolumna BOOK_ID, która jest unikalna i nie ma wartości null.
Z drugiej strony (w klasie Book ) musimy także wykonać połączenie One-to-One i wskazać pole, na którym następuje mapowanie. @OneToOne(mappedBy = "książka") - w tym przykładzie jest to pole książki klasy Author. WZP samodzielnie je połączy. Na pierwszy rzut oka może się wydawać, że panuje tam bałagan w adnotacjach, jednak w rzeczywistości jest to bardzo wygodne i z doświadczeniem dodasz je automatycznie, nawet bez zastanowienia.
  1. Teraz skonfigurujmy Hibernację. W tym celu utwórz plik hibernate.cfg.xml :

    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
        <session-factory>
            <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
            <property name="hibernate.connection.driver_class">org.h2.Driver</property>
    
            <property name="hibernate.connection.url">jdbc:h2:~/db/onetoone</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.connection.password"/>
    
            <property name="hibernate.hbm2ddl.auto">update</property>
    
            <property name="hibernate.show_sql">true</property>
            <property name="hibernate.format_sql">true</property>
            <property name="hibernate.use_sql_comments">true</property>
    
            <property name="hibernate.generate_statistics">true</property>
    
            <property name="hibernate.jdbc.batch_size">50</property>
            <property name="hibernate.jdbc.fetch_size">50</property>
    
            <property name="hibernate.order_inserts">true</property>
            <property name="hibernate.order_updates">true</property>
            <property name="hibernate.jdbc.batch_versioned_data">true</property>
    
            <mapping class="com.qthegamep.forjavarushpublication2.entity.Book"/>
            <mapping class="com.qthegamep.forjavarushpublication2.entity.Author"/>
        </session-factory>
    </hibernate-configuration>
Opis właściwości :
  1. hibernate.dialect jest dialektem wybranego przez nas systemu DBMS.
  2. hibernate.connection.driver_class - Klasa sterownika naszej bazy danych.
  3. hibernate.connection.url - utl naszej bazy danych. Możesz wziąć to z pierwszego punktu, w którym skonfigurowaliśmy DB.
  4. hibernate.connection.username - nazwa użytkownika bazy danych.
  5. hibernate.connection.password — hasło użytkownika bazy danych.
  6. hibernate.hbm2ddl.auto - konfiguracja generowania tabeli. Jeśli aktualizacja, to nie generuje, jeśli została już utworzona, a jedynie ją aktualizuje.
  7. hibernate.show_sql - czy wyświetlać zapytania DB.
  8. hibernate.format_sql - czy formatować zapytania DB. Jeśli nie, to wszyscy będą w jednej linii. Polecam włączyć.
  9. hibernate.use_sql_comments - komentuje zapytania DB. Jeśli jest to Wkładka, to nad żądaniem wpisywany jest komentarz, że żądanie jest typu Wstawka.
  10. hibernate.generate_statistics - generuje logi. Polecam i polecam ustawienie logowania na maksimum. Odczytanie logów zwiększy Twoje szanse na poprawną pracę z ORM-em.
  11. hibernate.jdbc.batch_size — Maksymalny rozmiar partii.
  12. hibernate.jdbc.fetch_size — Maksymalny rozmiar pobierania.
  13. hibernate.order_inserts - umożliwia wstawianie dynamiczne.
  14. hibernate.order_updates — umożliwia aktualizacje dynamiczne.
  15. hibernate.jdbc.batch_versioned_data - umożliwia przetwarzanie wsadowe. Spójrz na swój system DBMS: nie każdy to obsługuje.
  16. mapping class - klasy będące naszymi bytami. Wszystko trzeba spisać.
  1. Teraz musimy określić naszą istotę. Możemy to sprawdzić w zakładce trwałości:

    Podmioty JPA i relacje DB - 11

    Wynik:

    Podmioty JPA i relacje DB - 12
  2. Musimy także skonfigurować przypisywanie danych:

    Podmioty JPA i relacje DB - 13 Podmioty JPA i relacje DB - 14

    Wyniki: Wykonaliśmy mapowanie jeden do jednego. Materiał ma charakter wyłącznie informacyjny, szczegóły w odnośnikach.

Relacja jeden do wielu

Link do oddziału tutaj . Nie będę już publikować kodu w artykule, ponieważ jest już za długi. Przyglądamy się całemu kodowi w GitHubie.
  1. W wyniku wykonania skryptu inicjującego otrzymujemy:

    Podmioty JPA i relacje DB - 15

    Czy czujesz różnicę w stosunku do poprzedniego stołu?

  2. Diagram:

    Podmioty JPA i relacje DB - 16

    Relacja jeden do wielu – jeden autor może mieć kilka książek. Lewa jednostka odpowiada jednemu lub większej liczbie prawych.

  3. Różnica w mapowaniu będzie dotyczyć adnotacji i pól:

    W klasie Autor pojawia się pole :

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
    private Set<Book> books;

    To już komplet, bo książek możemy mieć kilka. @OneToMany mówi o typie postawy. FetchType.Lazy mówi, że nie musimy ładować całej listy książek, jeśli nie jest to określone w żądaniu. Należy również powiedzieć, że tego pola NIE MOŻNA dodać do toString, w przeciwnym razie zaczniemy palić StackOverflowError. Mój ukochany Lombok dba o to:

    @ToString(exclude = "books")

    Na zajęciach Książka przeprowadzamy informację zwrotną typu „wiele do jednego”:

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "AUTHOR_ID", nullable = false)
    private Author author;

    Tutaj dochodzimy do wniosku, że jeden do wielu jest lustrzanym odbiciem wielu do jednego i odwrotnie. Należy podkreślić, że Hibernate nie ma zielonego pojęcia o komunikacji dwukierunkowej. Dla niego są to dwa różne połączenia: jedno w jedną stronę, drugie w drugą stronę.

  4. W pliku hibernate.cfg.xml niewiele się zmieniło .

  5. Trwałość:

    Podmioty JPA i relacje DB - 17

Relacja wiele do jednego

Ponieważ „Wiele do jednego” jest lustrzanym odbiciem „Jeden do wielu”, różnic będzie niewiele. Link do oddziału tutaj .
  1. W wyniku wykonania skryptu inicjującego otrzymujemy następujący wynik:

    Podmioty JPA i relacje DB - 18
  2. Diagram:

    Podmioty JPA i relacje DB - 19
  3. Różnica w mapowaniu będzie dotyczyć adnotacji i pól:

    W klasie Author nie ma już zbioru , ponieważ został on przeniesiony do klasy Book .

  4. hibernacja.cfg.xml

  5. Trwałość:

    Podmioty JPA i relacje DB - 20

Relacja wiele do wielu

Przejdźmy do najciekawszej relacji. Relację tę, zgodnie ze wszystkimi zasadami przyzwoitości i nieprzyzwoitości, tworzy dodatkowa tabela. Ale ta tabela nie jest bytem. Ciekawe, prawda? Rzućmy okiem na to gówno. Link do oddziału tutaj .
  1. Spójrz na skrypt inicjujący , pojawia się tutaj dodatkowa tabela HAS. Otrzymujemy coś w rodzaju autor-ma-książkę.

    W wyniku wykonania skryptu otrzymamy następujące tabele:

    Jednostki WZP i relacje DB - 21
  2. Diagram:

    JPA Entities and DB Relationships - 22

    W naszym przykładzie okazuje się, że książka może mieć wielu autorów, a autor może mieć wiele książek. Mogą się nakładać.

  3. Klasy mapujące będą miały zestawy w klasach. Ale jak powiedziałem, tabela HAS nie jest bytem.

    Klasa autora :

    @ManyToMany
    @JoinTable(name = "HAS",
            joinColumns = @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"),
            inverseJoinColumns = @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID")
    )
    private Set<Book> books;

    @ManyToMany to rodzaj relacji.

    @JoinTable - właśnie to połączy atrybut z dodatkową tabelą HAS. Podajemy w nim dwa atrybuty, które będą wskazywały klucze podstawowe dwóch podmiotów.

    Klasa książki :

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "books")
    private Set<Author> authors;

    Tutaj wskazujemy FetchType i pole, którego będziemy używać do mapowania.

  4. Nasz plik hibernate.cfg.xml znów pozostał niezmieniony (nie biorę pod uwagę faktu, że dla każdej gałęzi utworzyliśmy nową bazę danych).

  5. Trwałość:

    JPA Entities and DB Relationships - 23

Odprawa

Zatem pobieżnie sprawdziliśmy typy relacji DB i wymyśliliśmy, jak je zaimplementować w modelu ORM. Napisaliśmy projekt testowy, który demonstruje wszystkie połączenia i wymyśliliśmy, jak skonfigurować hibernację/jpa. Uff.

Przydatne linki

Moje poprzednie artykuły: PS W tekście mogą występować błędy i niedociągnięcia. PPS Autor pisząc ten artykuł palił coś dziwnego. Dziękuję za uwagę!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION