Rad s kolekcijama lijenih elemenata u JPA

Java Top

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

1. Pregled

JPA specifikacija pruža dvije različite strategije dohvaćanja: željnu i lijenu. Iako lijeni pristup pomaže izbjeći nepotrebno učitavanje podataka koji nam nisu potrebni, ponekad moramo čitati podatke koji u početku nisu učitani u zatvoreni kontekst postojanosti. Štoviše, pristup lijenim kolekcijama elemenata u zatvorenom kontekstu postojanosti čest je problem.

U ovom uputstvu usredotočit ćemo se na to kako učitati podatke iz lijenih kolekcija elemenata. Istražit ćemo tri različita rješenja: jedno koje uključuje jezik upita JPA, drugo s upotrebom grafikona entiteta i posljednje s širenjem transakcija.

2. Problem sakupljanja elemenata

Prema zadanim postavkama, JPA koristi strategiju lijenog dohvaćanja u asocijacijama tipa @ElementCollection. Stoga će svaki pristup zbirci u zatvorenom kontekstu postojanosti rezultirati iznimkom.

Da bismo razumjeli problem, definirajmo model domene na temelju odnosa između zaposlenika i njegovog telefonskog popisa:

@ Zaposlenik javne klase entiteta {@Id private int id; privatni naziv niza; @ElementCollection @CollectionTable (name = "phone_phone", joinColumns = @JoinColumn (name = "worker_id")) privatni popis telefona; // standardni konstruktori, getteri i postavljači} @Embeddable javni razred telefona {private String type; private String areaCode; privatni broj niza; // standardni konstruktori, getteri i postavljači}

Naš model određuje da zaposlenik može imati mnogo telefona. Popis telefona je zbirka ugradivih tipova. Upotrijebimo Spring Repozitorij s ovim modelom:

@Repository javna klasa EmployeeRepository {javni zaposlenik findById (int id) {return em.find (Employee.class, id); } // dodatna svojstva i pomoćne metode} 

Sada, reproducirajmo problem s jednostavnim JUnit test slučajem:

javna klasa ElementCollectionIntegrationTest {@Before public void init () {zaposlenik zaposlenik = novi zaposlenik (1, "Fred"); worker.setPhones (Arrays.asList (novi Telefon ("posao", "+55", "99999-9999"), novi Telefon ("dom", "+55", "98888-8888"))); workerRepository.save (zaposlenik); } @Nakon javne praznine clean () {workerRepository.remove (1); } @Test (očekuje se = org.hibernate.LazyInitializationException.class) javna praznina whenAccessLazyCollection_thenThrowLazyInitializationException () {Zaposlenik zaposlenik = workerRepository.findById (1); assertThat (worker.getPhones (). size (), je (2)); }} 

Ovaj test donosi iznimku kada pokušavamo pristupiti popisu telefona jer je kontekst postojanosti zatvoren.

Taj problem možemo riješiti do promjena strategije dohvaćanja @ElementCollection poslužiti se željnim pristupom. Međutim, željno dohvaćanje podataka nije nužno najbolje rješenje, budući da će se telefonski podaci uvijek učitati, bez obzira jesu li nam potrebni ili ne.

3. Učitavanje podataka jezikom upita JPA

Jezik upita JPA omogućuje nam prilagodbu projiciranih podataka. Stoga možemo definirati novu metodu u našem Spremište zaposlenika za odabir zaposlenika i njegovih telefona:

javni Employee findByJPQL (int id) {return em.createQuery ("SELECT u FROM Employee AS u JOIN FETCH u.phones WHERE u.id =: id", Employee.class) .setParameter ("id", id) .getSingleResult ( ); } 

Gornji upit koristi operaciju unutarnjeg spajanja za dohvaćanje popisa telefona za svakog zaposlenika koji se vratio.

4. Učitavanje podataka grafikonom entiteta

Drugo moguće rješenje je upotreba značajke grafikona entiteta iz JPA. Grafikon entiteta omogućuje nam odabir polja koja će se projektirati prema JPA upitima. Definirajmo još jednu metodu u našem spremištu:

javni zaposlenik findByEntityGraph (int id) {EntityGraph entityGraph = em.createEntityGraph (Employee.class); entityGraph.addAttributeNodes ("ime", "telefoni"); Svojstva karte = novi HashMap (); svojstva.put ("javax.persistence.fetchgraph", entityGraph); return em.find (Employee.class, id, properties); } 

To možemo vidjeti naš grafikon entiteta uključuje dva atributa: ime i telefone. Dakle, kada JPA ovo prevede u SQL, projicirat će povezane stupce.

5. Učitavanje podataka u transakcijskom opsegu

Napokon ćemo istražiti posljednje rješenje. Do sada smo vidjeli da je problem povezan sa životnim ciklusom konteksta postojanosti.

Ono što se događa je to naš kontekst postojanosti obuhvaća transakciju i ostat će otvoren dok transakcija ne završi. Životni ciklus transakcije obuhvaća od početka do kraja izvršenja metode spremišta.

Dakle, kreirajmo još jedan testni slučaj i konfigurirajmo naš kontekst postojanosti da se veže na transakciju započetu našom test metodom. Kontekst postojanosti držat ćemo otvorenim sve dok test ne završi:

@Test @Transactional javna praznina whenUseTransaction_thenFetchResult () {Zaposlenik zaposlenik = workerRepository.findById (1); assertThat (worker.getPhones (). size (), je (2)); } 

The @Transational anotacija konfigurira transakcijski proxy oko instance povezane klase ispitivanja. Štoviše, transakcija je povezana s niti koja je izvršava. Uzimajući u obzir zadanu postavku širenja transakcija, svaki kontekst postojanosti stvoren ovom metodom pridružuje se istoj toj transakciji. Slijedom toga, kontekst trajnosti transakcije vezan je za opseg transakcije ispitne metode.

6. Zaključak

U ovom vodiču, procijenili smo tri različita rješenja za rješavanje problema čitanja podataka iz lijenih udruga u zatvorenom kontekstu postojanosti.

Prvo smo koristili jezik upita JPA za dohvaćanje zbirki elemenata. Zatim smo definirali graf entiteta za dohvaćanje potrebnih podataka.

I, u krajnjem rješenju, koristili smo proljetnu transakciju kako bi kontekst postojanosti bili otvoreni i pročitali potrebne podatke.

Kao i uvijek, primjer koda za ovu lekciju dostupan je na GitHubu.

Dno Java

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

$config[zx-auto] not found$config[zx-overlay] not found