mange til mange foreninger er en af de mest anvendte foreninger med JPA og Hibernate. Du kan finde masser af eksempler for dem i den virkelige verden, og du kan kortlægge dem med JPA og dvale som en uni – eller tovejs association i dit domæne model.
men du ved sikkert også, at disse kortlægninger giver flere faldgruber. I denne artikel vil jeg vise dig 5 bedste fremgangsmåder, der hjælper dig med at undgå disse faldgruber og implementere effektive kortlægninger. Du vil lære:
- den mest effektive datatype til din tilknytning
- hvorfor du har brug for hjælpemetoder til at administrere din tilknytning
- den rigtige FetchType til en effektiv kortlægning
- hvornår og hvordan du bruger forespørgselsspecifik hentning
- Cascadetypen, du bør undgå for enhver pris
Jeg vil ikke dykke ned i detaljerne i en grundlæggende mange-til-mange kortlægning. Hvis du ikke er helt sikker på, hvordan du opretter en sådan kortlægning, kan du tage et kig på mange-til-mange afsnit i min forening kortlægning guide.
den mest effektive datatype til din forening
de fleste udviklere bruger ikke mange tanker om datatypen for en til-mange forening. De vælger bare en java.util.Liste, fordi det er simpelt og ikke udfører nogen kontrol for at undgå dubletter.
det er OK, hvis du implementerer en grundlæggende Java-klasse, eller hvis du modellerer en en-til-mange/mange-til-en-forening. Men du bør aldrig bruge en liste, hvis du modellerer en mange-til-mange forening.
@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 håndtag fjern operationer på mange til mange relationer, der er kortlagt til en java.util.Liste meget 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 fjerner først alle poster fra tilknytningstabellen, før det indsætter alle resterende.
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 skal i stedet modellere en mange-til-mange forening som java.util.Indstille.
@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 derefter håndterer fjerne operationer på foreningen meget bedre. Det fjerner nu kun de forventede poster fra foreningen og holder de andre uberørte.
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=?
hvorfor du har brug for hjælpemetoder til at styre din tilknytning
Tovejsassociationer er kortlagt til en enhedsattribut i begge ender af relationerne. Så i det foregående eksempel har du en forfatterattribut på Bogenheden og en bogattribut på Forfatterenheden. Det gør implementeringen af en JPKL eller Kriterieforespørgsel meget behagelig, fordi du kan bruge disse attributter til at definere en JOIN-klausul.
men at tilføje eller fjerne en forening bliver mere kompliceret. Du skal altid udføre ændringen i begge ender af foreningen. Hvis du f.eks. vil føje en bog til Forfatter, skal du føje den til attributten bøger for Forfatterenheden, og du skal også tilføje den forfatter, som forfatterne tilskriver på Bogenheden. Ellers indeholder din nuværende persistenskontekst inkonsekvente data, som du vil bruge indtil slutningen af din nuværende 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 forfatter og bog enheder gør opdatering og fjernelse meget lettere. Inden for disse metoder udfører du de nødvendige operationer på begge enheder.
@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);}}
den rigtige FetchType til en effektiv kortlægning
Dette er en hurtig en. Du bør altid bruge FetchType.Doven for dine mange til mange foreninger. Det fortæller din persistensudbyder ikke at hente de tilknyttede enheder fra databasen, før du bruger dem. Det er normalt tilfældet, når du kalder sin getter-metode for første gang.
heldigvis er det standard for alle til mange foreninger. Så sørg for, at du ikke ændrer det.
og hvis du vil lære mere om JPA ‘ s forskellige FetchTypes, så tag et kig på min introduktion til JPA FetchTypes.
hvornår og hvordan du bruger forespørgselsspecifik hentning
Hvis du bruger FetchType.Doven, du har brug for at vide om forespørgselsspecifik hentning. Ellers vil din ansøgning være meget langsom, fordi du har oprettet masser af N+1 Vælg problemer.
Når du indlæser en enhed og bruger forespørgselsspecifik hentning, fortæller du Hibernate, hvilke kortlagte foreninger det skal initialisere for hver hentet enhed. Det udvider derefter SELECT-klausulen i din forespørgsel, så den inkluderer de kolonner, der er kortlagt af disse andre enheder, og initialiserer tilknytningerne. Og fordi tilknytningerne allerede er initialiseret, behøver Hibernate ikke at udføre en ekstra forespørgsel, når du får adgang til dens getter-metode for første gang.
Du kan implementere forespørgselsspecifik hentning på flere forskellige måder. Den enkleste er en JOIN hente klausul, som jeg vil vise dig her. Men du kan også bruge en @NamedEntityGraph eller en EntityGraph, som jeg forklarede i tidligere artikler.
definitionen af en JOIN FETCH-klausul er næsten identisk med en simpel JOIN-klausul i en JPKL-forespørgsel. Du skal blot tilføje Hent nøgleordet.
Author a = em.createQuery("SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = 1", Author.class).getSingleResult();
alligevel ser en JOIN-og en join-HENTNINGSKLAUSUL meget ens ud, JOIN-HENTNINGSKLAUSULEN har en meget større effekt på den genererede forespørgsel. Det bliver ikke kun oversat til en joinforbindelse, da det er tilfældet for en joinforbindelse, det tvinger også din persistensudbyder til at udvide SELECT-klausulen med alle kolonner, der er kortlagt af den tilknyttede enhed.
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
den CascadeType, du skal undgå for enhver pris
Hvis du aktiverer cascading på en tilknytning, anvender din persistensudbyder de operationer, du udfører på enheden, på alle tilknyttede enheder. Hvis det gør det for alle operationer eller bare for et par udvalgte, afhænger det af den konfigurerede CascadeType.
det lyder måske som en fantastisk ide, der gør implementeringen af din forretningslogik meget lettere. Og det er ikke helt forkert.
men undgå venligst CascadeTypes fjern og alle, som omfatter Fjern, for mange-til-mange foreninger. I bedste fald skaber det kun ydelsesproblemer, men i værste fald kan det også fjerne flere poster, end du havde til hensigt.
Jeg forklarede både faldgruber og deres løsning i store detaljer i en tidligere artikel. Eller hvis du vil holde det enkelt, skal du udløse de krævede oplysninger programmatisk på de tilknyttede enheder. Dette kan kræve et par flere linjer kode, men det undgår uventede bivirkninger.
konklusion
Du kan finde masser af eksempler for mange-til-mange foreninger i den virkelige verden, og du kan nemt kortlægge dem med JPA og Hibernate. Desværre skjuler disse enkle kortlægninger et par faldgruber, som du kan undgå ved at følge disse 5 bedste fremgangsmåder:
- Modelforeninger som java.util.Indstille.
- Angiv hjælpemetoder til at tilføje eller fjerne en enhed fra en tilknytning.
- Brug altid FetchType.Doven, som er standard, for at undgå ydelsesproblemer.
- Anvend forespørgselsspecifik hentning for at undgå N+1 Vælg problemer.
- brug ikke CascadeTypes fjern og alle.