Java >> Java tutorial >  >> Tag >> class

Hibernate Tips:Sådan tilpasses et konstruktørudtryk til forskellige underklasser

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:

I sidste uge stillede en af ​​mine coachingkunder et interessant spørgsmål:

Han brugte InheritanceType.SINGLE_TABLE til at kortlægge et arvehierarki til én databasetabel. I en af ​​sine forespørgsler ønskede han at returnere den samme DTO-projektion for forskellige underenheder i hierarkiet. Afhængigt af subentity-klassen ønskede han at initialisere forskellige egenskaber for DTO-objektet.

Spørgsmålet var:Hvordan implementerer man en forespørgsel, der tilpasser konstruktørkaldet til forskellige underenheder?

Løsning:

For at være ærlig var definitionen af ​​forespørgslen mere kompleks, end jeg havde forventet. Jeg tænkte først, at jeg bare skulle implementere 2 forskellige konstruktører og derefter bruge et CASE-udtryk med JPQL's TYPE-funktion til at skelne mellem de 2 underklasser.

Men desværre forårsagede kombinationen af ​​CASE-udtryk, TYPE-funktion og konstruktørudtryk en mærkelig fejlmeddelelse med Hibernate 5.4.

Til sidst var jeg nødt til at implementere én konstruktør med parametre for alle egenskaber i DTO'en og bruge et CASE-udtryk for hver egenskab, der ikke var kortlagt af superklassen.

Lad os tage et kig på et forenklet eksempel, da jeg åbenbart ikke kan dele nogen klients kode.

Enheder og DTO

Jeg oprettede et lille arvehierarki bestående af en Publikation som superklassen og underklasserne Bog og BlogPost .

Og jeg vil bruge PublicationPresentationValue klasse som projektionen af ​​min forespørgsel. Som du kan se i diagrammet, forventer konstruktøren 4 parametre med værdierne for id , titel , antalSider og url ejendomme. Id og titel er kortlagt af Publikationen enhed og vil blive indstillet for alle PublicationPresentationValue genstande. antalPages egenskaben er specifik for Bog enheder og vil være nul for alle BlogPost s. url egenskab vil kun blive indstillet for publikationer af typen BlogPost .

Subentitetsspecifikke konstruktørkald i JPQL

Som jeg forklarede i begyndelsen af ​​dette Hibernate Tip, fungerede kombinationen af ​​CASE-udtryk, TYPE-funktion og konstruktørudtryk ikke med Hibernate 5.4. Jeg måtte i stedet bruge en konstruktør med parametre for alle egenskaber af DTO'en. Du kan se forespørgslen her.

TypedQuery<PublicationPresentationValue> q = em.createQuery(
	"SELECT new org.thoughts.on.java.model.PublicationPresentationValue(p.id, p.title, "
		+ "CASE TYPE(p) WHEN Book THEN TREAT(p as Book).numPages ELSE NULL END , "
		+ "CASE TYPE(p) WHEN BlogPost THEN TREAT(p as BlogPost).url ELSE NULL END )"
	+ "FROM Author a JOIN a.publications p WHERE a.id = :id", PublicationPresentationValue.class);
q.setParameter("id", 1L);
List<PublicationPresentationValue> values = q.getResultList();

Forespørgslen starter med et simpelt konstruktørudtryk, der fortæller Hibernate at instansiere en PublicationPresentationValue objekt for hver post. De interessante dele er de følgende linjer i forespørgslen.

Linje 2 starter med en CASE udtryk, som jeg bruger til at bestemme den 3. konstruktørparameter. Et CASE-udtryk ligner en if-sætning i Java. Den evaluerer en when_clause , som i denne forespørgsel er TYPE(p) . TYPE funktion returnerer typen af ​​den valgte enhed. I dette eksempel er det enten en bog eller et blogindlæg . Hvis det er en bog , kalder jeg BEHANDLING funktion til at caste p til en bog enhed og referer til numPages attribut. Blogindlæg enheder har ikke et antalPages attribut, og jeg returnerer null i stedet for.

Linje 3 ligner meget den forrige. Denne gang vil jeg enten returnere url attribut for et BlogPost enhed eller null, hvis det er en bog enhed. Så jeg bruger igen TYPE funktion for at få klassen for den aktuelle enhed og BEHANDLING funktion til at caste det til et BlogPost enhed.

Som du kan se, er JPQL alligevel ikke så kraftfuld som SQL, den giver dig stadig mulighed for at oprette ret komplekse forespørgsler. Hvis du kører denne forespørgsel og aktiverer logningen af ​​SQL-sætningen, kan du se, at Hibernate genererer følgende SQL-sætning.

18:55:20,810 DEBUG [org.hibernate.SQL] - 
    select
        publicatio2_.id as col_0_0_,
        publicatio2_.title as col_1_0_,
        case publicatio2_.DTYPE 
            when 'Book' then publicatio2_.numPages 
            else null 
        end as col_2_0_,
        case publicatio2_.DTYPE 
            when 'BlogPost' then publicatio2_.url 
            else null 
        end as col_3_0_ 
    from
        Author author0_ 
    inner join
        PublicationAuthor publicatio1_ 
            on author0_.id=publicatio1_.authorId 
    inner join
        Publication publicatio2_ 
            on publicatio1_.publicationId=publicatio2_.id 
            and publicatio2_.DTYPE in (
                'Book',
            'BlogPost') 
        where
            author0_.id=?

Få flere oplysninger:

Hvis du kunne lide denne artikel, er du muligvis også interesseret i:

  • Den ultimative guide til JPQL-forespørgsler med JPA og Hibernate
  • Tip til dvale:Sådan downcaster du enheder i JPQL-forespørgsler
  • Hvorfor, hvornår og hvordan man bruger DTO-projektioner med JPA og Hibernate
  • Enheder eller DTO'er – Hvornår skal du bruge hvilken projektion?

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