Uvod u Querydsl

1. Uvod

Ovo je uvodni članak koji će vas pokrenuti s moćnim API-jem Querydsl za trajnost podataka.

Cilj je ovdje dati vam praktične alate za dodavanje Querydsla u vaš projekt, razumjeti strukturu i svrhu generiranih klasa i steći osnovno razumijevanje kako pisati upite baze podataka sigurne za tip za najčešće scenarije.

2. Svrha Querydsla

Objektno-relacijski okviri mapiranja jezgra su Enterprise Java. Oni kompenziraju neusklađenost između objektno orijentiranog pristupa i relacijskog modela baze podataka. Oni također omogućuju programerima da napišu čistiji i sažetiji kôd postojanja i logiku domene.

Međutim, jedan od najtežih izbora za dizajn ORM okvira je API za izgradnju ispravnih i sigurnih upita.

Jedan od najčešće korištenih Java ORM okvira, Hibernate (i također usko povezan JPA standard), predlaže jezik upita HQL (JPQL) zasnovan na nizovima vrlo sličan SQL-u. Očiti nedostaci ovog pristupa su nedostatak sigurnosti tipa i nepostojanje statičke provjere upita. Također, u složenijim slučajevima (na primjer, kada je upit potrebno konstruirati tijekom izvođenja, ovisno o nekim uvjetima), izrada HQL upita obično uključuje spajanje nizova koji je obično vrlo nesiguran i sklon pogreškama.

Standard JPA 2.0 donio je poboljšanje u obliku API-ja za kriterije kriterija - novu i sigurnu metodu izrade upita koja je iskoristila klase metamodela generirane tijekom predobrade bilješki. Nažalost, budući da je u svojoj osnovi bio revolucionaran, Criteria Query API završio je vrlo opširan i praktički nečitljiv. Evo primjera iz udžbenika Jakarta EE za generiranje jednostavnog upita ODABERITE p IZ Pet str:

EntityManager em = ...; CriteriaBuilder cb = em.getCriteriaBuilder (); CriteriaQuery cq = cb.createQuery (Pet.class); Korijenski ljubimac = cq.od (Pet.class); cq.select (ljubimac); TypedQuery q = em.createQuery (cq); Popis svihPets = q.getResultList ();

Nije ni čudo što se ubrzo pojavila adekvatnija knjižnica Querydsl, temeljena na istoj ideji generiranih klasa metapodataka, a implementirana s tečnim i čitljivim API-jem.

3. Generacija klase Querydsl

Počnimo s generiranjem i istraživanjem čarobnih metaklasa koje čine tečni API Querydsla.

3.1. Dodavanje Querydsla u Maven Build

Uključivanje Querydsla u vaš projekt jednostavno je poput dodavanja nekoliko ovisnosti vašoj datoteci gradnje i konfiguriranja dodatka za obradu JPA bilješki. Krenimo od ovisnosti. Verzija knjižnica Querydsl trebala bi se izdvojiti u zasebno svojstvo unutar odjeljak, kako slijedi (za najnoviju verziju knjižnica Querydsl, provjerite spremište Maven Central):

 4.1.3 

Dalje, dodajte sljedeće ovisnosti u odjeljak vašeg pom.xml datoteka:

  com.querydsl querydsl-apt $ {querydsl.version} pod uvjetom da com.querydsl querydsl-jpa $ {querydsl.version} 

The querydsl-apt ovisnost je alat za obradu napomena (APT) - implementacija odgovarajućeg Java API-ja koji omogućuje obradu napomena u izvornim datotekama prije nego što prijeđu u fazu kompilacije. Ovaj alat generira takozvane Q-tipove - klase koje se izravno odnose na klase entiteta vaše aplikacije, ali imaju prefiks sa slovom Q. Na primjer, ako imate Korisnik razred označen s @ Entitet napomena u vašoj aplikaciji, tada će generirani Q-tip boraviti u a QUser.java izvorna datoteka.

The pod uvjetom opseg querydsl-apt ovisnost znači da bi ovaj jar trebao biti dostupan samo u vrijeme izrade, ali ne i uključen u artefakt aplikacije.

Biblioteka querydsl-jpa sama je Querydsl, dizajnirana za upotrebu zajedno s JPA aplikacijom.

Za konfiguriranje dodatka za obradu napomena koji koristi prednosti querydsl-apt, dodajte sljedeću konfiguraciju dodatka u svoj pom - unutar element:

 com.mysema.maven apt-maven-plugin 1.1.3 proces target / generated-sources / java com.querydsl.apt.jpa.JPAAnnotationProcessor 

Ovaj dodatak osigurava da se Q-tipovi generiraju tijekom procesa procesa Maven gradnje. The outputDirectory konfiguracijsko svojstvo ukazuje na direktorij u kojem će se generirati izvorne datoteke Q-tipa. Vrijednost ovog svojstva bit će korisna kasnije, kada krenete u istraživanje Q-datoteka.

Također biste trebali dodati ovaj direktorij u izvorne mape projekta, ako vaš IDE to ne radi automatski - potražite u dokumentaciji za svoj omiljeni IDE kako to učiniti.

Za ovaj ćemo članak koristiti jednostavni JPA model blog usluge koji se sastoji od Korisnici i njihovi BlogPosts s odnosom jedan-prema-mnogima:

@Entity javni razred Korisnik {@Id @GeneratedValue private Long id; prijava u privatni niz; privatni Booleov onemogućen; @OneToMany (cascade = CascadeType.PERSIST, mappedBy = "user") privatni Postavi blogPosts = novi HashSet (0); // getteri i postavljači} @Entity javna klasa BlogPost {@Id @GeneratedValue private Long id; privatni naslov niza; tijelo privatne žice; @ManyToOne privatni korisnik; // geteri i postavljači}

Da biste generirali Q-vrste za svoj model, jednostavno pokrenite:

mvn sastaviti

3.2. Istraživanje generirane nastave

Sada idite u direktorij naveden u outputDirectory svojstvo apt-maven-plugin (target / generirani izvori / java u našem primjeru). Vidjet ćete strukturu paketa i klase koja izravno odražava model vaše domene, osim što sve klase počinju slovom Q (QUser i QBlogPost u našem slučaju).

Otvorite datoteku QUser.java. Ovo je vaša ulazna točka za izgradnju svih upita koji postoje Korisnik kao korijenski entitet. Prvo što ćete primijetiti je @ Generirano napomena što znači da je ova datoteka automatski generirana i da je ne treba ručno uređivati. Ako promijenite bilo koju klasu modela domene, morat ćete pokrenuti mvn sastaviti opet za regeneraciju svih odgovarajućih Q-tipova.

Osim nekoliko QUser konstruktori prisutni u ovoj datoteci, trebali biste također primijetiti javnu statičku konačnu instancu QUser razred:

javni statički konačni korisnik QUser = novi QUser ("korisnik");

Ovo je instanca koju možete koristiti u većini svojih upita Querydsl ovom entitetu, osim kada trebate napisati neke složenije upite, poput spajanja nekoliko različitih instanci tablice u jedan upit.

Posljednje na što treba obratiti pažnju jest da za svako polje klase entiteta postoji odgovarajuće *Staza polje u Q-tipu, poput ID brojčane staze, StringPath prijava i SetPath blogPosts u QUser klasa (primijetite da naziv polja odgovara Postavi je pluraliziran). Ta se polja koriste kao dijelovi API-ja za fluidne upite s kojima ćemo se kasnije susresti.

4. Upiti pomoću Querydsla

4.1. Jednostavno postavljanje upita i filtriranje

Da bismo izgradili upit, prvo će nam trebati primjerak JPAQueryFactory, što je preferirani način započinjanja procesa gradnje. Jedino što JPAQueryFactory potrebe je EntityManager, koji bi već trebao biti dostupan u vašoj aplikaciji JPA putem EntityManagerFactory.createEntityManager () nazovite ili @PersistenceContext injekcija.

EntityManagerFactory emf = Perzistentnost.createEntityManagerFactory ("com.baeldung.querydsl.intro"); EntityManager em = entityManagerFactory.createEntityManager (); JPAQueryFactory queryFactory = novi JPAQueryFactory (em);

Sada kreirajmo svoj prvi upit:

QUser korisnik = QUser.user; Korisnik c = queryFactory.selectFrom (user) .where (user.login.eq ("David")) .fetchOne ();

Primijetite da smo definirali lokalnu varijablu QUser korisnik i inicijalizirao ga s QUser.korisnik statička instanca. To je učinjeno čisto radi kratkoće, a možete i uvoziti statički QUser.korisnik polje.

The odaberiteOd metoda JPAQueryFactory započinje izgradnju upita. Prolazimo QUser instance i nastavite graditi uvjetnu klauzulu upita s .gdje() metoda. The korisnik.login je referenca na a StringPath polje QUser razred koji smo već vidjeli. The StringPath objekt također ima .eq () metoda koja omogućuje tečno nastavljanje izgradnje upita specificiranjem uvjeta jednakosti polja.

Konačno, da bismo dohvatili vrijednost iz baze podataka u kontekst postojanosti, završavamo lanac zgrade pozivom na fetchOne () metoda. Ova metoda se vraća null ako objekt nije moguće pronaći, ali baca a NonUniqueResultException ako postoji više entiteta koji zadovoljavaju .gdje() stanje.

4.2. Naručivanje i grupiranje

Sada dohvatimo sve korisnike na popisu, poredane prema njihovoj prijavi u uzlaznom redoslijedu.

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

Ova sintaksa je moguća jer *Staza razredi imaju .asc () i .desc () metode. Također možete navesti nekoliko argumenata za .orderBy () metoda za sortiranje po više polja.

Pokušajmo sada nešto teže. Pretpostavimo da trebamo grupirati sve postove prema naslovu i brojati duplicirane naslove. To je učinjeno s .groupBy () klauzula. Također ćemo htjeti naslove poredati prema rezultatima pojavljivanja.

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

Odabrali smo naslov bloga i broj duplikata, grupirajući prema naslovu, a zatim poredajući prema skupnom broju. Primijetite da smo prvo stvorili alias za računati() polje u.Odaberi() klauzula, jer smo je trebali referencirati u .orderBy () klauzula.

4.3. Složeni upiti sa spojevima i podupitima

Pronađimo sve korisnike koji su napisali post pod nazivom "Hello World!" Za takav upit mogli bismo upotrijebiti unutarnji spoj. Primijetite da smo stvorili alias blogPost da bi je spojena tablica upućivala na nju u .na() klauzula:

QBlogPost blogPost = QBlogPost.blogPost; Popis korisnika = queryFactory.selectFrom (user) .innerJoin (user.blogPosts, blogPost) .on (blogPost.title.eq ("Hello World!")) .Fetch ();

Pokušajmo sada postići isto s podupitom:

Popis korisnika = queryFactory.selectFrom (user) .where (user.id.in (JPAExpressions.select (blogPost.user.id) .from (blogPost) .where (blogPost.title.eq ("Hello World!"))) ) .fetch ();

Kao što vidimo, podupiti su vrlo slični upitima, a također su i prilično čitljivi, ali počinju sa JPAExpression tvorničke metode. Da bismo povezali podupite s glavnim upitom, kao i uvijek, pozivamo se na ranije definirane i korištene pseudonime.

4.4. Izmjena podataka

JPAQueryFactory omogućuje ne samo konstruiranje upita, već i izmjenu i brisanje zapisa. Promijenimo korisničko ime i onemogućimo račun:

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

Možemo imati bilo koji broj .set () klauzule koje želimo za različita polja. The .gdje() klauzula nije potrebna, tako da možemo istovremeno ažurirati sve zapise.

Da bismo izbrisali zapise koji odgovaraju određenom stanju, možemo koristiti sličnu sintaksu:

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

The .gdje() klauzula također nije potrebna, ali budite oprezni jer izostavljate .gdje() klauzula rezultira brisanjem svih entiteta određene vrste.

Možda se pitate zašto JPAQueryFactory nema .umetnuti() metoda. Ovo je ograničenje sučelja JPA upita. Temeljni javax.persistence.Query.executeUpdate () metoda može izvršiti ažuriranje i brisanje, ali ne i umetanje izjava. Da biste umetnuli podatke, jednostavno trebate ustrajati entitete s EntityManager-om.

Ako i dalje želite iskoristiti sličnu sintaksu Querydsl za umetanje podataka, trebali biste je koristiti SQLQueryFactory klase koja se nalazi u knjižnici querydsl-sql.

5. Zaključak

U ovom smo članku otkrili moćan i siguran za API API za trajnu manipulaciju objektima koji pruža Querydsl.

Naučili smo dodati Querydsl u projekt i istražili generirane Q-vrste. Također smo obradili neke tipične slučajeve upotrebe i uživali u njihovoj sažetosti i čitljivosti.

Sav izvorni kod za primjere može se naći u spremištu github.

Konačno, Querydsl nudi, naravno, mnogo više značajki, uključujući rad sa sirovim SQL-om, nepostojanim zbirkama, NoSQL bazama podataka i pretraživanjem cjelovitog teksta - a neke ćemo istražiti u budućim člancima.