Introduktion til Querydsl

1. Introduktion

Dette er en indledende artikel, der får dig til at køre med den kraftfulde Querydsl API til vedholdenhed af data.

Målet her er at give dig de praktiske værktøjer til at tilføje Querydsl til dit projekt, forstå strukturen og formålet med de genererede klasser og få en grundlæggende forståelse af, hvordan man skriver typesikker databaseforespørgsler til de mest almindelige scenarier.

2. Formålet med Querydsl

Objektrelationelle kortlægningsrammer er kernen i Enterprise Java. Disse kompenserer misforholdet mellem objektorienteret tilgang og relationsdatabasemodel. De tillader også udviklere at skrive renere og mere kortfattet udholdenhedskode og domæne logik.

Et af de sværeste designvalg for en ORM-ramme er imidlertid API'en til opbygning af korrekte og typesikre forespørgsler.

En af de mest anvendte Java ORM-rammer, Hibernate (og også nært beslægtet JPA-standard), foreslår et strengbaseret forespørgselssprog HQL (JPQL), der minder meget om SQL. De åbenlyse ulemper ved denne tilgang er manglen på typesikkerhed og fravær af statisk forespørgselskontrol. I mere komplekse tilfælde (for eksempel når forespørgslen skal konstrueres ved kørsel afhængigt af nogle forhold) involverer opbygning af en HQL-forespørgsel typisk sammenkædning af strenge, som normalt er meget usikre og fejlbehæftede.

JPA 2.0-standarden medførte en forbedring i form af Criteria Query API - en ny og typesikker metode til opbygning af forespørgsler, der udnyttede metamodelklasser genereret under forbehandling af annotering. Desværre var Criteria Query API meget banebrydende og praktisk talt uleselig, da den var banebrydende i sin essens. Her er et eksempel fra Jakarta EE-tutorial til generering af en forespørgsel så enkel som VÆLG p FRA Pet s:

EntityManager em = ...; CriteriaBuilder cb = em.getCriteriaBuilder (); CriteriaQuery cq = cb.createQuery (Pet.class); Roddyr = cq. Fra (pet.klasse); cq.select (pet); TypedQuery q = em.createQuery (cq); Vis allPets = q.getResultList ();

Ikke underligt, at der snart opstod et mere passende Querydsl-bibliotek baseret på den samme idé om genererede metadataklasser, men alligevel implementeret med et flydende og læsbart API.

3. Generering af Querydsl-klasse

Lad os starte med at generere og udforske de magiske metaklasser, der tegner sig for Querydsls flydende API.

3.1. Tilføjelse af Querydsl til Maven Build

At medtage Querydsl i dit projekt er så simpelt som at tilføje flere afhængigheder til din build-fil og konfigurere et plugin til behandling af JPA-kommentarer. Lad os starte med afhængighederne. Versionen af ​​Querydsl-biblioteker skal udvindes til en separat ejendom inde i sektion, som følger (for den nyeste version af Querydsl-biblioteker, se Maven Central repository):

 4.1.3 

Føj derefter følgende afhængigheder til sektion af din pom.xml fil:

  com.querydsl querydsl-apt $ {querydsl.version} forudsat com.querydsl querydsl-jpa $ {querydsl.version} 

Det querydsl-apt afhængighed er et annotationsbehandlingsværktøj (APT) - implementering af tilsvarende Java API, der tillader behandling af annoteringer i kildefiler, før de går videre til kompileringsfasen. Dette værktøj genererer de såkaldte Q-typer - klasser, der direkte relaterer til enhedsklasserne i din applikation, men er præfikset med bogstavet Q. For eksempel, hvis du har en Bruger klasse markeret med @Enhed kommentar i din applikation, så vil den genererede Q-type være i en QUser.java kildefil.

Det stillet til rådighed omfanget af querydsl-apt afhængighed betyder, at denne krukke kun skal stilles til rådighed ved byggetid, men ikke inkluderes i applikationsgenstanden.

Qerydsl-jpa-biblioteket er selve Querydsl designet til at blive brugt sammen med en JPA-applikation.

For at konfigurere plugin til annoteringsbehandling, der udnytter querydsl-apt, tilføj følgende plugin-konfiguration til din pom - inde i element:

 com.mysema.maven apt-maven-plugin 1.1.3 procesmål / genereret-kilder / java com.querydsl.apt.jpa.JPAAnnotationProcessor 

Dette plugin sørger for, at Q-typerne genereres under procesmålet med Maven build. Det outputDirectory konfigurationsegenskab peger på det bibliotek, hvor Q-type kildefiler genereres. Værdien af ​​denne egenskab vil være nyttig senere, når du skal udforske Q-filerne.

Du skal også tilføje denne mappe til projektets kildemapper, hvis din IDE ikke gør dette automatisk - se dokumentationen til din yndlings-IDE om, hvordan du gør det.

I denne artikel bruger vi en simpel JPA-model af en blogtjeneste, der består af Brugere og deres BlogPosts med et en-til-mange forhold mellem dem:

@Entity offentlig klasse bruger {@Id @GeneratedValue privat Lang id; privat streng login; private boolske handicappede; @OneToMany (cascade = CascadeType.PERSIST, mappedBy = "bruger") privat Sæt blogPosts = nyt HashSet (0); // getters and setters} @Entity public class BlogPost {@Id @GeneratedValue private Lang id; privat streng titel; private String organ; @ManyToOne privat brugerbruger; // getters og setters}

For at generere Q-typer til din model skal du blot køre:

mvn kompilere

3.2. Udforskning af genererede klasser

Gå nu til den mappe, der er angivet i outputDirectory ejendom af apt-maven-plugin (mål / genererede kilder / java i vores eksempel). Du vil se en pakke og klassestruktur, der direkte afspejler din domænemodel, undtagen alle klasser starter med bogstavet Q (QUser og QBlogPost i vores tilfælde).

Åbn filen QUser.java. Dette er din indgang til at opbygge alle forespørgsler, der har Bruger som en rodenhed. Den første ting, du vil bemærke, er @ Genereret kommentar, hvilket betyder, at denne fil blev genereret automatisk og ikke skulle redigeres manuelt. Skulle du ændre nogen af ​​dine domænemodellklasser, skal du køre mvn kompilere igen for at regenerere alle de tilsvarende Q-typer.

Bortset fra flere QUser konstruktører til stede i denne fil, skal du også være opmærksom på en offentlig statisk endelig forekomst af QUser klasse:

offentlig statisk endelig QUser-bruger = ny QUser ("bruger");

Dette er den instans, som du kan bruge i de fleste af dine Querydsl-forespørgsler til denne enhed, undtagen når du har brug for at skrive nogle mere komplekse forespørgsler, som at forbinde flere forskellige forekomster af en tabel i en enkelt forespørgsel.

Den sidste ting, der skal bemærkes, er, at for hvert felt i enhedsklassen er der en tilsvarende *Sti felt i Q-typen, ligesom NumberPath-id, StringPath login og SetPath blogPosts i QUser klasse (bemærk, at navnet på det felt, der svarer til Sæt er pluraliseret). Disse felter bruges som dele af flydende forespørgsel API, som vi vil støde på senere.

4. Forespørgsel med Querydsl

4.1. Enkel forespørgsel og filtrering

For at oprette en forespørgsel skal vi først bruge en forekomst af en JPAQueryFactory, som er en foretrukken måde at starte byggeprocessen på. Det eneste der JPAQueryFactory behov er et EntityManager, som allerede skulle være tilgængelig i din JPA-applikation via EntityManagerFactory.createEntityManager () ring eller @PersistenceContext indsprøjtning.

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

Lad os nu oprette vores første forespørgsel:

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

Bemærk, at vi har defineret en lokal variabel QUser brugeren og initialiserede den med QUser.user statisk forekomst. Dette gøres udelukkende for kortfattethed, alternativt kan du importere det statiske QUser.user Mark.

Det vælg Fra metode til JPAQueryFactory begynder at oprette en forespørgsel. Vi sender den QUser eksempel og fortsæt med at opbygge den betingede sætning i forespørgslen med .hvor() metode. Det user.login er en henvisning til en StringPath felt i QUser klasse, som vi har set før. Det StringPath objektet har også .eq () metode, der gør det muligt flydende at fortsætte med at oprette forespørgslen ved at specificere feltets ligestillingsbetingelse.

Endelig slutter vi bygningskæden med opkaldet til for at hente værdien fra databasen til persistens-kontekst fetchOne () metode. Denne metode vender tilbage nul hvis objektet ikke kan findes, men kaster et NonUniqueResultException hvis der er flere enheder, der tilfredsstiller .hvor() tilstand.

4.2. Bestilling og gruppering

Lad os nu hente alle brugere på en liste sorteret efter deres login i opstartsrækkefølge.

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

Denne syntaks er mulig, fordi *Sti klasser har .asc () og .desc () metoder. Du kan også angive flere argumenter for .orderBy () metode til at sortere efter flere felter.

Lad os nu prøve noget mere vanskeligt. Antag, at vi har brug for at gruppere alle indlæg efter titel og tælle duplikerende titler. Dette gøres med .groupBy () klausul. Vi vil også gerne bestille titlerne efter antallet af forekomster.

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

Vi valgte titlen på blogindlægget og antallet af dubletter, gruppering efter titel og derefter rækkefølge efter samlet antal. Bemærk, at vi først oprettede et alias til tælle() felt i.Vælg() klausul, fordi vi havde brug for at henvise til den i .orderBy () klausul.

4.3. Komplekse forespørgsler med sammenføjninger og underforespørgsler

Lad os finde alle brugere, der skrev et indlæg med titlen "Hello World!" Til en sådan forespørgsel kunne vi bruge en indre sammenføjning. Bemærk, at vi har oprettet et alias blogindlæg for at den sammenføjede tabel skal henvise til den i .på() klausul:

QBlogPost blogPost = QBlogPost.blogPost; Liste brugere = queryFactory.selectFrom (bruger) .innerJoin (user.blogPosts, blogPost). På (blogPost.title.eq ("Hello World!")) .Fetch ();

Lad os nu prøve at opnå det samme med underforespørgsel:

Liste brugere = queryFactory.selectFrom (bruger) .where (user.id.in (JPAExpressions.select (blogPost.user.id). Fra (blogPost) .where (blogPost.title.eq ("Hello World!"))) ) .fetch ();

Som vi kan se, er underforespørgsler meget lig forespørgsler, og de er også ret læselige, men de starter med JPAExpressions fabriksmetoder. For at forbinde underforespørgsler med hovedforespørgslen henviser vi som altid til de aliasser, der er defineret og brugt tidligere.

4.4. Ændring af data

JPAQueryFactory tillader ikke kun at konstruere forespørgsler, men også at ændre og slette poster. Lad os ændre brugerens login og deaktivere kontoen:

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

Vi kan have et hvilket som helst antal .sæt() klausuler, vi ønsker for forskellige felter. Det .hvor() klausul er ikke nødvendig, så vi kan opdatere alle poster på én gang.

For at slette de poster, der matcher en bestemt tilstand, kan vi bruge en lignende syntaks:

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

Det .hvor() klausul er heller ikke nødvendig, men vær forsigtig, fordi udeladelse af .hvor() klausul resulterer i sletning af alle enheder af en bestemt type.

Du undrer dig måske over hvorfor JPAQueryFactory har ikke .indsæt () metode. Dette er en begrænsning af JPA-forespørgselsgrænsefladen. Det underliggende javax.persistence.Query.executeUpdate () metoden er i stand til at udføre opdatering og sletning, men ikke indsætte udsagn. For at indsætte data skal du blot vedligeholde enhederne med EntityManager.

Hvis du stadig vil drage fordel af en lignende Querydsl-syntaks til indsættelse af data, skal du bruge SQLQueryFactory klasse, der ligger i querydsl-sql biblioteket.

5. Konklusion

I denne artikel har vi opdaget en kraftfuld og typesikker API til vedvarende objektmanipulation, der leveres af Querydsl.

Vi har lært at tilføje Querydsl til projektet og udforsket de genererede Q-typer. Vi har også dækket nogle typiske brugssager og haft deres kortfattethed og læsbarhed.

Al kildekoden til eksemplerne findes i github-arkivet.

Endelig er der naturligvis mange flere funktioner, som Querydsl tilbyder, herunder arbejde med rå SQL, ikke-vedvarende samlinger, NoSQL-databaser og søgning i fuldtekst - og vi vil undersøge nogle af disse i fremtidige artikler.