Úvod do Querydsl

1. Úvod

Toto je úvodný článok, ktorý vás oboznámi s fungovaním výkonného rozhrania Querydsl API na vytrvalosť údajov.

Cieľom je poskytnúť praktické nástroje na pridanie Querydsl do vášho projektu, porozumieť štruktúre a účelu vygenerovaných tried a získať základné znalosti o tom, ako písať typovo bezpečné databázové dotazy pre najbežnejšie scenáre.

2. Účel dotazu Querydsl

Rámec objektovo-relačného mapovania je jadrom Enterprise Java. Tieto kompenzujú nesúlad medzi objektovo orientovaným prístupom a modelom relačnej databázy. Umožňujú tiež vývojárom písať čistejší a výstižnejší kód perzistencie a logiku domény.

Jednou z najťažších možností návrhu rámca ORM je však API na vytváranie správnych a typovo bezpečných dotazov.

Jeden z najbežnejšie používaných rámcov Java ORM, Hibernate (a tiež úzko súvisiaci štandard JPA), navrhuje reťazcový dotazovací jazyk HQL (JPQL) veľmi podobný SQL. Zjavnou nevýhodou tohto prístupu je nedostatok bezpečnosti typu a absencia statickej kontroly dopytu. V zložitejších prípadoch (napríklad keď je potrebné dotaz zostaviť za behu, v závislosti od určitých podmienok), zostavenie dotazu HQL zvyčajne vyžaduje zreťazenie reťazcov, ktoré sú zvyčajne veľmi nebezpečné a náchylné na chyby.

Štandard JPA 2.0 priniesol vylepšenie v podobe Criteria Query API - novej a typovo bezpečnej metódy vytvárania dotazov, ktorá využívala výhody tried metamodel generovaných počas predspracovania anotácií. Criteria Query API, bohužiaľ, vo svojej podstate priekopnícke, skončilo veľmi podrobne a prakticky nečitateľne. Tu je príklad z tutoriálu Jakarta EE na generovanie jednoduchého dotazu VYBERTE p ZO Domáceho maznáčika str:

EntityManager em = ...; CriteriaBuilder cb = em.getCriteriaBuilder (); CriteriaQuery cq = cb.createQuery (Pet.class); Root pet = cq.from (Pet.class); cq.select (pet); TypedQuery q = em.createQuery (cq); Zoznam allPets = q.getResultList ();

Niet divu, že sa čoskoro objavila adekvátnejšia knižnica Querydsl, založená na rovnakej myšlienke generovaných tried metadát, implementovaná napriek tomu pomocou plynulého a čitateľného API.

3. Generovanie triedy Querydsl

Začnime s generovaním a skúmaním čarovných metaklasov, ktoré zodpovedajú plynulému API Querydsl.

3.1. Pridanie Querydsl do Maven Build

Zahrnutie Querydsl do vášho projektu je také jednoduché ako pridanie niekoľkých závislostí do vášho zostavovacieho súboru a konfigurácia doplnku na spracovanie anotácií JPA. Začnime závislosťami. Verzia knižníc Querydsl by sa mala extrahovať do samostatnej vlastnosti vo vnútri súboru časti (najnovšiu verziu knižníc Querydsl nájdete v centrálnom úložisku Maven):

 4.1.3 

Ďalej do súboru pridajte nasledujúce závislosti časť vašej pom.xml spis:

  com.querydsl querydsl-apt $ {querydsl.version} poskytnuté com.querydsl querydsl-jpa $ {querydsl.version} 

The querydsl-apt dependency je nástroj na spracovanie anotácií (APT) - implementácia zodpovedajúceho Java API, ktoré umožňuje spracovanie anotácií v zdrojových súboroch skôr, ako prejdú do fázy kompilácie. Tento nástroj generuje takzvané typy Q - triedy, ktoré priamo súvisia s triedami entít vašej aplikácie, ale majú predponu písmenom Q. Napríklad, ak máte Používateľ trieda označená @Entity anotácie vo vašej aplikácii, potom sa vygenerovaný typ Q bude nachádzať v a QUser.java zdrojový súbor.

The za predpokladu rozsah pôsobnosti querydsl-apt závislosť znamená, že táto jar by mala byť sprístupnená iba v čase zostavenia, ale nemala by byť zahrnutá do artefaktu aplikácie.

Knižnica querydsl-jpa je samotný Querydsl, navrhnutý na použitie spolu s aplikáciou JPA.

Konfigurácia doplnku na spracovanie anotácií, ktorý využíva výhody querydsl-apt, pridajte do svojho adaptéra nasledujúcu konfiguráciu pluginu - element:

 com.mysema.maven apt-maven-plugin 1.1.3 cieľ procesu / generované-zdroje / java com.querydsl.apt.jpa.JPAAnnotationProcessor 

Tento plugin zaisťuje, že sa typy Q generujú počas procesu procesu zostavenia Maven. The outputDirectory konfiguračná vlastnosť ukazuje na adresár, kde sa budú generovať zdrojové súbory typu Q. Hodnota tejto vlastnosti bude užitočná neskôr, keď preskúmate súbory Q.

Tento adresár by ste mali tiež pridať do zdrojových priečinkov projektu, ak to vaše IDE nerobí automaticky - pozrite si dokumentáciu k vášmu obľúbenému IDE, ako to urobiť.

V tomto článku použijeme jednoduchý model JPA blogovej služby, ktorý obsahuje: Používatelia a ich BlogPosts s pomerom medzi dvoma:

@Entity verejná trieda Používateľ {@Id @GeneratedValue private Long id; súkromné ​​prihlásenie pomocou reťazca; súkromný booleovský zdravotne postihnutý; @OneToMany (cascade = CascadeType.PERSIST, mappedBy = "user") private Set blogPosts = new HashSet (0); // getters and setters} @Entity public class BlogPost {@Id @GeneratedValue private Long id; súkromný názov reťazca; súkromné ​​strunové telo; @ManyToOne súkromný užívateľ; // zakladatelia a zakladatelia}

Ak chcete pre svoj model vygenerovať typy Q, jednoducho spustite:

mvn zostaviť

3.2. Skúmanie generovaných tried

Teraz choďte do adresára uvedeného v outputDirectory vlastnosť apt-maven-pluginu (cieľ / generované-zdroje / java v našom príklade). Uvidíte štruktúru balíka a triedy, ktorá priamo zrkadlí váš doménový model, s výnimkou toho, že všetky triedy začínajú písmenom Q (QUser a QBlogPost v našom prípade).

Otvorte súbor QUser.java. Toto je váš vstupný bod pre vytváranie všetkých dotazov, ktoré majú Používateľ ako koreňová entita. Prvá vec, ktorú si všimnete, je @ Generované anotácia, čo znamená, že tento súbor bol vygenerovaný automaticky a nemal by sa upravovať manuálne. Ak zmeníte ktorúkoľvek zo svojich tried doménových modelov, budete musieť bežať mvn zostaviť znova na regeneráciu všetkých zodpovedajúcich typov Q.

Až na niekoľko QUser konštruktéri prítomní v tomto súbore by ste si mali tiež všimnúť verejnú statickú poslednú inštanciu súboru QUser trieda:

verejný statický konečný užívateľ QUser = nový QUser ("užívateľ");

Toto je inštancia, ktorú môžete použiť vo väčšine svojich dotazov Querydsl na túto entitu, s výnimkou prípadov, keď potrebujete napísať zložitejšie dotazy, napríklad spojiť niekoľko rôznych inštancií tabuľky v jednom dotaze.

Posledná vec, ktorú si treba uvedomiť, je, že pre každé pole triedy entít existuje zodpovedajúca položka * Cesta pole v type Q, ako NumberPath id, Prihlásenie na StringPath a SetPath blogPríspevky v QUser triedy (všimnite si, že názov poľa zodpovedajúci Nastaviť je množné číslo). Tieto polia sa používajú ako súčasť rozhrania API pre plynulé dotazy, s ktorými sa neskôr stretneme.

4. Dopytovanie pomocou Querydsl

4.1. Jednoduché dopytovanie a filtrovanie

Na zostavenie dotazu najskôr budeme potrebovať inštanciu a JPAQueryFactory, čo je preferovaný spôsob začatia procesu výstavby. Jediná vec, ktorá JPAQueryFactory potreby je EntityManager, ktoré by už mali byť dostupné vo vašej aplikácii JPA cez EntityManagerFactory.createEntityManager () volať príp @PersistenceContext injekcia.

EntityManagerFactory emf = Persistence.createEntityManagerFactory ("com.baeldung.querydsl.intro"); EntityManager em = entityManagerFactory.createEntityManager (); JPAQueryFactory queryFactory = nový JPAQueryFactory (em);

Teraz vytvoríme náš prvý dopyt:

Užívateľ QUser = QUser.user; Používateľ c = queryFactory.selectFrom (user) .where (user.login.eq ("David")) .fetchOne ();

Všimnite si, že sme definovali lokálnu premennú QUser používateľa a inicializoval ho pomocou QUser.user statická inštancia. Toto sa deje čisto kvôli stručnosti, môžete tiež importovať statické QUser.user lúka.

The vybraťOd metóda JPAQueryFactory začne vytvárať dopyt. Míňame to QUser inštancie a pokračujte v vytváraní podmienenej klauzuly dotazu pomocou .kde() metóda. The user.login je odkaz na a StringPath pole QUser triedy, ktorú sme už videli. The StringPath objekt má tiež .eq () metóda, ktorá umožňuje plynule pokračovať v vytváraní dotazu zadaním podmienky rovnosti poľa.

Nakoniec, aby sme načítali hodnotu z databázy do kontextu vytrvalosti, ukončíme stavebný reťazec volaním do fetchOne () metóda. Táto metóda sa vráti nulový ak predmet nemožno nájsť, ale hodí a NonUniqueResultException ak existuje viac subjektov vyhovujúcich .kde() stav.

4.2. Objednávanie a zoskupovanie

Teraz načítame všetkých používateľov v zozname, zoradených podľa ich prihlásenia vo vzostupnom poradí.

Zoznam c = queryFactory.selectFrom (user) .orderBy (user.login.asc ()) .fetch ();

Táto syntax je možná, pretože * Cesta triedy majú .asc () a .desc () metódy. Môžete tiež určiť niekoľko argumentov pre .zoradiť podľa() spôsob triedenia podľa viacerých polí.

Teraz skúsme niečo zložitejšie. Predpokladajme, že musíme zoskupiť všetky príspevky podľa názvu a počítať duplicitné názvy. To sa deje pomocou .groupBy () doložka. Budeme tiež chcieť zoradiť tituly podľa výsledného počtu výskytov.

NumberPath count = Expressions.numberPath (Long.class, "c"); Zoznam userTitleCounts = queryFactory.select (blogPost.title, blogPost.id.count (). As (count)) .from (blogPost) .groupBy (blogPost.title) .orderBy (count.desc ()) .fetch ();

Vybrali sme názov blogového príspevku a počet duplikátov, pričom sme boli zoskupení podľa názvu a potom zoradení podľa agregovaného počtu. Všimnite si, že sme najskôr vytvorili alias pre count () pole v.vyberte () doložka, pretože sme ju potrebovali uviesť v .zoradiť podľa() doložka.

4.3. Komplexné dotazy so spojeniami a poddotazmi

Nájdeme všetkých používateľov, ktorí napísali príspevok s názvom „Hello World!“ Na takýto dopyt by sme mohli použiť vnútorné spojenie. Všimnite si, že sme vytvorili alias príspevok v blogu pre spojenú tabuľku, aby sa na ňu odkazovalo v .on () doložka:

QBlogPost blogPost = QBlogPost.blogPost; Zoznam používateľov = queryFactory.selectFrom (user) .innerJoin (user.blogPosts, blogPost) .on (blogPost.title.eq ("Hello World!")) .Fetch ();

Teraz sa pokúsime dosiahnuť to isté pomocou poddotazov:

Zoznam používateľov = queryFactory.selectFrom (používateľ) .where (user.id.in (JPAExpressions.select (blogPost.user.id) .from (blogPost) .where (blogPost.title.eq ("Hello World!"))) ) .fetch ();

Ako vidíme, poddotazy sú veľmi podobné dotazom a sú tiež celkom dobre čitateľné, ale začínajú sa na JPAExpressions továrenské metódy. Na pripojenie poddotazov k hlavnému dotazu ako vždy odkazujeme na aliasy definované a použité skôr.

4.4. Úprava údajov

JPAQueryFactory umožňuje nielen vytvárať dotazy, ale aj upravovať a mazať záznamy. Zmeňme prihlasovacie údaje používateľa a deaktivujme účet:

queryFactory.update (user) .where (user.login.eq ("Ash")) .set (user.login, "Ash2") .set (user.disabled, true) .execute ();

Môžeme mať ľubovoľný počet .set () klauzuly, ktoré chceme pre rôzne polia. The .kde() klauzula nie je nutná, takže môžeme aktualizovať všetky záznamy naraz.

Na odstránenie záznamov zodpovedajúcich určitej podmienke môžeme použiť podobnú syntax:

queryFactory.delete (user) .where (user.login.eq ("David")) .execute ();

The .kde() doložka tiež nie je nutná, ale buďte opatrní, pretože vynechanie .kde() doložka vedie k odstráneniu všetkých entít určitého typu.

Možno sa čudujete, prečo JPAQueryFactory nemá . vložiť () metóda. Toto je obmedzenie rozhrania JPA Query. Podkladové javax.persistence.Query.executeUpdate () metóda je schopná vykonávať aktualizácie a mazať, ale nie vkladať príkazy. Ak chcete vložiť údaje, mali by ste entity jednoducho zachovať pomocou EntityManageru.

Ak stále chcete využiť podobnú syntax Querydsl na vkladanie údajov, mali by ste použiť SQLQueryFactory trieda, ktorá sa nachádza v knižnici querydsl-sql.

5. Záver

V tomto článku sme objavili výkonné a typovo bezpečné API pre pretrvávajúcu manipuláciu s objektmi, ktoré poskytuje Querydsl.

Naučili sme sa pridávať Querydsl do projektu a skúmali vygenerované typy Q. Zaoberali sme sa tiež niektorými typickými prípadmi použitia a tešili sa z ich stručnosti a čitateľnosti.

Všetky zdrojové kódy príkladov nájdete v úložisku github.

Nakoniec tu samozrejme je oveľa viac funkcií, ktoré Querydsl poskytuje, vrátane práce so surovým SQL, neperzistentnými kolekciami, databázami NoSQL a fulltextovým vyhľadávaním - a niektoré z nich preskúmame v ďalších článkoch.