První polovina tohoto kurzu představila základy rozhraní API Java Persistence a ukázala vám, jak konfigurovat aplikaci JPA pomocí Hibernate 5.3.6 a Java 8. Pokud jste si přečetli tento tutoriál a prostudovali jeho ukázkovou aplikaci, pak znáte základy modelování entit JPA a mnohostranných vztahů v JPA. Také jste měli nějaké zkušenosti s psaním pojmenovaných dotazů pomocí JPA Query Language (JPQL).
V této druhé polovině tutoriálu pojedeme hlouběji s JPA a Hibernate. Dozvíte se, jak modelovat vztah mezi mnoha Film
a Super hrdina
entity, nastavit jednotlivá úložiště pro tyto entity a přetrvávat entity do databáze H2 v paměti. Dozvíte se také více o roli kaskádových operací v JPA a získáte tipy pro výběr a CascadeType
strategie pro entity v databázi. Nakonec spojíme fungující aplikaci, kterou můžete spustit ve svém IDE nebo na příkazovém řádku.
Tento kurz se zaměřuje na základy JPA, ale nezapomeňte se podívat na tyto tipy pro Javu, které zavádějí pokročilejší témata v JPA:
- Vztahy dědičnosti v JPA a Hibernate
- Kompozitní klíče v JPA a Hibernate
Vztahy mnoho k mnoha v JPA
Vztahy mnoho k mnoha definujte entity, pro které mohou mít obě strany vztahu více vzájemných odkazů. Pro náš příklad budeme modelovat filmy a superhrdiny. Na rozdíl od příkladu Autoři a knihy z 1. části může mít film několik superhrdinů a superhrdina se může objevit ve více filmech. Naši superhrdinové, Ironman a Thor, se objevují ve dvou filmech „The Avengers“ a „Avengers: Infinity War“.
K modelování tohoto vztahu mnoho k mnoha pomocí JPA budeme potřebovat tři tabulky:
- FILM
- SUPER HRDINA
- SUPERHERO_MOVIES
Obrázek 1 ukazuje model domény se třemi tabulkami.

Všimněte si, že SuperHero_Movies
je připojit stůl mezi Film
a Super hrdina
tabulky. V JPA je tabulka spojení speciální druh tabulky, který usnadňuje vztah mezi mnoha.
Jednosměrný nebo obousměrný?
V JPA používáme @ManyToMany
anotace k modelování vztahů mezi mnoha. Tento typ vztahu může být jednosměrný nebo obousměrný:
- V jednosměrný vztah pouze jedna entita ve vztahu ukazuje na druhou.
- V obousměrný vztah obě entity ukazují na sebe.
Náš příklad je obousměrný, což znamená, že film ukazuje na všechny své superhrdiny a superhrdina ukazuje na všechny jejich filmy. V obousměrném vztahu mnoho k jedné entitě vlastní vztah a druhý je mapováno na vztah. Používáme mappedBy
atribut @ManyToMany
anotace k vytvoření tohoto mapování.
Výpis 1 zobrazuje zdrojový kód pro Super hrdina
třída.
Výpis 1. SuperHero.java
balíček com.geekcap.javaworld.jpa.model; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @Entity @Table (name = "SUPER_HERO") veřejná třída SuperHero {@Id @GeneratedValue soukromé celé číslo; soukromé jméno řetězce; @ManyToMany (fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable (name = "SuperHero_Movies", joinColumns = {@JoinColumn (name = "superhero_id")}, inverseJoinColumns = {@JoinColumn) }) soukromá sada filmů = nová HashSet (); public SuperHero () {} public SuperHero (Integer id, String name) {this.id = id; this.name = name; } public SuperHero (název řetězce) {this.name = name; } public Integer getId () {return id; } public void setId (Integer id) {this.id = id; } public String getName () {zpáteční jméno; } public void setName (název řetězce) {this.name = name; } public Set getMovies () {návrat filmů; } @Override public String toString () {return "SuperHero {" + "id =" + id + ", + name +" \ '' + ", + films.stream (). Map (Movie :: getTitle) .collect (Collectors.toList ()) + "\ '' + '}'; }}
The Super hrdina
třída má několik anotací, které by měly být známé z části 1:
@Entity
identifikujeSuper hrdina
jako subjekt JPA.@Stůl
mapujeSuper hrdina
entita do tabulky „SUPER_HERO“.
Všimněte si také Celé číslo
id
pole, které určuje, že primární klíč tabulky bude automaticky generován.
Dále se podíváme na @ManyToMany
a @JoinTable
anotace.
Načítání strategií
Věc, kterou si musíte všimnout v @ManyToMany
anotace je způsob, jakým konfigurujeme načítání strategie, což může být líné nebo dychtivé. V tomto případě jsme nastavili vynést
na EAGER
, takže když získáme a Super hrdina
z databáze také automaticky načteme všechny její odpovídající Film
s.
Pokud jsme se rozhodli provést a LÍNÝ
místo toho načíst, načteme pouze každý Film
protože to bylo konkrétně přístupné. Líné načítání je možné pouze při Super hrdina
je připojen k EntityManager
; jinak přístup k filmům superhrdiny vyvolá výjimku. Chceme mít přístup k filmům superhrdiny na vyžádání, takže v tomto případě zvolíme možnost EAGER
načítání strategie.
CascadeType.PERSIST
Kaskádové operace definovat, jak se superhrdinové a jejich odpovídající filmy přetrvávají do az databáze. Existuje řada konfigurací typu kaskády, ze kterých si můžete vybrat, a o nich si povíme později v tomto kurzu. Prozatím si všimněte, že jsme nastavili kaskáda
přisuzovat CascadeType.PERSIST
, což znamená, že když uložíme superhrdinu, uloží se také jeho filmy.
Připojte se ke stolům
JoinTable
je třída, která usnadňuje vztah mezi mnoha lidmi Super hrdina
a Film
. V této třídě definujeme tabulku, která bude ukládat primární klíče pro oba Super hrdina
a Film
subjekty.
Výpis 1 určuje, že název tabulky bude SuperHero_Movies
. The připojit sloupec bude superhrdina_id
a sloupec inverzního spojení bude film_id
. The Super hrdina
entita vlastní vztah, takže sloupec spojení bude naplněn Super hrdina
primární klíč. Sloupec inverzního spojení pak odkazuje na entitu na druhé straně vztahu, což je Film
.
Na základě těchto definic v seznamu 1 bychom očekávali vytvoření nové tabulky s názvem SuperHero_Movies
. Tabulka bude mít dva sloupce: superhrdina_id
, který odkazuje na id
sloupec SUPER HRDINA
stůl a film_id
, který odkazuje na id
sloupec FILM
stůl.
Třída filmu
Výpis 2 zobrazuje zdrojový kód pro Film
třída. Připomeňme, že v obousměrném vztahu vlastní jeden subjekt vztah (v tomto případě Super hrdina
), zatímco druhý je mapován na vztah. Kód v seznamu 2 obsahuje mapování vztahů aplikované na Film
třída.
Výpis 2. Movie.java
balíček com.geekcap.javaworld.jpa.model; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; import java.util.HashSet; import java.util.Set; @Entity @Table (name = "MOVIE") veřejná třída Film {@Id @GeneratedValue soukromé celé číslo; soukromý název řetězce; @ManyToMany (mappedBy = "films", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) soukromá sada superHeroes = nová HashSet (); public Movie () {} public Movie (Integer id, String title) {this.id = id; this.title = title; } veřejný film (název řetězce) {this.title = název; } public Integer getId () {return id; } public void setId (Integer id) {this.id = id; } public String getTitle () {návratový název; } public void setTitle (název řetězce) {this.title = title; } public Set getSuperHeroes () {return superHeroes; } public void addSuperHero (SuperHero superHero) {superHeroes.add (superHero); superHero.getMovies (). přidat (toto); } @Override public String toString () {return "Movie {" + "id =" + id + ", + title +" \ '' + '}'; }}
Následující vlastnosti jsou aplikovány na @ManyToMany
anotace ve výpisu 2:
mappedBy
odkazuje na název pole naSuper hrdina
třída, která spravuje vztah mnoho k mnoha. V tomto případě odkazuje na filmy pole, které jsme definovali v seznamu 1 s odpovídajícímJoinTable
.kaskáda
je nakonfigurován naCascadeType.PERSIST
, což znamená, že když aFilm
je uložen odpovídajícíSuper hrdina
entity by také měly být uloženy.vynést
říkáEntityManager
že by měl získat superhrdiny filmu dychtivě: když načte aFilm
, měl by také načíst všechny odpovídajícíSuper hrdina
subjekty.
Něco jiného k poznámce o Film
třída je jeho addSuperHero ()
metoda.
Při konfiguraci entit pro trvalost nestačí jednoduše přidat do filmu superhrdinu; musíme také aktualizovat druhou stranu vztahu. To znamená, že musíme film přidat do superhrdiny. Když jsou obě strany vztahu správně nakonfigurovány, takže film má odkaz na superhrdinu a superhrdina má odkaz na film, bude tabulka připojení také správně naplněna.
Definovali jsme naše dvě entity. Nyní se podívejme na úložiště, která použijeme k jejich přetrvávání do az databáze.
Spropitné! Postavte obě strany stolu
Je běžnou chybou nastavit pouze jednu stranu vztahu, přetrvávat entitu a poté sledovat, že tabulka spojení je prázdná. Nastavení obou stran vztahu to napraví.
Úložiště JPA
Mohli bychom implementovat veškerý náš kód perzistence přímo v ukázkové aplikaci, ale vytváření tříd úložiště nám umožňuje oddělit kód perzistence od kódu aplikace. Stejně jako v případě aplikace Knihy a autoři v části 1 vytvoříme EntityManager
a poté jej použít k inicializaci dvou úložišť, jednoho pro každou entitu, kterou přetrváváme.
Výpis 3 zobrazuje zdrojový kód pro Úložiště filmu
třída.
Výpis 3. MovieRepository.java
balíček com.geekcap.javaworld.jpa.repository; import com.geekcap.javaworld.jpa.model.Movie; import javax.persistence.EntityManager; import java.util.List; import java.util.Optional; veřejná třída MovieRepository {private EntityManager entityManager; public MovieRepository (EntityManager entityManager) {this.entityManager = entityManager; } public Volitelné uložení (film) {try {entityManager.getTransaction (). begin (); entityManager.persist (film); entityManager.getTransaction (). commit (); návrat Optional.of (film); } catch (Výjimka e) {e.printStackTrace (); } vrátit Optional.empty (); } public Volitelné findById (celé číslo) {Movie movie = entityManager.find (Movie.class, id); vrátit film! = null? Optional.of (film): Optional.empty (); } public List findAll () {return entityManager.createQuery ("from Movie"). getResultList (); } public void deleteById (Integer id) {// Načíst film s tímto ID Movie movie = entityManager.find (Movie.class, id); if (film! = null) {try {// Zahájit transakci, protože změníme databázi entityManager.getTransaction (). begin (); // Odebrat všechny odkazy na tento film superhrdiny movie.getSuperHeroes (). ForEach (superHero -> {superHero.getMovies (). Remove (movie);}); // Nyní odeberte film entityManager.remove (film); // Potvrdit transakci entityManager.getTransaction (). Commit (); } catch (Výjimka e) {e.printStackTrace (); }}}}
The Úložiště filmu
je inicializován pomocí EntityManager
, poté jej uloží do členské proměnné, která se použije v metodách perzistence. Zvažujeme každou z těchto metod.
Metody perzistence
Pojďme si to zopakovat Úložiště filmu
metody vytrvalosti a uvidíte, jak interagují s EntityManager
metody vytrvalosti.