Java >> Java tutorial >  >> Tag >> hibernate

Hibernate-specifikke udvidelser til Criteria API

De fleste udviklere ved, at JPA-specifikationen definerer det strengbaserede JPQL-forespørgselssprog, og at Hibernate udvider det til at understøtte ting som databasespecifikke funktioner, vinduesfunktioner og sætbaserede operationer. Men de fleste udviklere ved ikke, at Hibernate siden version 6 har gjort det samme for JPA's Criteria API.

Udvidelse af en API er selvfølgelig lidt mere kompleks end at gøre det samme for et strengbaseret forespørgselssprog. For at udvide funktionerne i JPQL behøver Hibernate-teamet kun at tilføje flere funktioner til parseren af ​​forespørgselsstrengen og behøver ikke at ændre nogen af ​​de officielle API'er. Udvidelsen af ​​Criteria API kræver yderligere grænseflader og nye metoder, der returnerer disse grænseflader.

Hibernate 6 håndterer dette ved at levere HibernateCriteriaBuilder grænseflade, som udvider JPA's CriteriaBuilder interface, og ved at tilføje en metode til dens proprietære Session grænseflade for at få en HibernateCriteriaBuilder eksempel.

HibernateCriteriaBuilder grænseflade

Før vi taler om HibernateCriteriaBuilder grænseflade, skal vi tage et skridt tilbage og se på oprettelsen af ​​en standard CriteriaQuery . Og derefter vil jeg vise dig, hvordan du får en HibernateCriteriaBuilder og de funktioner, den føjer til JPA's standard Criteria API.

Arbejde med JPA's CriteriaBuilder grænseflade

Det første skridt til at bruge Criteria API er altid et kald af getCriteriaBuilder  metode på EntityManager  interface. Denne metode returnerer en forekomst af JPAs CriteriaBuilder , som du kan bruge til at oprette forskellige dele af din forespørgsel. I det følgende kodestykke bruger jeg det til at oprette en meget grundlæggende forespørgsel, der returnerer alle ChessGame  enheder, som en spiller spillede med de hvide brikker, hvis navn ender på "anssen".

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

CriteriaBuilder cBuilder = em.getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> game = q.from(ChessGame.class);
q.select(game);
q.where(cBuilder.like(game.get("playerWhite"), "%anssen"));

em.getTransaction().commit();
em.close();

Som du kan se, bruger jeg JPA's CriteriaBuilder 2 steder:

  1. Sådan oprettes en CriteriaQuery objekt, der repræsenterer en forespørgsel, der returnerer ChessGame genstande.
  2. For at oprette et like-prædikat for WHERE-sætningen i forespørgslen, der kontrollerer, om playerWhite attribut for Skakspillet er ligesom "%anssen"

JPA's CriteriaBuilder grænsefladen indeholder mange andre metoder, som du kan bruge til at instansiere forskellige typer CriteriaQuery objekter, byg mere komplekse WHERE-sætninger og kalder databasefunktioner. Jeg forklarer alt det mere detaljeret i Advanced Hibernate-kurset inkluderet i Persistence Hub, og du kan finde en komplet liste over alle metoder i den officielle Javadoc.

Sådan får du en HibernateCriteriaBuilder forekomst

Hibernates HibernateCriteriaBuilder interface udvider JPA's CriteriaBuilder interface. På grund af det, en implementering af HibernateCriteriaBuilder understøtter de samme metoder, og du kan bruge det på samme måde, som jeg viste dig i forrige afsnit. Derudover definerer grænsefladen nogle få proprietære metoder til at understøtte ting som sæt operationer og yderligere databasefunktioner.

Den største forskel, du vil genkende i din kode, er, hvordan du instansierer en HibernateCriteriaBuilder . Den bedste måde at instansiere det på er ved at kalde getCriteriaBuilder metode på Hibernates session grænseflade.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> game = q.from(ChessGame.class);
q.select(game);
q.where(cBuilder.like(game.get("playerWhite"), "%anssen"));

Du kan også caste en CriteriaBuilder grænseflade til HibernateCriteriaBuilder . Besætningen er naturligvis ikke typesikker, og den er afhængig af implementeringsdetaljerne, at Hibernate bruger den samme klasse til at implementere begge grænseflader. Jeg anbefaler derfor, at du får en session og kald getCriteriaBuilder metode.

HibernateCriteriaBuilder cBuilder = (HibernateCriteriaBuilder) em.getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> game = q.from(ChessGame.class);
q.select(game);
q.where(cBuilder.like(game.get("playerWhite"), "%anssen"));

Funktioner tilføjet af HibernateCriteriaBuilder

Som du kan se i den officielle Javadoc af HibernateCriteriaBuilder grænsefladen, definerer grænsefladen mange metoder til at bygge forskellige dele af din forespørgsel. Nogle af dem er defineret af JPA's CriteriaBuilder; andre er dvale-specifikke funktioner. Her er nogle af de mest interessante tilføjelser defineret af HibernateCriteriaBuilder grænseflade.

Indsæt i udvalgte udsagn

INSERT INTO SELECT-sætninger er en velkendt SQL-funktion, der gør det muligt for dig at indsætte data valgt af en forespørgsel som nye poster i en databasetabel. Siden version 6 understøtter Hibernate dette for HQL-sætninger, og Hibernate 6.1 vil give denne funktion som en udvidelse til Criteria API.

Yderligere udtryk

HibernateCriteriaBuilder definerer flere metoder til at skabe udtryk s, som du kan bruge til at udføre beregninger, transformere eller udtrække information og få den aktuelle dato eller klokkeslæt. Her er et par eksempler:

  • JpaExpression-tegn(udtryk x)
    Returnerer 1, hvis det angivne argument er positivt, -1, hvis det er negativt, og 0, hvis det er nøjagtigt 0.
  • JpaExpression loft (udtryk x)
    Returnerer det mindste heltal større eller lig med det angivne argument.
  • JpaExpression floor(Expression x)
    Returnerer det mindste største heltal, der er mindre eller lig med det angivne argument.
  • JpaExpression round(Expression x, Integer n)
    Returnerer det 1. argument afrundet til antallet af decimalcifre, der er angivet som det 2. argument.
  • JpaExpression exp(Expression x) og JpaExpression power(Expression x, Expression y)
    Returnerer Eulers nummer e hævet til magten af ​​det angivne argument eller returnerer det 1. argument hævet til magten af ​​det 2. argument.
  • JpaExpression ln(Expression x)
    Returnerer den naturlige logaritme af det angivne argument.
  • JpaExpression localDate() , JpaExpression localDateTime() og JpaExpression localTime()
    Returnerer den aktuelle dato, dato og klokkeslæt eller klokkeslæt for din databaseserver.

Svarende til metoderne defineret af JPA's CriteriaBuilder grænseflade, der definerer udtryk, kan du bruge disse metoder til at definere din forespørgsels projektion eller WHERE-sætning.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> game = q.from(ChessGame.class);
q.select(game);
q.where(cBuilder.equal(game.get("playedOn"), cBuilder.localDate()));

List<ChessGame> games = em.createQuery(q).getResultList();

Hibernate inkluderer derefter disse udtryk i den genererede SQL-sætning. Din database behandler dem og returnerer resultatet. Dette er vigtigt, hvis du behandler de returnerede værdier og stoler på tidszoner eller andre lokaliseringer. I disse situationer skal du sikre dig, at din Java-applikation og database bruger de samme indstillinger.

11:58:59,183 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.playedOn,c1_0.playerBlack_id,c1_0.playerWhite_id,c1_0.version from ChessGame c1_0 where c1_0.playedOn=current_date

Yderligere prædikater

Hibernate giver også et par ekstra prædikater, som du kan bruge til at definere din WHERE-sætning. De mest interessante er de forskellige versioner af ilike og notilike metoder, som giver en nem måde at definere et case-ufølsomt LIKE eller NOT LIKE udtryk.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessPlayer> q = cBuilder.createQuery(ChessPlayer.class);
Root<ChessPlayer> player = q.from(ChessPlayer.class);
q.select(player);
q.where(cBuilder.ilike(player.get("firstName"), "%ikar%"));

List<ChessPlayer> games = em.createQuery(q).getResultList();
games.forEach(p -> log.info(p));
16:32:13,147 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 where c1_0.firstName ilike ? escape ''
16:32:13,148 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [%ikar%]
16:32:13,168 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Hikaru, lastName=Nakamura]

Og hvis du modellerede en tilknytning som et java.util.Map , kan du bruge metoderne isMapEmpty , isMapNotEmpty , og mapSize for at kontrollere, om eller hvor mange elementer, der kortlægger indeholder.

Bestilling

JPA's CriteriaBuilder giver dig mulighed for at hente resultatsættet i stigende eller faldende rækkefølge af en eller flere entitetsattributter. Derudover er HibernateCriteriaBuilder giver dig også mulighed for at definere håndteringen af ​​nulværdier og rækkefølge efter resultatet af et udtryk , f.eks. resultatet af en databasefunktion.

Ud over asc og desc metoder defineret af JPA's CriteriaBuilder , HibernateCriteriaBuilder definerer en 2. version af hver metode, der accepterer en boolean som 2. metodeparameter. Denne boolean definerer, om null-værdier skal returneres først.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessPlayer> q = cBuilder.createQuery(ChessPlayer.class);
Root<ChessPlayer> player = q.from(ChessPlayer.class);
q.select(player);
q.orderBy(cBuilder.asc(player.get("firstName"), true));

List<ChessPlayer> games = em.createQuery(q).getResultList();
games.forEach(p -> log.info(p));
17:24:56,003 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 order by c1_0.firstName asc nulls first
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=2, firstName=Fabiano, lastName=Caruana]
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Hikaru, lastName=Nakamura]
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=1, firstName=Magnus, lastName=Carlsen]
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=3, firstName=Richard, lastName=Rapport]

Hvis du ønsker at definere en mere kompleks ORDER BY-klausul baseret på et udtryk , skal du ringe til en af ​​sorten metoder. De giver dig mulighed for at give udtrykket som du vil sortere resultatet efter, om du ønsker at få resultatet i stigende eller faldende rækkefølge og hvordan du vil håndtere nulværdier.

Jeg bruger det i det følgende kodestykke til at få forespørgselsresultatet i stigende rækkefølge efter længden af ​​spillerens fornavne. I dette eksempel giver det ingen mening at definere håndteringen af ​​nulværdier. Men hvis du bestiller dit forespørgselsresultat efter et andet udtryk , kan du angive en tredje metodeparameter til at definere håndteringen af ​​null-værdier.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessPlayer> q = cBuilder.createQuery(ChessPlayer.class);
Root<ChessPlayer> player = q.from(ChessPlayer.class);
q.select(player);
q.orderBy(cBuilder.sort(cBuilder.length(player.get("firstName")), SortOrder.ASCENDING));

List<ChessPlayer> games = em.createQuery(q).getResultList();
games.forEach(p -> log.info(p));
08:15:10,477 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 order by character_length(c1_0.firstName) asc
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=1, firstName=Magnus, lastName=Carlsen]
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Hikaru, lastName=Nakamura]
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=2, firstName=Fabiano, lastName=Caruana]
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=3, firstName=Richard, lastName=Rapport]

Indstil handlinger

Hibernate 6 introducerede også understøttelse af sætoperationer til HQL- og Criteria-forespørgsler. Brug af HibernateCriteriaBuilder , kan du nu kombinere resultatsættene af 2 forespørgselssætninger ved hjælp af metoderne union , unionAll , skærer , intersectAll , undtagen , og undtagenAlle .

Her kan du se et eksempel, der vælger fornavn og efternavn for alle ChessPlayer i 1. og fornavn og efternavn for alle ChessStreamer i 2. forespørgsel og skaber en forening af begge resultatsæt. Når du bruger sætoperationer, skal du huske på, at alle resultatsæt skal følge den samme struktur.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<Tuple> qPlayer = cBuilder.createTupleQuery();
Root<ChessPlayer> player = qPlayer.from(ChessPlayer.class);
qPlayer.multiselect(player.get("firstName").alias("firstName"), player.get("lastName").alias("lastName"));

CriteriaQuery<Tuple> qStreamer = cBuilder.createTupleQuery();
Root<ChessStreamer> streamer = qStreamer.from(ChessStreamer.class);
qStreamer.multiselect(streamer.get("firstName").alias("firstName"), streamer.get("lastName").alias("lastName"));

CriteriaQuery<Tuple> qPlayerAndStreamer = cBuilder.union(qPlayer, qStreamer);

List<Tuple> persons = em.createQuery(qPlayerAndStreamer).getResultList();
persons.forEach(t -> log.info(t.get("firstName") + ", " + t.get("lastName")));

Som du kan se i log-outputtet, genererede Hibernate en SQL-sætning, der fortæller databasen at anvende sætoperationsunionen på de 2 resultatsæt, der indeholder for- og efternavne på alle ChessPlayer og ChessStreamer .

17:43:05,857 DEBUG [org.hibernate.SQL] - select c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 union select c2_0.firstName,c2_0.lastName from ChessStreamer c2_0
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Hikaru, Nakamura
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Fabiano, Caruana
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Magnus, Carlsen
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Richard, Rapport
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Levy, Rozman
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Ben, Finegold

Konklusion

Som du så i denne artikel, Hibernates HibernateCriteriaBuilder interface udvider JPA's CriteriaBuilder interface og tilføjer metoder til Hibernates proprietære forespørgselsfunktioner. Disse er:

  • Yderligere udtryk s, som runde og exp , som du kan bruge til at udføre beregninger, transformere eller udtrække information og få den aktuelle dato eller klokkeslæt.
  • Yderligere prædikat s, ligesom ilike prædikatet , som du kan bruge til at definere dine WHERE-sætninger.
  • Metoder til at definere mere komplekse ORDER BY-sætninger, f.eks. baseret på resultatet af en SQL-funktion.
  • Indstil handlinger til at kombinere resultatet af flere forespørgsler.

Ved at tilføje alle disse proprietære funktioner, HibernateCriteriaBuilder interface giver dig de samme forespørgselsfunktioner som Hibernates HQL-forespørgselssprog, som udvider JPAs JPQL-sprog. Det giver dig mulighed for nemt at skifte mellem de 2 tilgange og bruge den forespørgselsdefinition, du føler dig bedst tilpas med.


Java tag