Osvědčené postupy pro mnoho asociací s hibernace a JPA

mnoho asociací je jedním z nejčastěji používaných asociací s JPA a hibernace. Můžete najít spoustu příkladů pro ně v reálném světě, a můžete je zmapovat s JPA a hibernace jako uni-nebo obousměrné sdružení v modelu domény.

ale pravděpodobně také víte, že tato mapování poskytují několik úskalí. V tomto článku vám ukážu 5 osvědčených postupů, které vám pomohou vyhnout se těmto nástrahám a implementovat efektivní mapování. Dozvíte se:

  1. nejvíce efektivní datový typ pro vaše asociace
  2. Proč potřebujete nástroj metod, jak spravovat své asociace
  3. právo FetchType pro efektivní mapování
  4. Kdy a jak použít dotaz-konkrétní načítání
  5. CascadeType byste se měli vyhnout za každou cenu

Nebudu se ponořit do podrobností o základní mnoho-k-mnoha mapování. Pokud si nejste úplně jisti, jak takové mapování vytvořit, podívejte se prosím na sekci many-to-many V mém průvodci mapováním přidružení.

nejvíce efektivní datový typ pro vaše asociace

YouTube video

Většina vývojářů nemusíte trávit spoustu myšlenek na typ dat to-many sdružení. Prostě si vyberou Javu.util.Seznam, protože je to jednoduché a neprovádí žádné kontroly, aby se zabránilo duplikáty.

to je v pořádku, pokud implementujete základní třídu Java nebo pokud modelujete asociaci One-to-Many/Many-to-One. Ale nikdy byste neměli používat seznam, pokud modelujete mnoho-K-mnoho sdružení.

@Entitypublic class Book {// DON'T DO THIS!!!@ManyToMany@JoinTable(name = "book_author", joinColumns = { @JoinColumn(name = "fk_book") }, inverseJoinColumns = { @JoinColumn(name = "fk_author") })private List<Author> authors = new ArrayList<Author>();...}

hibernace zpracovává odstranění operací na mnoha až mnoha vztazích, které jsou mapovány na Javu.util.Seznam velmi neefektivně.

em = emf.createEntityManager();em.getTransaction().begin();// Get Book entity with 2 Authorsb = em.find(Book.class, 1L);// Remove one of the Authorb.getAuthors().remove(a);em.getTransaction().commit();em.close();

nejprve odstraní všechny záznamy z tabulky přidružení, než vloží všechny zbývající.

09:54:28,876 DEBUG - update Book set title=?, version=? where id=? and version=?09:54:28,878 DEBUG - delete from book_author where fk_book=?09:54:28,882 DEBUG - insert into book_author (fk_book, fk_author) values (?, ?)

místo toho byste měli modelovat asociaci many-to-many jako java.util.Nastavit.

@Entitypublic class Book {@ManyToMany@JoinTable(name = "book_author", joinColumns = { @JoinColumn(name = "fk_book") }, inverseJoinColumns = { @JoinColumn(name = "fk_author") })private Set<Author> authors = new HashSet<Author>();...}

hibernace pak zpracovává odstranění operací na asociaci mnohem lépe. Nyní ze sdružení odstraní pouze očekávané záznamy a ostatní ponechá nedotčené.

10:00:37,709 DEBUG - update Book set title=?, version=? where id=? and version=?10:00:37,711 DEBUG - delete from book_author where fk_book=? and fk_author=?

Proč potřebujete nástroj metod, jak spravovat své spojení

Obousměrný sdružení jsou mapovány na entity atribut na obou koncích vztahy. Takže v předchozím příkladu máte atribut autoři na entitě Knihy a atribut knihy na entitě autora. Díky tomu je implementace JPQL nebo CriteriaQuery velmi pohodlná, protože tyto atributy můžete použít k definování klauzule spojení.

ale přidání nebo odebrání asociace se komplikuje. Vždy je třeba provést změnu na obou koncích sdružení. Například pokud chcete přidat knihu autorovi, musíte ji přidat do atributu knihy entity autora a také musíte přidat autora atribut autoři na entitu knihy. V opačném případě váš aktuální kontext persistence obsahuje nekonzistentní data, která budete používat až do konce vaší aktuální transakce.

Book b = new Book();b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");em.persist(b);Author a = em.find(Author.class, 1L);a.getBooks().add(b);b.getAuthors().add(a);

nástroje pro Vaše autorské a knižní entity usnadňují aktualizaci a odstraňování. V rámci těchto metod provádíte požadované operace na obou entitách.

@Entitypublic class Author {@ManyToMany(mappedBy = "authors")private Set<Book> books = new HashSet<Book>();...public void addBook(Book book) {this.books.add(book);book.getAuthors().add(this);}public void removeBook(Book book) {this.books.remove(book);book.getAuthors().remove(this);}}

doprava FetchType pro efektivní mapování

YouTube video

Toto je rychlý. Vždy byste měli používat FetchType.Líný pro vaše Mnoho-to-mnoho sdružení. Říká poskytovateli persistence, aby nepřinesl přidružené entity z databáze, dokud je nepoužijete. To je obvykle případ, kdy zavoláte jeho metodu getter poprvé.

naštěstí je to výchozí hodnota pro všechny to-many asociace. Takže se prosím ujistěte, že to nezměníte.

a pokud se chcete dozvědět více o různých Fetchtypech JPA, podívejte se prosím na můj úvod do JPA FetchTypes.

kdy a jak používat načítání specifické pro dotaz

Pokud používáte FetchType.Líný, potřebujete vědět o načítání specifickém pro dotaz. V opačném případě bude vaše aplikace velmi pomalá, protože jste vytvořili spoustu problémů s výběrem n+1.

YouTube video

Při načtení entity a použít dotaz-konkrétní načítání, řekni Hibernace, která mapuje sdružení musí inicializovat pro každé načtené entity. Poté rozšíří klauzuli SELECT vašeho dotazu tak, že obsahuje sloupce mapované těmito dalšími entitami a inicializuje přidružení. A protože asociace jsou již inicializovány, Hibernate nemusí při prvním přístupu k metodě getter provádět další dotaz.

můžete implementovat načítání specifické pro dotaz několika různými způsoby. Nejjednodušší z nich je připojit načíst klauzule, které vám ukážu zde. Ale můžete také použít @NamedEntityGraph nebo EntityGraph, který jsem vysvětlil v předchozích článcích.

definice klauzule join FETCH je téměř totožná s klauzulí simple JOIN v dotazu JPQL. Stačí přidat klíčové slovo načíst.

Author a = em.createQuery("SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = 1", Author.class).getSingleResult();

přesto klauzule JOIN a JOIN FETCH vypadají velmi podobně, klauzule join FETCH má mnohem větší vliv na generovaný dotaz SQL. Nejenže se překládá do SQL JOIN, protože je to případ klauzule JPQL JOIN, ale také nutí vašeho poskytovatele vytrvalosti rozšířit klauzuli SELECT o všechny sloupce, které jsou mapovány přidruženou entitou.

16:21:03,046 DEBUG SQL:94 - select author0_.id as id1_0_0_, book2_.id as id1_1_1_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_, book2_.format as format2_1_1_, book2_.publishingDate as publishi3_1_1_, book2_.title as title4_1_1_, book2_.version as version5_1_1_, books1_.author_id as author_i2_2_0__, books1_.book_id as book_id1_2_0__ from Author author0_ inner join book_author books1_ on author0_.id=books1_.author_id inner join Book book2_ on books1_.book_id=book2_.id where author0_.id=1

CascadeType byste se měli vyhnout za každou cenu

Pokud aktivujete kaskádové o sdružení, vaše vytrvalost poskytovatel se vztahuje na operace budete provádět na subjektu, aby všechny související subjekty. Pokud to udělá pro všechny operace nebo jen pro několik vybraných, záleží na nakonfigurovaném typu CascadeType.

YouTube video

To může znít jako skvělý nápad, který umožňuje provádění své obchodní logiky mnohem jednodušší. A to není úplně špatně.

ale prosím, vyhněte se CascadeTypes odstranit a vše, který zahrnuje odstranit, pro mnoho-to-mnoho asociací. V nejlepším případě vytváří pouze problémy s výkonem, ale v nejhorším případě může také odstranit více záznamů, než jste zamýšleli.

obě úskalí a jejich řešení jsem podrobně vysvětlil v předchozím článku. Nebo pokud chcete, aby to bylo jednoduché, spusťte požadované informace programově o přidružených entitách. To může vyžadovat několik dalších řádků kódu, ale vyhýbá se neočekávaným vedlejším účinkům.

závěr

najdete mnoho příkladů pro mnoho asociací v reálném světě a můžete je snadno mapovat pomocí JPA a hibernace. Tato jednoduchá mapování bohužel skrývají několik úskalí, kterým se můžete vyhnout dodržováním těchto 5 osvědčených postupů:

  1. modelové asociace jako java.util.Nastavit.
  2. Poskytněte obslužné metody pro přidání nebo odebrání entity z přidružení.
  3. vždy používejte FetchType.LAZY, což je výchozí, aby se zabránilo problémům s výkonem.
  4. použijte načítání specifické pro dotaz, abyste předešli problémům s výběrem n+1.
  5. nepoužívejte kaskádové typy odebrat a všechny.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.