a sok-sok asszociációk az egyik leggyakrabban használt asszociáció a JPA és a Hibernate esetén. Rengeteg példát találhatsz rájuk a valós világban, és leképezheted őket a JPA – val és a Hibernate-tel egy-vagy kétirányú asszociációként a domain modelledben.
de valószínűleg azt is tudja, hogy ezek a leképezések számos buktatót nyújtanak. Ebben a cikkben 5 legjobb gyakorlatot mutatok be, amelyek segítenek elkerülni ezeket a buktatókat és hatékony leképezéseket végrehajtani. Meg fogja tanulni:
- a Társítás leghatékonyabb adattípusa
- miért van szükség segédprogramokra az Társítás kezeléséhez
- a megfelelő FetchType a hatékony leképezéshez
- mikor és hogyan kell használni a lekérdezés-specifikus leképezést
- a CascadeType-ot, amelyet minden áron el kell kerülnie
nem fogok belemerülni egy alapvető sok-sok leképezés részleteibe. Ha nem biztos abban, hogyan lehet ilyen leképezést létrehozni, kérjük, vessen egy pillantást az asszociációs leképezési útmutatóm sok-sok szakaszára.
a Társítás leghatékonyabb adattípusa
a legtöbb fejlesztő nem sok gondolatot költ egy to-many Társítás adattípusára. Csak egy java-t választanak.util.Lista, mert egyszerű, és nem végez ellenőrzéseket a duplikációk elkerülése érdekében.
Ez rendben van, ha egy alapvető Java osztályt implementál, vagy ha EGY-a-sokhoz/sok-az-egyhez társítást modellez. De soha ne használjon listát, ha sok-sok társítást modellez.
@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>();...}
a hibernált fogantyúk eltávolítják a java-hoz leképezett sok-sok kapcsolat műveleteit.util.A lista nagyon nem hatékony.
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();
először eltávolítja az összes rekordot az asszociációs táblából, mielőtt beszúrja az összes maradékot.
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 (?, ?)
ehelyett egy sok-sok társítást kell java-ként modelleznie.util.Kész.
@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>();...}
hibernálás, majd kezeli eltávolítani műveletek az egyesület sokkal jobb. Most már csak a várt rekordokat távolítja el az Egyesületből, a többieket pedig érintetlenül tartja.
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=?
miért van szükség segédprogramokra az asszociáció kezeléséhez?
a kétirányú asszociációk egy entitás attribútumhoz vannak hozzárendelve a kapcsolatok mindkét végén. Tehát az előző példában van egy szerzők attribútum a könyv entitáson, és egy könyvek attribútum a szerző entitáson. Ez nagyon kényelmessé teszi a JPQL vagy a CriteriaQuery megvalósítását, mert ezeket az attribútumokat használhatja egy csatlakozási záradék meghatározására.
de egy Társítás hozzáadása vagy eltávolítása bonyolultabbá válik. Mindig el kell végeznie a változást az egyesület mindkét végén. Ha például könyvet szeretne hozzáadni a szerzőhöz, akkor azt hozzá kell adnia a szerző entitás könyvek attribútumához, valamint hozzá kell adnia a szerző a szerzők attribútumot a könyv entitáshoz. Ellenkező esetben a jelenlegi perzisztencia kontextus inkonzisztens adatokat tartalmaz, amelyeket a jelenlegi tranzakció végéig használ.
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);
a szerző és a könyv entitások segédprogramjai sokkal könnyebbé teszik a frissítést és az eltávolítást. Ezeken a módszereken belül mindkét entitáson végrehajtja a szükséges műveleteket.
@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);}}
A megfelelő FetchType a hatékony leképezéshez
Ez egy gyors. Mindig használja FetchType.Lusta a sok-sok egyesület számára. Azt mondja a perzisztencia szolgáltatónak, hogy addig ne töltse le a társított entitásokat az adatbázisból, amíg nem használja őket. Ez általában akkor fordul elő, amikor először hívja meg getter módszerét.
szerencsére, ez az alapértelmezett Minden To-sok egyesület. Ezért kérjük, győződjön meg róla, hogy nem változtatja meg.
és ha többet szeretne megtudni a JPA különböző FetchTypes-jeiről, kérjük, nézze meg a JPA FetchTypes Bevezetőmet.
mikor és hogyan kell használni a lekérdezés-specifikus lekérést
Ha FetchType-t használ.Lusta, tudnia kell a lekérdezés-specifikus lekérésről. Ellenkező esetben az alkalmazás nagyon lassú lesz, mert sok n+1 select problémát hozott létre.
amikor betölt egy entitást és lekérdezés-specifikus lekérést használ, megmondja a Hibernate-nek, hogy mely leképezett asszociációkat inicializálja az egyes lekérett entitásokhoz. Ezután kiterjeszti a lekérdezés SELECT záradékát úgy, hogy tartalmazza az ezen entitások által leképezett oszlopokat, és inicializálja az asszociációkat. Mivel a társítások már inicializálva vannak, a Hibernate-nek nem kell további lekérdezést végrehajtania, amikor először lép be a getter metódusába.
a lekérdezés-specifikus lekérést többféle módon valósíthatja meg. A legegyszerűbb egy JOIN FETCH záradék, amelyet itt mutatok meg. De használhat egy @NamedEntityGraph-ot vagy egy EntityGraph-ot is, amelyet a korábbi cikkekben kifejtettem.
a JOIN FETCH záradék meghatározása majdnem megegyezik a jpql lekérdezés egyszerű JOIN záradékával. Csak hozzá kell adnia a FETCH kulcsszót.
Author a = em.createQuery("SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = 1", Author.class).getSingleResult();
ennek ellenére a JOIN és a JOIN FETCH záradék nagyon hasonlít, a JOIN FETCH záradék sokkal nagyobb hatással van a generált SQL lekérdezésre. Ez nem csak lesz lefordítva SQL JOIN, mint ez a helyzet a JPQL JOIN záradék, azt is kényszeríti a perzisztencia szolgáltató kiterjeszteni a SELECT záradék minden oszlopot, amely leképezi a társított entitás.
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
A CascadeType, amelyet minden áron el kell kerülnie
ha a cascading funkciót egy társításon aktiválja, a perzisztencia szolgáltató az entitáson végrehajtott műveleteket az összes társított entitásra alkalmazza. Ha ez nem, hogy az összes műveletet, vagy csak néhány kiválasztott is függ a konfigurált CascadeType.
Ez egy csodálatos ötletnek tűnhet, amely sokkal könnyebbé teszi az üzleti logika megvalósítását. És ez nem teljesen rossz.
de kérjük, kerülje a CascadeTypes REMOVE and ALL-t, amely magában foglalja az eltávolítást is, a sok-sok asszociációhoz. A legjobb esetben csak teljesítményproblémákat okoz, de a legrosszabb esetben több rekordot is eltávolíthat, mint amennyit tervezett.
egy korábbi cikkben részletesen elmagyaráztam mind a buktatókat, mind a megoldásukat. Vagy ha egyszerűen szeretné tartani, indítsa el a szükséges információkat programszerűen a társított entitásokon. Ehhez szükség lehet még néhány sornyi kódra, de elkerüli a váratlan mellékhatásokat.
következtetés
rengeteg példát találhatsz a sok-sok asszociációra a Való Világban, és könnyen feltérképezheted őket a JPA és a Hibernate segítségével. Sajnos ezek az egyszerű leképezések elrejtenek néhány buktatót, amelyeket elkerülhet az alábbi 5 legjobb gyakorlat követésével:
- modell asszociációk java-ként.util.Kész.
- adjon meg segédprogramokat egy entitás hozzáadásához vagy eltávolításához egy társításból.
- mindig használja FetchType.Lusta, ami az alapértelmezett, a teljesítményproblémák elkerülése érdekében.
- lekérdezés-specifikus Lekérés alkalmazása az n+1 select problémák elkerülése érdekében.
- ne használja a CASCADETYPES REMOVE-ot és mindent.