Práca s kolekciami Lazy Element v JPA

Java Top

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

1. Prehľad

Špecifikácia JPA poskytuje dve rôzne stratégie načítania: nedočkavé a lenivé. Aj keď lenivý prístup pomáha vyhnúť sa zbytočnému načítaniu údajov, ktoré nepotrebujeme, niekedy musíme čítať údaje, ktoré sa spočiatku nenačítali v uzavretom kontexte perzistencie. Spoločným problémom je navyše prístup k zbierkam lenivých prvkov v uzavretom kontexte perzistencie.

V tomto tutoriáli sa zameriame na to, ako načítať údaje zo zbierok lenivých prvkov. Preskúmame tri rôzne riešenia: jedno s dotazovacím jazykom JPA, ďalšie s využitím grafov entít a posledné s propagáciou transakcií.

2. Problém zbierania prvkov

V predvolenom nastavení JPA používa stratégiu lenivého načítania v asociáciách typu @ElementCollection. Akýkoľvek prístup do kolekcie v uzavretom kontexte perzistencie bude mať teda za následok výnimku.

Pre pochopenie problému definujme doménový model na základe vzťahu medzi zamestnancom a jeho telefónnym zoznamom:

@Entity public class Employee {@Id private int id; súkromné ​​meno reťazca; @ElementCollection @CollectionTable (name = "employee_phone", joinColumns = @JoinColumn (name = "employee_id")) súkromné ​​zoznamy telefónov; // štandardní konštruktori, getri a nastavovatelia} @Embeddable verejná trieda Telefón {súkromný typ reťazca; privátny reťazec areaCode; súkromné ​​číslo reťazca; // štandardné konštruktory, getre a setre}

Náš model určuje, že zamestnanec môže mať veľa telefónov. Telefónny zoznam je a zbierka zabudovateľných typov. Použijeme jarné úložisko s týmto modelom:

@Repository public class EmployeeRepository {public Employee findById (int id) {return em.find (Employee.class, id); } // ďalšie vlastnosti a pomocné metódy} 

Teraz si problém zopakujme pomocou jednoduchého testovacieho prípadu JUnit:

verejná trieda ElementCollectionIntegrationTest {@Before public void init () {zamestnanec zamestnanec = nový zamestnanec (1, "Fred"); employee.setPhones (Arrays.asList (new Phone ("work", "+55", "99999-9999"), new Phone ("home", "+55", "98888-8888"))); employeeRepository.save (zamestnanec); } @After public void clean () {employeeRepository.remove (1); } @Test (očakáva sa = org.hibernate.LazyInitializationException.class) public void whenAccessLazyCollection_thenThrowLazyInitializationException () {Employee employee = employeeRepository.findById (1); assertThat (employee.getPhones (). size (), is (2)); }} 

Tento test pri pokuse o prístup k telefónnemu zoznamu spôsobí výnimku, pretože kontext pretrvávania je uzavretý.

Tento problém môžeme vyriešiť zmena stratégie načítania súboru @ElementCollection použiť nedočkavý prístup. Získavanie údajov však dychtivo prebieha nie je nevyhnutne najlepším riešením, pretože dáta telefónu sa vždy načítajú, či už to potrebujeme alebo nie.

3. Načítanie údajov pomocou dotazovacieho jazyka JPA

Dotazovací jazyk JPA nám umožňuje prispôsobiť projektované informácie. Preto môžeme v našom definovať novú metódu Úložisko zamestnancov na výber zamestnanca a jeho telefónov:

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

Vyššie uvedený dopyt používa na načítanie telefónneho zoznamu operáciu vnútorného spojenia za každého vráteného zamestnanca.

4. Načítanie údajov pomocou grafu entít

Ďalším možným riešením je použitie funkcie grafu entít z JPA. Graf entity umožňuje zvoliť si, ktoré polia sa budú premietať pomocou dotazov JPA. Definujme v našom úložisku ešte jednu metódu:

public Employee findByEntityGraph (int id) {EntityGraph entityGraph = em.createEntityGraph (Employee.class); entityGraph.addAttributeNodes ("meno", "telefóny"); Vlastnosti mapy = nový HashMap (); properties.put ("javax.persistence.fetchgraph", entityGraph); návrat em.find (Employee.class, id, properties); } 

To vidíme náš entitový graf obsahuje dva atribúty: meno a telefóny. Keď to teda JPA preloží do jazyka SQL, premietne príslušné stĺpce.

5. Načítanie údajov v rozsahu transakcie

Na záver preskúmame posledné riešenie. Doteraz sme videli, že problém súvisí s životným cyklom Perzistencie v kontexte.

Čo sa stane, je to náš kontext vytrvalosti má rozsah transakcií a zostane otvorený, kým sa transakcia nedokončí. Životný cyklus transakcie trvá od začiatku do konca vykonávania metódy úložiska.

Poďme teda vytvoriť ďalší testovací prípad a nakonfigurovať náš kontext perzistencie tak, aby sa viazal na transakciu zahájenú našou testovacou metódou. Budeme udržiavať kontext Perzistencie otvorený, kým test neskončí:

@Test @Transactional public void whenUseTransaction_thenFetchResult () {Employee employee = employeeRepository.findById (1); assertThat (employee.getPhones (). size (), is (2)); } 

The @ Transakčné anotácia konfiguruje transakčný proxy okolo inštancie súvisiacej testovacej triedy. Transakcia je navyše spojená s vláknom, ktoré ju vykonáva. Ak vezmeme do úvahy predvolené nastavenie šírenia transakcií, každý kontext trvalosti vytvorený touto metódou sa pripája k tej istej transakcii. V dôsledku toho je kontext perzistencie transakcie viazaný na rozsah transakcie testovacej metódy.

6. Záver

V tomto návode vyhodnotili sme tri rôzne riešenia riešenia problému čítania údajov z lenivých asociácií v uzavretom kontexte perzistencie.

Najskôr sme na načítanie kolekcií prvkov použili dotazovací jazyk JPA. Ďalej sme definovali entitový graf na získanie potrebných údajov.

V konečnom riešení sme použili jarnú transakciu na udržanie kontextu Perzistencie v otvorenom stave a na prečítanie potrebných údajov.

Vzorový kód tohto tutoriálu je ako vždy k dispozícii na GitHub.

Java dole

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

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