Java >> Java opplæring >  >> Tag >> hibernate

@NaturalId – En god måte å opprettholde naturlige IDer med Hibernate?

I den virkelige verden har de fleste objekter en naturlig identifikator. Typiske eksempler er ISBN-nummeret til en bok, et selskaps skatteidentifikator eller en persons personnummer. Du kan selvfølgelig bruke disse identifikatorene som primærnøkler. Men oftest er det en bedre idé å generere numeriske surrogatnøkler. De er enklere å administrere, og de fleste rammeverk kan håndtere dem mer effektivt enn mer komplekse naturlige identifikatorer.

En naturlig identifikator identifiserer likevel en databasepost og et objekt i den virkelige verden. Mange brukssaker bruker dem i stedet for en kunstig surrogatnøkkel. Det er derfor god praksis å modellere dem som unike nøkler i databasen. Hibernate lar deg også modellere dem som en naturlig identifikator for en enhet og gir en ekstra API for å hente dem fra databasen.

Definer et attributt som en naturlig id

Det eneste du trenger å gjøre for å modellere et attributt er en naturlig id, er å legge til @NaturalId merknad. Du kan se et eksempel i følgende kodebit. isbn nummeret til en bok er en typisk naturlig id. Den identifiserer posten, men er mer kompleks enn primærnøkkelen id . ID attributt er en surrogatnøkkel og blir generert av Hibernate.

@Entity
public class Book {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = “id”, updatable = false, nullable = false)
  private Long id;

  @NaturalId
  private String isbn;

  …
}

Naturlige IDer er uforanderlige som standard, og du bør ikke angi settermetoder for dem. Hvis du trenger mutbar, naturlig identifikator, må du angi mutable attributtet til @NaturalId kommentar til true .

Få en enhet med dens naturlige ID

Hibernates Session-grensesnitt gir metodene byNaturalId og bySimpleNaturalId å lese en enhet ved dens naturlige identifikator fra databasen. La oss ta en titt på byNaturalId metode først.

Følgende kodebit viser hvordan du kan bruke denne metoden for å få en enhet etter dens naturlige ID. Du må oppgi klassen eller navnet på enheten som en parameter til byNaturalId metode.

Anropet til brukeren metoden gir navnet på det naturlige ID-attributtet og verdien. Hvis den naturlige IDen består av flere attributter, må du kalle denne metoden flere ganger for å definere hver del av IDen. I dette eksemplet bruker jeg JPA-metamodellen for å få navnet på isbn attributt.

Etter at du har oppgitt verdien av den naturlige ID-en, kan du ringe load metode for å få enheten identifisert av den. Hibernate tilbyr også andre alternativer for å få enheten som jeg viser deg i den følgende delen.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);

Book b = session.byNaturalId(Book.class).using(Book_.isbn.getName(), “978-0321356680”).load();

En ting som overrasket meg da jeg brukte denne API-en for første gang, var antallet søk Hibernate utfører. Jeg forventet at Hibernate oppretter 1 SQL-setning for å lese enheten. Men det er ikke tilfelle. Hibernate utfører 2 spørringer, som du kan se i loggmeldinger nedenfor. Den første spørringen velger den primære for den gitte naturlige ID-en, og den andre bruker den for å hente enheten.

Årsaken til denne tilnærmingen er mest sannsynlig at Hibernate trenger primærnøkkelverdien internt for å sjekke cachen på 1. og 2. nivå. I de fleste tilfeller bør ikke denne tilleggsforespørselen ha stor innvirkning på ytelsen. Hibernate bufrer også den naturlige ID-en til primærnøkkelmapping for økten og kan lagre den i cachen på 2. nivå slik at det ikke er nødvendig å hente den igjen.

06:14:40,705 DEBUG SQL:92 – select book_.id as id1_0_ from Book book_ where book_.isbn=?
06:14:40,715 DEBUG SQL:92 – select book0_.id as id1_0_0_, book0_.isbn as isbn2_0_0_, book0_.publishingDate as publishi3_0_0_, book0_.title as title4_0_0_, book0_.version as version5_0_0_ from Book book0_ where book0_.id=?

bySimpleNaturalId metoden gir et praktisk alternativ for å velge enheter med enkle naturlige IDer som består av bare ett attributt. Som du kan se i følgende kodebit, kan du oppgi den naturlige ID-verdien direkte til last metoden og trenger ikke å kalle bruker metode.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);

Book b = session.bySimpleNaturalId(Book.class).load(“978-0321356680”);

3 alternativer for å hente enheten

Som jeg forklarte tidligere, tilbyr Hibernate 3 forskjellige alternativer for å hente en enhet med dens naturlige ID fra databasen:

load() Får en referanse til den initialiserte enheten.
loadOptional() Får en referanse til den initialiserte enheten eller null og pakker den inn i en Valgfri .
Jeg forklarte Hibernates valgfrie støtte mer detaljert i Hvordan bruke Java 8s valgfrie med Hibernate.
getReference() Får en referanse til enheten eller en uinitialisert proxy.

Låser

Grensesnittene NaturalIdLoadAccess og SimpleNaturalIdLoadAccess gi med(LockOptions-lås) metode. Du kjenner det sannsynligvis fra IdentifierLoadAccess grensesnitt som blir returnert av Session.byId(Class-entity) metode. Du kan bruke denne metoden til å definere hvilken låsemodus Hibernate skal bruke for spørringen.

I den følgende kodebiten bruker jeg denne metoden til å sette en skrivelås på den valgte enheten.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);

Book b = session.bySimpleNaturalId(Book.class).with(LockOptions.UPGRADE).load(“978-0321356680”);

Du kan se i den loggede SQL-setningen at Hibernate la til "for oppdatering" i spørringen. Dette nøkkelordet utløser skrivelåsen i PostgreSQL-databasen jeg bruker for dette eksemplet.

06:19:34,055 DEBUG SQL:92 – select book_.id as id1_0_ from Book book_ where book_.isbn=?
06:19:34,128 DEBUG SQL:92 – select book0_.id as id1_0_0_, book0_.isbn as isbn2_0_0_, book0_.publishingDate as publishi3_0_0_, book0_.title as title4_0_0_, book0_.version as version5_0_0_ from Book book0_ where book0_.id=? for update

Caching

Som jeg forklarte i begynnelsen, bufrer Hibernate den naturlige ID-en til primærnøkkelmapping for hver økt. Du kan se et eksempel på det i følgende kodebit og de tilhørende loggmeldingene.

Jeg laster først inn boken enhet med id 1 fra databasen og skriv en loggmelding. I neste trinn laster jeg inn den samme enheten med dens naturlige identifikator.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);

session.byId(Book.class).load(1L);
log.info(“Get book by id”);

Book b = session.bySimpleNaturalId(Book.class).load(“978-0321356680”);

Som du kan se i loggmeldingene, utfører Hibernate en select-setning for å få boken enhet med id 1. Men den kjører ikke en annen setning for å få den med sin naturlige ID. Hibernate la til primærnøkkelen til naturlig ID-tilordning til økten da jeg lastet inn enheten med id . Når jeg deretter laster inn enheten med dens naturlige ID, får Hibernate primærnøkkeltilordningen og enheten fra cachen på 1. nivå.

06:20:39,767 DEBUG SQL:92 – select book0_.id as id1_0_0_, book0_.isbn as isbn2_0_0_, book0_.publishingDate as publishi3_0_0_, book0_.title as title4_0_0_, book0_.version as version5_0_0_ from Book book0_ where book0_.id=?
06:20:39,785 INFO TestHibernateNaturalId:78 – Read book by id
06:20:39,788 INFO TestHibernateNaturalId:81 – Book title: Effective Java

Konklusjon

Å velge enheter etter dens naturlige identifikator er et vanlig brukstilfelle. Hibernates proprietære API gir en enkel og komfortabel måte å gjøre det på. Den ekstra select-erklæringen for å få primærnøkkelen for den oppgitte naturlige ID-en kommer som en overraskelse i begynnelsen. Men dette bør ikke være et ytelsesproblem, hvis du tenker på at du vanligvis legger til en databaseindeks i den naturlige identifikatorkolonnen. Så snart Hibernate kjenner til kartleggingen mellom den naturlige IDen og primærnøkkelen, kan den bruke de kjente optimaliserings- og hurtigbuffermekanismene.


Java Tag