Java >> Java tutorial >  >> Tag >> native

Hibernates @NotFound Annotation – Sådan bruger du den og et bedre alternativ

Nogle tabelmodeller håndhæver ikke deres fremmednøglereferencer af en fremmednøglebegrænsning. Dette er en dårlig praksis, der ofte fører til udenlandske nøglereferencer, der peger på ikke-eksisterende poster. Når Hibernate forsøger at løse en sådan ødelagt reference, kaster den en EntityNotFoundException . Du bør derfor definere en fremmednøglebegrænsning for hver fremmednøglereference.

Men efter at nogen har besluttet sig for at bruge begrænsninger for fremmednøgle og implementeret applikationen til produktion, er den beslutning ofte svær at omgøre. Og det sætter dig i den position, hvor du skal opbygge et persistenslag, der kan håndtere tilknytninger, der refererer til en ikke-eksisterende post.

Som standard kaster Hibernate en undtagelse, hvis den forsøger at løse en ødelagt fremmednøglereference. Den bedste måde at løse dette på er selvfølgelig at rydde op i din database og rette dine fremmednøglereferencer. Men hvis det ikke er en mulighed, skal du beslutte, om du:

  • ønsker at håndtere en EntityNotFoundException hver gang du kalder getter-metoden for en potentielt brudt tilknytning eller
  • brug Hibernates @NotFound annotation for at fortælle Hibernate at hente en potentielt brudt tilknytning og ignorere den eller kaste en FetchNotFoundException når du instansierer dit enhedsobjekt.

Hibernate's @NotFound anmærkning

Annotering af en tilknytning til Hibernates proprietære @NotFound annotation har 3 effekter:

  1. Dvaletilstand antager, at tabelmodellen ikke definerer en fremmednøglebegrænsning for den tilknytning og genererer ikke en, hvis den genererer tabelmodellen.
  2. Du definerer, om Hibernate skal ignorere ødelagte fremmednøglereferencer eller give en undtagelse.
  3. Dvaletilstand henter tilknytningen ivrigt, selvom du indstiller dens FetchType til LAZY .

Jeg vil komme ind på flere detaljer om Hibernates tvungne ivrige hentning i næste afsnit. Lad os først se nærmere på @NotFound annotering og de 2 understøttede NotFoundAction s.

NotFoundAction.EXCEPTION

Du kan definere NotFoundAction.EXCEPTION ved at annotere den attribut, der kortlægger din tilknytning til @NotFound og indstille handlingen attribut til EXCEPTION eller holde den tom. Dette fortæller Hibernate at kaste en FetchNotFoundException hvis det ikke kan løse fremmednøglereferencen.

@Entity
public class ChessGame {
   
    @ManyToOne(fetch = FetchType.LAZY)
    @NotFound(action = NotFoundAction.EXCEPTION)
    private ChessPlayer playerBlack;
	
	...
}

Denne adfærd kan virke meget lig den, du får uden at kommentere din tilknytning til @NotFound . Men der er 2 forskelle:

  • Dvaletilstand kaster en FetchNotFoundException i stedet for en EntityNotFoundException .
  • Dvale ignorerer den konfigurerede FetchType og forsøger ivrigt at hente foreningen for at validere den fremmede nøglereference. På grund af det kaster Hibernate FetchNotFoundException når den instansierer enhedsobjektet og ikke når du bruger tilknytningen for første gang. Dette gør FetchNotFoundException lidt nemmere at håndtere.

Du kan se alt dette i log-outputtet, når jeg bruger kortlægningen i en testcase, der henter et Skakspil enhed med en brudt udenlandsk nøglereference.

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

ChessGame game = em.find(ChessGame.class, 10L);
log.info(game.getPlayerWhite() + " - " + game.getPlayerBlack());

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

Hibernate slutter sig til og vælger playerBlack tilknytning i forespørgslen, der henter Skakspillet enhed og kaster en FetchNotFoundException .

17:04:20,702 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.chessTournament_id,c1_0.date,c1_0.playerBlack_id,p1_0.id,p1_0.birthDate,p1_0.firstName,p1_0.lastName,p1_0.version,c1_0.playerWhite_id,c1_0.round,c1_0.version from ChessGame c1_0 left join ChessPlayer p1_0 on p1_0.id=c1_0.playerBlack_id where c1_0.id=?
17:04:20,712 ERROR [com.thorben.janssen.sample.TestSample] - org.hibernate.FetchNotFoundException: Entity `com.thorben.janssen.sample.model.ChessPlayer` with identifier value `100` does not exist

NotFoundAction.IGNORE

Indstilling af NotFoundAction at IGNORER giver dig mulighed for at håndtere den ødelagte fremmednøglereference i din forretningskode. I stedet for at smide en undtagelse, hvis den ikke kan løse den fremmede nøglereference, indstiller Hibernate tilknytningsattributten til null . På grund af det kan du ikke længere skelne, om en tilknytning ikke blev sat, eller om den refererer til en post, der ikke længere eksisterer. Du skal tage stilling til din ansøgning, hvis du vil behandle disse 2 sager forskelligt. Hvis det er tilfældet, kan du ikke bruge NotFoundAction.IGNORE .

Ligesom i det foregående eksempel skal du annotere den attribut, der kortlægger tilknytningen til Hibernates @NotFound anmærkning. Men denne gang skal du også indstille handlingen til NotFoundAction.IGNORE .

@Entity
public class ChessGame {
   
    @ManyToOne(fetch = FetchType.LAZY)
    @NotFound(action = NotFoundAction.IGNORE)
    private ChessPlayer playerBlack;
	
	...
}

Når du derefter udfører den samme testcase som i det foregående afsnit, kaster Hibernate ikke længere en undtagelse og initialiserer playerBlack  attribut med null i stedet.

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

ChessGame game = em.find(ChessGame.class, 10L);
log.info(game.getPlayerWhite() + " - " + game.getPlayerBlack());

em.getTransaction().commit();
em.close();
17:23:24,203 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.chessTournament_id,c1_0.date,c1_0.playerBlack_id,p1_0.id,p1_0.birthDate,p1_0.firstName,p1_0.lastName,p1_0.version,c1_0.playerWhite_id,c1_0.round,c1_0.version from ChessGame c1_0 left join ChessPlayer p1_0 on p1_0.id=c1_0.playerBlack_id where c1_0.id=?
17:23:24,223 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
17:23:24,237 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Fabiano, lastName=Caruana, birthDate=1992-07-30, version=0] - null

Ingen doven hentning med @NotFound

Jeg nævnte tidligere, at annotering af en tilknytning til @NotFound ændrer henteadfærden til FetchType.EAGER . Det er endda tilfældet, hvis du udtrykkeligt indstiller FetchType.LAZY i din foreningskortlægning, som jeg gjorde i de foregående eksempler.

@Entity
public class ChessGame {
   
    @ManyToOne(fetch = FetchType.LAZY)
    @NotFound(action = NotFoundAction.IGNORE)
    private ChessPlayer playerBlack;
	
	...
}

Grunden til det er enkel. Hibernate skal bruge FetchType.EAGER for at sikre, at den kun initialiserer tilknytningsattributten, hvis den refererer til et eksisterende enhedsobjekt.


Følg mig på YouTube for ikke at gå glip af nye videoer.

Hvis du ikke annoterer din tilknytningsattribut med @NotFound , Dvale forventer, at en fremmednøglebegrænsning validerer den fremmede nøglereference. Derfor skal den kun kontrollere, om en fremmednøglereference er indstillet. Hvis det er tilfældet, ved det, at det vil være i stand til at løse referencen og initialiserer entity-attributten med et proxy-objekt. Når du bruger denne proxy for første gang, vil Hibernate udføre en SQL-sætning for at løse den fremmede nøglereference.

Hvis du annoterer tilknytningsattributten med @NotFound , Hibernate kan ikke længere stole på den fremmede nøglereference. Uden en fremmednøgle-begrænsning kan referencen være brudt. Hibernate kan derfor ikke blot bruge fremmednøgleværdien til at instantiere et proxyobjekt. Den skal først kontrollere, om referencen er gyldig. Ellers ville det være nødvendigt at sætte associationsattributten til null.

Udførelse af denne ekstra forespørgsel kan skabe ydeevneproblemer. Men der er kun en minimal ydelsesforskel mellem at kontrollere den fremmede nøglereference og forsøge at hente den tilknyttede enhed. På grund af dette besluttede Hibernate-teamet at bruge ivrig hentning for alle associationer, der er kommenteret med @NotFound .

Et ofte bedre alternativ

Den tvungne ivrige hentning af Hibernates @NotFound kortlægning kan forårsage ydeevneproblemer. Selvom implementeringen kan være mere kompleks, er det ofte bedre ikke at annotere din tilknytning til @NotFound og håndtere den ødelagte fremmednøglereference i din forretningskode.

Hibernate instansierer derefter et proxyobjekt, hvis fremmednøglereferencen er indstillet, og forsøger at løse det, når proxyobjektet bruges for første gang.

17:35:52,212 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.chessTournament_id,c1_0.date,c1_0.playerBlack_id,c1_0.playerWhite_id,c1_0.round,c1_0.version from ChessGame c1_0 where c1_0.id=?
17:35:52,241 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
17:35:52,255 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
17:35:52,260 ERROR [com.thorben.janssen.sample.TestSample] - jakarta.persistence.EntityNotFoundException: Unable to find com.thorben.janssen.sample.model.ChessPlayer with id 100

Hvis den fremmede nøglereference er brudt, kaster Hibernate en EntityNotFoundException , som du skal håndtere i din forretningskode. Den åbenlyse ulempe ved denne tilgang er, at du skal håndtere denne undtagelse forskellige steder i din forretningskode.

Du skal beslutte, om du er villig til at gøre det for at få fordelene ved FetchType.LAZY eller, hvis du foretrækker brugervenligheden fra Hibernates @NotFound kortlægning.

Deaktiver fremmednøglebegrænsningen

Hvis du beslutter dig for at håndtere de ødelagte fremmednøglereferencer i din virksomhedskode og bruge Hibernate til at generere din tabelmodel, skal du bede Hibernate om ikke at generere fremmednøglebegrænsningen.

OBS:Du bør kun bruge dette, hvis du arbejder på et ældre program, der ikke bruger begrænsninger for fremmednøgle. Hvis du stadig har valget, bør du altid bruge en fremmednøglebegrænsning til at håndhæve dine fremmednøglereferencer!

Du kan deaktivere genereringen af ​​fremmednøglebegrænsninger ved at annotere din tilknytning med en @JoinColumn annotering og indstilling af foreignKey attribut til @ForeignKey(ConstraintMode.NO_CONSTRAINT) . Denne annotation påvirker kun Hibernates generering af tabelmodellen og har ingen effekt under kørsel.

@Entity
public class ChessGame {
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
    private ChessPlayer playerBlack;
	
	...
}

Konklusion

Din tabelmodel bør validere alle fremmednøglereferencer med en fremmednøglebegrænsning. Dette sikrer, at nye fremmednøglereferencer kun kan referere til eksisterende poster, og at du ikke kan fjerne en post, der stadig refereres til.

Desværre beslutter nogle arkitekter og udviklingsteams sig for at undgå udenlandske nøglebegrænsninger. Før eller siden indeholder disse databaser ødelagte fremmednøglereferencer, som du skal håndtere i din enhedstilknytning eller forretningskode.

Hvis du vil håndtere dem i dine enhedstilknytninger, kan du annotere en tilknytning med @NotFound . Det fortæller Hibernate ikke at forvente eller generere nogen fremmednøglebegrænsning. Hibernate henter derefter foreningen ivrigt for at kontrollere gyldigheden af ​​den fremmede nøglereference. Håndteringen af ​​en ødelagt reference afhænger af din NotFoundAction . Hibernate kan enten ignorere det og initialisere attributten med null  eller smid en EntityFetchException .

Hvis du foretrækker at håndtere de ødelagte fremmednøglereferencer i din virksomhedskode, kan du annotere din tilknytningsattribut med @JoinColumn og definer ConstraintMode.NO_CONSTRAINT . Hibernate genererer så ikke en fremmednøglebegrænsning, når tabelmodellen genereres. Under kørsel tjekker den ikke den fremmede nøglereference, før det genererede proxyobjekt forsøger at løse det.


No
Java tag