Introduksjon til Querydsl

1. Introduksjon

Dette er en innledende artikkel for å komme i gang med den kraftige Querydsl API for vedvaring av data.

Målet her er å gi deg de praktiske verktøyene for å legge til Querydsl i prosjektet ditt, forstå strukturen og formålet med de genererte klassene, og få en grunnleggende forståelse av hvordan du skriver typesikre databasespørsmål for de vanligste scenariene.

2. Formålet med Querydsl

Rammeverk for kartlegging av objekter er kjernen i Enterprise Java. Disse kompenserer misforholdet mellom objektorientert tilnærming og relasjonsdatabasemodell. De lar også utviklere skrive renere og mer kortfattet utholdenhetskode og domenelogikk.

Imidlertid er et av de vanskeligste designvalgene for et ORM-rammeverk API-en for å bygge riktige og typesikre spørsmål.

En av de mest brukte Java ORM-rammene, Hibernate (og også nært beslektet JPA-standard), foreslår et strengbasert spørrespråk HQL (JPQL) veldig lik SQL. De åpenbare ulempene med denne tilnærmingen er mangelen på typesikkerhet og fravær av statisk spørresjekk. I mer komplekse tilfeller (for eksempel når spørringen må konstrueres ved kjøretid, avhengig av noen forhold), innebærer det å bygge en HQL-spørring vanligvis sammenkobling av strenger som vanligvis er veldig usikre og feilutsatt.

JPA 2.0-standarden medførte en forbedring i form av Criteria Query API - en ny og typesikker metode for å bygge spørsmål som utnyttet metamodellklasser generert under forhåndsbehandling av merknader. Dessverre, fordi det var banebrytende i sin essens, endte Criteria Query API veldig verbose og praktisk talt uleselig. Her er et eksempel fra Jakarta EE tutorial for å generere et spørsmål så enkelt som VELG p FRA Kjæledyr s:

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

Ikke rart at et mer tilstrekkelig Querydsl-bibliotek snart dukket opp, basert på den samme ideen om genererte metadataklasser, men likevel implementert med et flytende og lesbart API.

3. Querydsl klassegenerasjon

La oss begynne med å generere og utforske de magiske metaklassene som står for den flytende API-en til Querydsl.

3.1. Legge til Querydsl til Maven Build

Å inkludere Querydsl i prosjektet ditt er så enkelt som å legge til flere avhengigheter i build-filen din og konfigurere et plugin for behandling av JPA-merknader. La oss starte med avhengighetene. Versjonen av Querydsl-biblioteker bør hentes ut til en egen eiendom i seksjon, som følger (for den siste versjonen av Querydsl-biblioteker, sjekk Maven Central repository):

 4.1.3 

Deretter legger du til følgende avhengigheter til delen av din pom.xml fil:

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

De querydsl-apt avhengighet er et annoteringsbehandlingsverktøy (APT) - implementering av tilsvarende Java API som tillater behandling av merknader i kildefiler før de går videre til kompileringsfasen. Dette verktøyet genererer de såkalte Q-typene - klasser som direkte er relatert til enhetsklassene i applikasjonen din, men som er prefikset med bokstaven Q. For eksempel hvis du har en Bruker klasse merket med @Enhet merknader i applikasjonen din, så vil den genererte Q-typen ligge i en QUser.java kildefil.

De sørget for omfanget av querydsl-apt avhengighet betyr at denne krukken bare skal gjøres tilgjengelig ved byggetid, men ikke inkluderes i applikasjonsgjenstanden.

Qerydsl-jpa-biblioteket er selve Querydsl, designet for å brukes sammen med et JPA-program.

For å konfigurere programtillegg for merking av behandling som utnytter querydsl-apt, legg til følgende plugin-konfigurasjon til din pom - inne i element:

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

Dette pluginet sørger for at Q-typene genereres under prosessmålet for Maven build. De outputDirectory konfigurasjonsegenskap peker til katalogen der Q-type kildefiler skal genereres. Verdien på denne egenskapen vil være nyttig senere når du skal utforske Q-filene.

Du bør også legge til denne katalogen i kildemappene til prosjektet, hvis IDE ikke gjør dette automatisk - se dokumentasjonen for din favoritt IDE om hvordan du gjør det.

For denne artikkelen vil vi bruke en enkel JPA-modell av en bloggtjeneste, bestående av Brukere og deres BlogPosts med et en-til-mange forhold mellom dem:

@Entity public class User {@Id @GeneratedValue private Long id; privat strenginnlogging; private boolske funksjonshemmede; @OneToMany (cascade = CascadeType.PERSIST, mappedBy = "user") private Sett blogPosts = ny HashSet (0); // getters and setters} @Entity public class BlogPost {@Id @GeneratedValue private Lang id; privat strengetittel; privat streng kroppen; @ManyToOne privat brukerbruker; // getters og setters}

For å generere Q-typer for modellen din, bare kjør:

mvn kompilere

3.2. Utforske genererte klasser

Gå nå til katalogen som er angitt i outputDirectory egenskapen til apt-maven-plugin (mål / genererte kilder / java i vårt eksempel). Du vil se en pakke og klassestruktur som direkte speiler domenemodellen din, bortsett fra at alle klassene starter med bokstaven Q (QUser og QBlogPost i vårt tilfelle).

Åpne filen QUser.java. Dette er inngangspunktet for å bygge alle spørsmål som har Bruker som en rotenhet. Det første du vil legge merke til er @Generert kommentar som betyr at denne filen ble generert automatisk og ikke skal redigeres manuelt. Skulle du endre noen av domenemodellklassene dine, må du kjøre mvn kompilere igjen for å regenerere alle tilsvarende Q-typer.

Bortsett fra flere QUser byggere som er tilstede i denne filen, bør du også legge merke til en offentlig statisk sluttforekomst av QUser klasse:

offentlig statisk slutt QUser bruker = ny QUser ("bruker");

Dette er forekomsten du kan bruke i de fleste av dine Querydsl-spørsmål til denne enheten, bortsett fra når du trenger å skrive noen mer komplekse spørsmål, som å bli med i flere forskjellige forekomster av en tabell i en enkelt spørring.

Det siste som bør bemerkes er at for hvert felt i enhetsklassen er det en tilsvarende *Sti felt i Q-typen, som NumberPath-ID, StringPath-pålogging og SetPath blogPosts i QUser klasse (legg merke til at navnet på feltet tilsvarer Sett er pluralisert). Disse feltene brukes som deler av flytende API som vi vil møte senere.

4. Spørring med Querydsl

4.1. Enkel spørring og filtrering

For å lage et spørsmål, trenger vi først en forekomst av en JPAQueryFactory, som er en foretrukket måte å starte byggeprosessen på. Det eneste som JPAQueryFactory behov er et EntityManager, som allerede skal være tilgjengelig i JPA-applikasjonen din via EntityManagerFactory.createEntityManager () ring eller @PersistenceContext injeksjon.

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

La oss nå lage vårt første spørsmål:

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

Legg merke til at vi har definert en lokal variabel QUser brukeren og initialisert den med QUser.user statisk forekomst. Dette gjøres kun for kortfattethet, alternativt kan du importere det statiske QUser.user felt.

De velg Fra metoden for JPAQueryFactory begynner å bygge et spørsmål. Vi gir den QUser forekomst og fortsett å bygge den betingede paragrafen til spørringen med .hvor() metode. De Brukerinnlogging er en referanse til a StringPath felt av QUser klasse som vi har sett før. De StringPath objektet har også .eq () metode som gjør det mulig å fortsette å bygge spørringen flytende ved å spesifisere forholdet for feltlikhet.

Til slutt, for å hente verdien fra databasen til utholdenhetskontekst, avslutter vi bygningskjeden med samtalen til fetchOne () metode. Denne metoden returnerer null hvis objektet ikke kan bli funnet, men kaster a NonUniqueResultException hvis det er flere enheter som tilfredsstiller .hvor() tilstand.

4.2. Bestilling og gruppering

La oss nå hente alle brukerne i en liste, sortert etter pålogging i stigningsrekkefølge.

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

Denne syntaksen er mulig fordi *Sti klasser har .asc () og .desc () metoder. Du kan også spesifisere flere argumenter for .rekkefølge etter() metode for å sortere etter flere felt.

La oss nå prøve noe vanskeligere. Anta at vi må gruppere alle innlegg etter tittel og telle dupliserende titler. Dette gjøres med .gruppe av() klausul. Vi vil også bestille titlene etter antall tilfeller.

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

Vi valgte blogginnleggets tittel og antall duplikater, gruppering etter tittel og deretter rekkefølge etter samlet antall. Legg merke til at vi først opprettet et alias for telle() felt i.å velge() klausul, fordi vi trengte å referere til den i .rekkefølge etter() klausul.

4.3. Komplekse spørsmål med sammenføyninger og underspørringer

La oss finne alle brukere som skrev et innlegg med tittelen "Hello World!" For slike spørsmål kan vi bruke en indre sammenføyning. Legg merke til at vi har opprettet et alias blogg innlegg for den sammenføyde tabellen for å referere til den i .på() klausul:

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

La oss nå prøve å oppnå det samme med underspørring:

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

Som vi kan se, er undersøk veldig lik spørsmål, og de er også ganske lesbare, men de begynner med JPAExpressions fabrikkmetoder. For å koble undersøk med hovedspørringen, refererer vi som alltid til aliasene som er definert og brukt tidligere.

4.4. Endring av data

JPAQueryFactory tillater ikke bare å lage spørsmål, men også å endre og slette poster. La oss endre brukerens pålogging og deaktivere kontoen:

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

Vi kan ha et hvilket som helst antall .sett() klausuler vi ønsker for forskjellige felt. De .hvor() klausul er ikke nødvendig, så vi kan oppdatere alle postene på en gang.

For å slette postene som samsvarer med en viss tilstand, kan vi bruke en lignende syntaks:

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

De .hvor() klausul er heller ikke nødvendig, men vær forsiktig, fordi utelatelse av .hvor() ledd resulterer i sletting av alle enhetene av en bestemt type.

Du lurer kanskje på hvorfor JPAQueryFactory har ikke den .sett inn() metode. Dette er en begrensning av JPA Query-grensesnittet. Det underliggende javax.persistence.Query.executeUpdate () metoden er i stand til å utføre oppdatering og sletting, men ikke sette inn utsagn. For å sette inn data, bør du bare vedvare enhetene med EntityManager.

Hvis du fortsatt vil dra nytte av en lignende Querydsl-syntaks for å sette inn data, bør du bruke SQLQueryFactory klasse som ligger i querydsl-sql-biblioteket.

5. Konklusjon

I denne artikkelen har vi oppdaget et kraftig og typesikkert API for vedvarende objektmanipulering som tilbys av Querydsl.

Vi har lært å legge til Querydsl i prosjektet og utforsket de genererte Q-typene. Vi har også dekket noen typiske brukssaker og likte deres kortfattethet og lesbarhet.

All kildekoden for eksemplene finner du i github-depotet.

Til slutt er det selvfølgelig mange flere funksjoner som Querydsl tilbyr, inkludert arbeid med rå SQL, ikke-vedvarende samlinger, NoSQL-databaser og fulltekstsøk - og vi vil utforske noen av disse i fremtidige artikler.


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