Bästa praxis för många till många föreningar med Hibernate och JPA

många till många föreningar är en av de vanligaste föreningarna med JPA och Hibernate. Du kan hitta massor av exempel för dem i den verkliga världen, och du kan kartlägga dem med JPA och Hibernate som en uni – eller dubbelriktad förening i din domänmodell.

men du vet förmodligen också att dessa mappningar ger flera fallgropar. I den här artikeln kommer jag att visa dig 5 bästa metoder som hjälper dig att undvika dessa fallgropar och att genomföra effektiva mappningar. Du kommer att lära dig:

  1. den mest effektiva datatypen för din förening
  2. Varför behöver du verktygsmetoder för att hantera din förening
  3. rätt FetchType för en effektiv mappning
  4. när och hur man använder frågespecifik hämtning
  5. CascadeType du bör undvika till varje pris

Jag kommer inte att dyka in i detaljerna i en grundläggande många-till-många mappning. Om du inte är helt säker på hur du skapar en sådan kartläggning, ta en titt på många-till-många avsnittet i min association mapping guide.

den mest effektiva datatypen för din förening

YouTube-video

de flesta utvecklare spenderar inte mycket tankar på datatypen för en till-många förening. De väljer bara en java.util.Lista eftersom det är enkelt och inte utför några kontroller för att undvika dubbletter.

det är OK, om du implementerar en grundläggande Java-klass eller om du modellerar en en-till-många/många-till-en-förening. Men du bör aldrig använda en lista om du modellerar en många-till-många förening.

@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>();...}

Hibernate hanterar ta bort operationer på många-till-många relationer som mappas till en java.util.Lista mycket ineffektivt.

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();

det tar först bort alla poster från associationstabellen innan den infogar alla återstående.

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 (?, ?)

Du bör istället modellera en många-till-många-förening som en java.util.Ställa.

@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>();...}

Hibernate hanterar sedan ta bort operationer på föreningen mycket bättre. Det tar nu bara bort de förväntade posterna från föreningen och håller de andra orörda.

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=?

varför behöver du verktygsmetoder för att hantera din förening

Dubbelriktade föreningar mappas till ett entitetsattribut i båda ändarna av relationerna. Så i det föregående exemplet har du ett författarattribut på Bokenheten och ett bokattribut på Författarenheten. Det gör implementeringen av en JPQL eller CriteriaQuery mycket bekväm eftersom du kan använda dessa attribut för att definiera en ANSLUTNINGSKLAUSUL.

men att lägga till eller ta bort en förening blir mer komplicerat. Du behöver alltid utföra förändringen i båda ändarna av föreningen. Om du till exempel vill lägga till en bok I författaren måste du lägga till den i bokattributet för Författarenheten, och du måste också lägga till författaren författarattributet på Bokenheten. Annars innehåller ditt nuvarande uthållighetskontext inkonsekventa data som du kommer att använda till slutet av din nuvarande transaktion.

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);

Utility metoder på din författare och bok enheter gör uppdatering och ta bort mycket lättare. Inom dessa metoder utför du de nödvändiga operationerna på båda enheterna.

@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);}}

rätt FetchType för en effektiv kartläggning

YouTube video

detta är en snabb en. Du bör alltid använda FetchType.Lat för dina många till många föreningar. Det berättar för din persistensleverantör att inte hämta de associerade enheterna från databasen tills du använder dem. Det är vanligtvis fallet när du kallar sin getter-metod för första gången.

lyckligtvis är det standard för alla till många föreningar. Så se till att du inte ändrar det.

och om du vill lära dig mer om JPA: s olika FetchTypes, ta en titt på min introduktion till JPA FetchTypes.

när och hur du använder sökspecifik hämtning

Om du använder FetchType.Lat, du behöver veta om frågespecifik hämtning. Annars kommer din ansökan att vara mycket långsam eftersom du skapade massor av N + 1 select-problem.

YouTube-video

När du laddar en entitet och använder frågespecifik hämtning, berättar du Hibernate vilka mappade föreningar den ska initiera för varje hämtad entitet. Den utvidgar sedan select-klausulen i din fråga så att den innehåller kolumnerna mappade av dessa andra enheter och initierar associationerna. Och eftersom associationerna redan är initierade behöver Hibernate inte utföra en ytterligare fråga när du öppnar gettermetoden för första gången.

Du kan implementera sökspecifik hämtning på flera olika sätt. Den enklaste är en koppling hämta klausul, som jag kommer att visa dig här. Men du kan också använda en @NamedEntityGraph eller en EntityGraph, som jag förklarade i tidigare artiklar.

definitionen av en JOIN FETCH-klausul är nästan identisk med en simple JOIN-klausul i en JPQL-fråga. Du behöver bara lägga till sökordet hämta.

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

ändå ser en JOIN och en Join FETCH-klausul mycket lika ut, join FETCH-klausulen har en mycket större effekt på den genererade SQL-frågan. Det blir inte bara översatt till en SQL JOIN, eftersom det är fallet för en jpql JOIN-klausul, det tvingar också din persistensleverantör att utöka SELECT-klausulen med alla kolumner som mappas av den associerade enheten.

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

Cascadetypen som du bör undvika till varje pris

om du aktiverar cascading på en association, tillämpar din persistence provider de operationer du utför på entiteten på alla associerade entiteter. Om det gör det för alla operationer eller bara för några utvalda beror på den konfigurerade CascadeType.

YouTube video

det kan låta som en fantastisk ide som gör implementeringen av din affärslogik mycket enklare. Och det är inte helt fel.

men undvik CascadeTypes ta bort och alla, som inkluderar Ta bort, för många till många föreningar. I bästa fall skapar det bara prestandaproblem, men i värsta fall kan det också ta bort fler poster än du tänkte.

Jag förklarade både fallgropar och deras lösning i stora detaljer i en tidigare artikel. Eller om du vill hålla det enkelt, utlösa den nödvändiga informationen programmatiskt på de associerade enheterna. Detta kan kräva några fler rader kod, men det undviker eventuella oväntade biverkningar.

slutsats

Du kan hitta massor av exempel för många till många föreningar i den verkliga världen, och du kan enkelt kartlägga dem med JPA och Hibernate. Tyvärr döljer dessa enkla mappningar några fallgropar som du kan undvika genom att följa dessa 5 bästa metoder:

  1. Modellföreningar som en java.util.Ställa.
  2. ange verktygsmetoder för att lägga till eller ta bort en enhet från en förening.
  3. Använd alltid FetchType.Lat, vilket är standard, för att undvika prestandaproblem.
  4. använd frågespecifik hämtning för att undvika n + 1 Välj problem.
  5. Använd inte CascadeTypes ta bort och alla.

Lämna ett svar

Din e-postadress kommer inte publiceras.