Java >> Java tutorial >  >> Tag >> SQL

Hibernate Tips:Sådan anvender du DISTINCT til din JPQL, men ikke din SQL-forespørgsel

Hibernate Tips er en række indlæg, hvor jeg beskriver en hurtig og nem løsning på almindelige Hibernate-spørgsmål. Hvis du har et spørgsmål til et fremtidigt Hibernate Tip, bedes du skrive en kommentar nedenfor.

Spørgsmål:

Jeg bruger en forespørgsel med en JOIN FETCH-klausul til at indlæse overordnede enheder med alle deres underordnede enheder. JPQL-forespørgslen kræver, at DISTINCT-nøgleordet kun returnerer hver overordnet enhed én gang. Jeg vil ikke have DISTINCT i SQL-forespørgslen. Hvordan kan jeg fortælle Hibernate kun at bruge DISTINCT i JPQL-forespørgslen?

Løsning:

JPA's JOIN FETCH-klausul giver en fantastisk og nem at bruge måde at indlæse en enhed og dens dovne associationer i én forespørgsel. Fra et præstationssynspunkt er dette meget bedre end at bruge yderligere forespørgsler til at initialisere tilknytningerne. Det er især tilfældet, hvis du indlæser en Liste af enheder.

Men hvis du bruger denne klausul for første gang, vil du måske blive overrasket over, at Listen af enheder returneret af getResultList metoden indeholder dubletter. SQL-forespørgslen forbinder de tilknyttede databaseposter, og resultatsættet består af kombinationer af en overordnet post med hver underordnede post. Desværre løser Hibernate ikke disse dubletter som standard.

Her kan du se et eksempel på sådan en JPQL-forespørgsel, den udførte SQL-forespørgsel og de returnerede entiteter.

List<Author> authors = em.createQuery(
				"SELECT a FROM Author a JOIN FETCH a.books",
				Author.class).getResultList();
for (Author a : authors) {
	log.info(a.getFirstName() + " " + a.getLastName() + " wrote "
			+ a.getBooks().size() + " books.");
}

Som du kan se i logerklæringerne, er List forfatterne indeholder lige så mange referencer til Forfatter 1 og Forfatter 2 enheder, mens de har skrevet bøger. Hver af disse referencer peger på det samme objektobjekt, som indeholder en fuldt initialiseret liste over relaterede bog enheder.

08:31:06,047 DEBUG [org.hibernate.SQL] - 
    select
        author0_.id as id1_0_0_,
        book2_.id as id1_1_1_,
        author0_.firstName as firstNam2_0_0_,
        author0_.lastName as lastName3_0_0_,
        author0_.version as version4_0_0_,
        book2_.publisherid as publishe5_1_1_,
        book2_.publishingDate as publishi2_1_1_,
        book2_.title as title3_1_1_,
        book2_.version as version4_1_1_,
        books1_.authorId as authorId2_2_0__,
        books1_.bookId as bookId1_2_0__ 
    from
        Author author0_ 
    inner join
        BookAuthor books1_ 
            on author0_.id=books1_.authorId 
    inner join
        Book book2_ 
            on books1_.bookId=book2_.id
08:31:06,227 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 1 wrote 2 books.
08:31:06,227 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 1 wrote 2 books.
08:31:06,227 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 2 wrote 3 books.
08:31:06,227 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 2 wrote 3 books.
08:31:06,227 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 2 wrote 3 books.
08:31:06,227 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 3 wrote 1 books.

Returnering kun unikke resultater

Du kan tilføje DISTINCT nøgleord til din forespørgsel for at bede Hibernate returnere hver Forfatter enhed kun én gang.

List<Author> authors = em.createQuery(
				"SELECT DISTINCT a FROM Author a JOIN FETCH a.books",
				Author.class).getResultList();
for (Author a : authors) {
	log.info(a.getFirstName() + " " + a.getLastName() + " wrote "
			+ a.getBooks().size() + " books.");
}

Men som du kan se i de følgende logmeddelelser, tilføjer Hibernate også nøgleordet DISTINCT til SQL-forespørgslen. Dette er ofte ikke tilsigtet og kan resultere i en effektiv databaseforespørgsel.

08:35:53,031 DEBUG [org.hibernate.SQL] - 
    select
        distinct author0_.id as id1_0_0_,
        book2_.id as id1_1_1_,
        author0_.firstName as firstNam2_0_0_,
        author0_.lastName as lastName3_0_0_,
        author0_.version as version4_0_0_,
        book2_.publisherid as publishe5_1_1_,
        book2_.publishingDate as publishi2_1_1_,
        book2_.title as title3_1_1_,
        book2_.version as version4_1_1_,
        books1_.authorId as authorId2_2_0__,
        books1_.bookId as bookId1_2_0__ 
    from
        Author author0_ 
    inner join
        BookAuthor books1_ 
            on author0_.id=books1_.authorId 
    inner join
        Book book2_ 
            on books1_.bookId=book2_.id
08:35:53,686 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 3 wrote 1 books.
08:35:53,687 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 2 wrote 3 books.
08:35:53,688 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 1 wrote 2 books.

Den PassDistinctThrough Tip

Siden Hibernate 5.2 kan du forhindre Hibernate i at tilføje DISTINCT nøgleord til SQL-sætningen ved at indstille forespørgselstippet hibernate.query.passDistinctThrough til falsk. Den nemmeste måde at indstille dette tip på er at bruge konstanten defineret i Hibernates org.hibernate.jpa.QueryHints og org.hibernate.annotations.QueryHints klasse.

TypedQuery<Author> q = em.createQuery("SELECT DISTINCT a FROM Author a JOIN FETCH a.books", Author.class);
q.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false);
List<Author> authors = q.getResultList();
for (Author a : authors) {
	log.info(a.getFirstName() + " " + a.getLastName() + " wrote "
			+ a.getBooks().size() + " books.");
}

Som du kan se i logmeddelelserne, tilføjer Hibernate ikke længere DISTINCT nøgleord til SQL-sætningen. Det anvender det nu kun på SQL-resultatsættet og returnerer hver Author enhed kun én gang.

08:36:55,075 DEBUG [org.hibernate.SQL] - 
    select
        author0_.id as id1_0_0_,
        book2_.id as id1_1_1_,
        author0_.firstName as firstNam2_0_0_,
        author0_.lastName as lastName3_0_0_,
        author0_.version as version4_0_0_,
        book2_.publisherid as publishe5_1_1_,
        book2_.publishingDate as publishi2_1_1_,
        book2_.title as title3_1_1_,
        book2_.version as version4_1_1_,
        books1_.authorId as authorId2_2_0__,
        books1_.bookId as bookId1_2_0__ 
    from
        Author author0_ 
    inner join
        BookAuthor books1_ 
            on author0_.id=books1_.authorId 
    inner join
        Book book2_ 
            on books1_.bookId=book2_.id
08:36:55,182 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 1 wrote 2 books.
08:36:55,183 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 2 wrote 3 books.
08:36:55,183 INFO  [org.thoughts.on.java.model.TestJoinFetch] - Author 3 wrote 1 books.

Få flere oplysninger:

Hvis du vil lære mere om dovne associationer eller forespørgselstip, kan du nyde at læse følgende artikler:

  • Enhedstilknytninger:Introduktion til JPA FetchTypes
  • 5 måder at initialisere dovne associationer på, og hvornår de skal bruges
  • 11 JPA- og Hibernate-forespørgselstips, som enhver udvikler bør vide

Dvaletipsbog







Få flere opskrifter som denne i min nye bog Hibernate Tips:Mere end 70 løsninger på almindelige dvaleproblemer.

Den giver dig mere end 70 klar-til-brug opskrifter til emner som grundlæggende og avancerede kortlægninger, logning, Java 8-understøttelse, caching og statisk og dynamisk definerede forespørgsler.

Få det nu!



Java tag