Cele mai bune practici pentru asociațiile Multi-La-Mulți cu Hibernate și JPA

asociațiile Multi-La-mulți sunt una dintre cele mai frecvent utilizate asociații cu JPA și Hibernate. Puteți găsi o mulțime de exemple pentru ei în lumea reală, și le puteți mapa cu JPA și Hibernate ca uni – sau asociere bidirecțională în modelul de domeniu.

dar probabil știți, de asemenea, că aceste mapări oferă mai multe capcane. În acest articol, vă voi arăta 5 cele mai bune practici care vă vor ajuta să evitați aceste capcane și să implementați mapări eficiente. Veți învăța:

  1. cel mai eficient tip de date pentru asocierea dvs.
  2. De ce aveți nevoie de metode de utilitate pentru a vă gestiona asocierea
  3. FetchType potrivit pentru o mapare eficientă
  4. când și cum să utilizați preluarea specifică interogării
  5. Tipul Cascad pe care ar trebui să îl evitați cu orice preț

nu mă voi scufunda în detaliile unei mapări de bază multi-La-mulți. Dacă nu sunteți sigur cum să creați o astfel de cartografiere, vă rugăm să aruncați o privire la secțiunea mulți-La-mulți din ghidul meu de cartografiere a Asociației.

cel mai eficient tip de date pentru asociația dvs.

YouTube video

majoritatea dezvoltatorilor nu petrec o mulțime de gânduri cu privire la tipul de date al unei asociații to-many. Ei aleg doar un java.util.Listă pentru că este simplu și nu efectuează verificări pentru a evita duplicatele.

este în regulă, dacă implementați o clasă Java de bază sau dacă modelați o asociere unu-la-mulți / mulți-la-unu. Dar nu ar trebui să utilizați niciodată o listă dacă modelați o asociație Multi-La-mulți.

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

mânerele Hibernate elimină operațiile din relațiile Multi-La-mulți care sunt mapate la un java.util.Listați foarte ineficient.

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

se elimină mai întâi toate înregistrările din tabelul de asociere înainte de a insera toate cele rămase.

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

ar trebui să modelați în schimb o asociere multi-La-mulți ca java.util.Gata.

@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 apoi se ocupă de a elimina operațiunile de asociere mult mai bine. Acum elimină doar înregistrările așteptate din asociație și le păstrează pe celelalte neatinse.

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

De ce aveți nevoie de metode de utilitate pentru a gestiona asocierea

asociațiile bidirecționale sunt mapate la un atribut de entitate la ambele capete ale relațiilor. Deci, în exemplul anterior, aveți un atribut autori pe entitatea carte și un atribut cărți pe entitatea autor. Acest lucru face ca implementarea unui JPQL sau Criteriiaquery să fie foarte confortabilă, deoarece puteți utiliza aceste atribute pentru a defini o clauză de asociere.dar adăugarea sau eliminarea unei asociații devine mai complicată. Întotdeauna trebuie să efectuați Schimbarea la ambele capete ale Asociației. De exemplu, dacă doriți să adăugați o carte La autor, trebuie să o adăugați la atributul Cărți al entității autor și, de asemenea, trebuie să adăugați autorul atributul autori pe entitatea carte. În caz contrar, contextul dvs. actual de persistență conține date inconsistente pe care le veți utiliza până la sfârșitul tranzacției curente.

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

metode de utilitate pe autor și entitățile de carte face actualizarea și eliminarea mult mai ușor. În cadrul acestor metode, efectuați operațiunile necesare pe ambele entități.

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

FetchType dreapta pentru o cartografiere eficientă

YouTube video

acesta este unul rapid. Ar trebui să utilizați întotdeauna FetchType.Leneș pentru multe-la-multe asociații. Acesta spune furnizorul de persistență nu pentru a prelua entitățile asociate din Baza de date până când le utilizați. Acesta este de obicei cazul când apelați metoda getter pentru prima dată.

Din fericire, aceasta este valoarea implicită pentru toate asociațiile to-many. Deci, asigurați-vă că nu o schimbați.

și dacă doriți să aflați mai multe despre diferitele FetchTypes ale JPA, vă rugăm să aruncați o privire la introducerea mea în FETCHTYPES JPA.

când și cum să utilizați preluarea specifică interogării

Dacă utilizați FetchType.LAZY, trebuie să știți despre preluarea specifică interogării. În caz contrar, cererea dumneavoastră va fi foarte lent, deoarece ați creat o mulțime de N+1 Selectați probleme.

YouTube video

când încărcați o entitate și utilizați preluarea specifică interogării, spuneți Hibernate ce asociații mapate va inițializa pentru fiecare entitate preluată. Apoi extinde clauza SELECT a interogării dvs. astfel încât să includă coloanele mapate de aceste alte entități și să inițializeze asociațiile. Și pentru că asociațiile sunt deja inițializate, Hibernate nu trebuie să efectueze o interogare suplimentară atunci când accesați metoda getter pentru prima dată.

puteți implementa preluarea specifică interogării în mai multe moduri diferite. Cea mai simplă este o clauză FETCH JOIN, pe care vă voi arăta aici. Dar puteți utiliza și un @ NamedEntityGraph sau un EntityGraph, pe care l-am explicat în articolele anterioare.

definiția unei clauze join FETCH este aproape identică cu o clauză simplă JOIN într-o interogare JPQL. Trebuie doar să adăugați cuvântul cheie FETCH.

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

chiar și așa, o clauză JOIN și JOIN FETCH arată foarte asemănătoare, clauza JOIN FETCH are un efect mult mai mare asupra interogării SQL generate. Nu numai că este tradus într-un SQL JOIN, așa cum este cazul unei clauze JPQL JOIN, ci forțează furnizorul dvs. de persistență să extindă clauza SELECT Cu toate coloanele mapate de entitatea asociată.

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

Tipul de cascadă pe care ar trebui să-l evitați cu orice preț

dacă activați cascading pe o asociere, furnizorul dvs. de persistență aplică operațiunile pe care le efectuați asupra entității tuturor entităților asociate. Dacă face asta pentru toate operațiunile sau doar pentru câteva selectate depinde de CascadeType configurat.

Video YouTube

care ar putea suna ca o idee uimitoare care face implementarea logicii dvs. de afaceri mult mai ușoară. Și asta nu este în întregime greșit.

dar vă rugăm să evitați CascadeTypes elimina și toate, care include elimina, pentru multe-la-multe asociații. În cel mai bun caz, creează doar probleme de performanță, dar în cel mai rău caz, ar putea elimina și mai multe înregistrări decât ați intenționat.

am explicat atât capcanele, cât și soluția lor în detalii minunate într-un articol anterior. Sau, dacă doriți să păstrați-l simplu, declanșa informațiile necesare programatic pe entitățile asociate. Acest lucru ar putea necesita încă câteva linii de cod, dar evită orice efecte secundare neașteptate.

concluzie

puteți găsi o mulțime de exemple pentru multe-la-multe asociații din lumea reală, și le puteți mapa cu ușurință cu JPA și Hibernate. Din păcate, aceste mapări simple ascund câteva capcane pe care le puteți evita urmând aceste 5 cele mai bune practici:

  1. Asociații de modele ca java.util.Gata.
  2. furnizați metode de utilitate pentru a adăuga sau elimina o entitate dintr-o asociație.
  3. utilizați întotdeauna FetchType.LAZY, care este implicit, pentru a evita problemele de performanță.
  4. aplicați preluarea specifică interogării pentru a evita problemele de selectare n + 1.
  5. nu utilizați CascadeTypes elimina și toate.

Lasă un răspuns

Adresa ta de email nu va fi publicată.