Podmioty JPA i relacje DB
Dzień dobry, koledzy!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 : 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 .-
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:
Następnie przechodzimy do ustawień DB. Możesz wprowadzić swoje dane, a nawet swój system DBMS; powtarzam, dla uproszczenia używam H2 DB.
Następnie skonfigurujmy obwód. Ten krok jest opcjonalny, ale zalecany, jeśli masz wiele schematów w bazie danych.
Zastosuj ustawienia, a na koniec powinniśmy otrzymać coś takiego:
-
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:
Wynik wykonania w konsoli:
Wynik w DB:
-
Spójrzmy na schemat naszych tabel. Aby to zrobić, RMB na naszej bazie danych:
Wynik:
Na diagramie UML widzimy wszystkie klucze podstawowe i obce, widzimy także powiązania pomiędzy naszymi tabelami.
-
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.
-
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; }
- Wszystkie pola w klasie powtarzają atrybuty jednostki DB.
- @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.
- @Entity mówi, że dana klasa jest jednostką i jest powiązana z jednostką DB.
- @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.
- @Table (nazwa = „AUTHOR”) wiąże klasę Book z tabelą DB AUTHOR.
- @Id mówi, że to pole jest kluczem podstawowym.
- @GeneratedValue (strategy = GenerationType.IDENTITY) – strategia generowania klucza podstawowego.
- @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.
- @OneToOne mówi, że dane pole jest polem relacji jeden do jednego.
- @JoinColumn (name = "BOOK_ID", unikatowy = true, nullable = false) - zostanie utworzona kolumna BOOK_ID, która jest unikalna i nie ma wartości null.
-
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>
- hibernate.dialect jest dialektem wybranego przez nas systemu DBMS.
- hibernate.connection.driver_class - Klasa sterownika naszej bazy danych.
- hibernate.connection.url - utl naszej bazy danych. Możesz wziąć to z pierwszego punktu, w którym skonfigurowaliśmy DB.
- hibernate.connection.username - nazwa użytkownika bazy danych.
- hibernate.connection.password — hasło użytkownika bazy danych.
- hibernate.hbm2ddl.auto - konfiguracja generowania tabeli. Jeśli aktualizacja, to nie generuje, jeśli została już utworzona, a jedynie ją aktualizuje.
- hibernate.show_sql - czy wyświetlać zapytania DB.
- hibernate.format_sql - czy formatować zapytania DB. Jeśli nie, to wszyscy będą w jednej linii. Polecam włączyć.
- hibernate.use_sql_comments - komentuje zapytania DB. Jeśli jest to Wkładka, to nad żądaniem wpisywany jest komentarz, że żądanie jest typu Wstawka.
- 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.
- hibernate.jdbc.batch_size — Maksymalny rozmiar partii.
- hibernate.jdbc.fetch_size — Maksymalny rozmiar pobierania.
- hibernate.order_inserts - umożliwia wstawianie dynamiczne.
- hibernate.order_updates — umożliwia aktualizacje dynamiczne.
- hibernate.jdbc.batch_versioned_data - umożliwia przetwarzanie wsadowe. Spójrz na swój system DBMS: nie każdy to obsługuje.
- mapping class - klasy będące naszymi bytami. Wszystko trzeba spisać.
-
Teraz musimy określić naszą istotę. Możemy to sprawdzić w zakładce trwałości:
Wynik:
-
Musimy także skonfigurować przypisywanie danych:
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.-
W wyniku wykonania skryptu inicjującego otrzymujemy:
Czy czujesz różnicę w stosunku do poprzedniego stołu?
-
Diagram:
Relacja jeden do wielu – jeden autor może mieć kilka książek. Lewa jednostka odpowiada jednemu lub większej liczbie prawych.
-
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ę.
-
W pliku hibernate.cfg.xml niewiele się zmieniło .
-
Trwałość:
Relacja wiele do jednego
Ponieważ „Wiele do jednego” jest lustrzanym odbiciem „Jeden do wielu”, różnic będzie niewiele. Link do oddziału tutaj .-
W wyniku wykonania skryptu inicjującego otrzymujemy następujący wynik:
-
Diagram:
-
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 .
-
Trwałość:
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 .-
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:
-
Diagram:
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ć.
-
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.
-
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).
-
Trwałość:
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
- A właściwie sam projekt
- Oddział „jeden do jednego”.
- Oddział jeden do wielu
- Oddział wiele do jednego
- Oddział wiele do wielu
- Przeczytaj to
- I przeczytaj to
GO TO FULL VERSION