Revisjon med JPA, Hibernate og Spring Data JPA

1. Oversikt

I forbindelse med ORM betyr databaserevisjon sporing og logging av hendelser relatert til vedvarende enheter, eller bare enhetsversjonering. Inspirert av SQL-utløsere, er hendelsene innsetting, oppdatering og sletting av operasjoner på enheter. Fordelene med databaserevisjon er analoge med de som leveres av kildeversjonskontroll.

Vi vil demonstrere tre tilnærminger for å introdusere revisjon i en applikasjon. Først vil vi implementere det ved hjelp av standard JPA. Deretter vil vi se på to JPA-utvidelser som gir sin egen revisjonsfunksjonalitet: en levert av Hibernate, en annen av Spring Data.

Her er eksempler relaterte enheter, Bar og Foo, som vil bli brukt i dette eksemplet:

2. Revisjon med JPA

JPA inneholder ikke eksplisitt et revisjons-API, men funksjonaliteten kan oppnås ved å bruke enhets livssyklushendelser.

2.1. @PrePersist,@PreUpdate og @PreRemove

I JPA Enhet klasse, kan en metode spesifiseres som tilbakeringing som vil bli påkalt under en bestemt enhets livssyklushendelse. Ettersom vi er interessert i tilbakeringinger som utføres før de tilsvarende DML-operasjonene, er det @PrePersist, @PreUpdate og @PreRemove tilbakeringingskommentarer tilgjengelig for våre formål:

@Entity public class Bar {@PrePersist public void onPrePersist () {...} @PreUpdate public void onPreUpdate () {...} @PreRemove public void onPreRemove () {...}}

Interne tilbakekallingsmetoder skal alltid være ugyldige og ikke ta noen argumenter. De kan ha hvilket som helst navn og hvilket som helst tilgangsnivå, men bør ikke være statiske.

Vær oppmerksom på at @Versjon merknader i JPA er ikke strengt relatert til temaet vårt - det har å gjøre med optimistisk låsing mer enn med revisjonsdata.

2.2. Implementering av tilbakeringingsmetodene

Det er imidlertid en betydelig begrensning med denne tilnærmingen. Som nevnt i JPA 2-spesifikasjonen (JSR 317):

Generelt bør ikke livssyklusmetoden til en bærbar applikasjon påberope seg EntityManager eller Spørsmål operasjoner, få tilgang til andre enhetsforekomster eller endre relasjoner innenfor samme utholdenhetskontekst. En tilbakeringingsmetode for livssyklus kan endre tilstanden som ikke er relatert til den enheten den påberopes på.

I mangel av et revisjonsrammeverk, må vi vedlikeholde databaseskjemaet og domenemodellen manuelt. For vår enkle brukstilfelle, la oss legge til to nye egenskaper til enheten, ettersom vi bare kan administrere "enhetens ikke-forholdstilstand". An operasjon eiendommen vil lagre navnet på en utført operasjon og a tidsstempel eiendommen er for tidsstemplet for operasjonen:

@Entity public class Bar {// ... @Column (name = "operation") privat strengoperasjon; @Kolonne (navn = "tidsstempel") privat lang tidsstempel; // ... // standard settere og getters for de nye eiendommene // ... @PrePersist public void onPrePersist () {audit ("INSERT"); } @PreUpdate offentlig ugyldig onPreUpdate () {audit ("UPDATE"); } @PreRemove public void onPreRemove () {audit ("DELETE"); } privat ugyldig revisjon (strengoperasjon) {setOperation (operasjon); setTimestamp ((ny dato ()). getTime ()); }}

Hvis du trenger å legge til slik revisjon i flere klasser, kan du bruke @EntityListeners for å sentralisere koden. For eksempel:

@EntityListeners (AuditListener.class) @Entity public class Bar {...}
offentlig klasse AuditListener {@PrePersist @PreUpdate @PreRemove private void beforeAnyOperation (Object object) {...}}

3. Hibernate Envers

Med Dvalemodus kunne vi benytte oss av Avskjærere og EventListeners samt databaseutløsere for å utføre revisjon. Men ORM-rammeverket tilbyr Envers, en modul som implementerer revisjon og versjonering av vedvarende klasser.

3.1. Kom i gang med Envers

For å sette opp Envers, må du legge til dvalemodus JAR inn i klassestien din:

 org.hibernate hibernate-envers $ {hibernate.version} 

Så er det bare å legge til @Audited kommentar enten på en @Enhet (for å revidere hele enheten) eller spesifikt @Kolonnes (hvis du bare trenger å revidere spesifikke egenskaper):

@Entity @ Audited public class Bar {...}

Noter det Bar har et en-til-mange forhold til Foo. I dette tilfellet må vi enten revidere Foo også ved å legge til @AuditedFoo eller sett @NotAudited på forholdets eiendom i Bar:

@OneToMany (mappedBy = "bar") @NotAudited privat Sett fooSet;

3.2. Opprette revisjonsloggtabeller

Det er flere måter å lage revisjonstabeller på:

  • sett dvale.hbm2ddl.auto til skape, create-drop eller Oppdater, slik at Envers kan opprette dem automatisk
  • bruk org.hibernate.tool.EnversSchemaGenerator for å eksportere hele databaseskjemaet programmatisk
  • bruk en Ant-oppgave for å generere passende DDL-setninger
  • bruk et Maven-plugin for å generere et databaseskjema fra tilordningene dine (for eksempel Juplo) for å eksportere Envers-skjemaet (fungerer med dvalemodus 4 og høyere)

Vi vil gå den første ruten, siden den er den enkleste, men vær oppmerksom på at bruk dvale.hbm2ddl.auto er ikke trygt i produksjonen.

I vårt tilfelle, bar_AUD og foo_AUD (hvis du har satt Foo som @Audited også) bord bør genereres automatisk. Tilsynstabellene kopierer alle reviderte felt fra enhetens tabell med to felt, REVTYPE (verdiene er: "0" for å legge til, "1" for oppdatering, "2" for å fjerne en enhet) og REV.

Foruten disse, et ekstra bord navngitt REVINFO genereres som standard, den inneholder to viktige felt, REV og REVTSTMP og registrerer tidsstempelet for hver revisjon. Og som du kan gjette, bar_AUD.REV og foo_AUD.REV er faktisk utenlandske nøkler til REVINFO.REV.

3.3. Konfigurere Envers

Du kan konfigurere Envers-egenskaper som alle andre dvalemodus-eiendommer.

La oss for eksempel endre suffiks for revisjonstabellen (som standard er “_AUD") til "_AUDIT_LOG“. Slik angir du verdien til den tilsvarende egenskapen org.hibernate.envers.audit_table_suffix:

Egenskaper hibernateProperties = nye egenskaper (); hibernateProperties.setProperty ("org.hibernate.envers.audit_table_suffix", "_AUDIT_LOG"); sessionFactory.setHibernateProperties (hibernateProperties);

En fullstendig oversikt over tilgjengelige eiendommer finner du i Envers-dokumentasjonen.

3.4. Få tilgang til enhetshistorikk

Du kan spørre etter historiske data på en måte som ligner på spørring av data via API for Hibernate criteria. Du kan få tilgang til revisjonsloggen til en enhet ved hjelp av AuditReader grensesnitt, som kan fås med et åpent EntityManager eller Økt via AuditReaderFactory:

AuditReader reader = AuditReaderFactory.get (økt);

Envers gir AuditQueryCreator (returnert av AuditReader.createQuery ()) for å lage revisjonsspesifikke spørsmål. Følgende linje vil returnere alle Bar tilfeller endret ved revisjon 2 (hvor bar_AUDIT_LOG.REV = 2):

AuditQuery-spørring = reader.createQuery (). ForEntitiesAtRevision (Bar.class, 2)

Slik spør du etter BarSine revisjoner, dvs. det vil resultere i å få en liste over alle Bar tilfeller i alle deres stater som ble revidert:

AuditQuery-spørring = reader.createQuery () .forRevisionsOfEntity (Bar.class, true, true);

Hvis den andre parameteren er falsk, blir resultatet koblet til REVINFO tabellen, ellers returneres bare enhetsforekomster. Den siste parameteren angir om slettet skal returneres Bar tilfeller.

Deretter kan du spesifisere begrensninger ved hjelp av AuditEntity fabrikk klasse:

query.addOrder (AuditEntity.revisionNumber (). desc ());

4. Vårdata JPA

Spring Data JPA er et rammeverk som utvider JPA ved å legge til et ekstra abstraksjonslag på toppen av JPA-leverandøren. Dette laget muliggjør støtte for å opprette JPA-repositorier ved å utvide Spring JPA repository-grensesnitt.

For våre formål kan du utvide CrudRepository, grensesnittet for generiske CRUD-operasjoner. Så snart du har opprettet og injisert depotet til en annen komponent, vil Spring Data gi implementeringen automatisk, og du er klar til å legge til revisjonsfunksjonalitet.

4.1. Aktivere JPA-revisjon

For å starte, ønsker vi å aktivere revisjon via merkonfigurasjon. For å gjøre det, er det bare å legge til @EnableJpaAuditing på din @Konfigurasjon klasse:

@Configuration @EnableTransactionManagement @EnableJpaRepositories @EnableJpaAuditing public class PersistenceConfig {...}

4.2. Legger til Spring's Entity Callback Listener

Som vi allerede vet, gir JPA den @EntityListeners kommentar for å spesifisere tilbakeringingsklasser. Spring Data tilbyr sin egen JPA-enhet lytter klasse: RevisjonEntityListener. Så la oss spesifisere lytteren for Bar enhet:

@Entity @EntityListeners (AuditingEntityListener.class) offentlig klassebar {...}

Nå vil revisjonsinformasjon fanges opp av lytteren ved å vedvare og oppdatere Bar enhet.

4.3. Sporing opprettet og sist modifiserte datoer

Deretter vil vi legge til to nye egenskaper for lagring av de opprettet og sist endrede datoene til vår Bar enhet. Eiendommene er merket av @Opprettet dato og @LastModifiedDate merknader tilsvarende, og deres verdier blir satt automatisk:

@Entity @EntityListeners (AuditingEntityListener.class) offentlig klasselinje {// ... @Column (name = "created_date", nullable = false, updatable = false) @CreatedDate private long createdDate; @Column (name = "modified_date") @LastModifiedDate private long modifiedDate; // ...}

Vanligvis vil du flytte egenskapene til en basisklasse (kommentert av @MappedSuperClass) som vil bli utvidet av alle dine reviderte enheter. I vårt eksempel legger vi dem direkte til Bar for enkelhets skyld.

4.4. Revisjon av forfatteren av endringer med vårsikkerhet

Hvis appen din bruker Spring Security, kan du ikke bare spore når endringene ble gjort, men også hvem som gjorde dem:

@Entity @EntityListeners (AuditingEntityListener.class) public class Bar {// ... @Column (name = "created_by") @CreatedBy private String createdBy; @Column (name = "modified_by") @LastModifiedBy private String modifiedBy; // ...}

Kolonnene kommentert med @Laget av og @LastModifiedBy er fylt med navnet på rektor som opprettet eller sist endret enheten. Informasjonen hentes fra SecurityContext‘S Godkjenning forekomst. Hvis du vil tilpasse verdier som er satt til merkede felt, kan du implementere det AuditorAware grensesnitt:

offentlig klasse AuditorAwareImpl implementerer AuditorAware {@Override public String getCurrentAuditor () {// din tilpassede logikk}}

For å konfigurere appen som skal brukes AuditorAwareImpl å slå opp nåværende rektor, erklære en bønne av AuditorAware type initialisert med en forekomst av AuditorAwareImpl og spesifiser bønnens navn som revisorAwareRef parameterens verdi i @EnableJpaAuditing:

@EnableJpaAuditing (auditorAwareRef = "auditorProvider") offentlig klasse PersistenceConfig {// ... @Bean AuditorAware auditorProvider () {returner ny AuditorAwareImpl (); } // ...}

5. Konklusjon

Vi har vurdert tre tilnærminger for å implementere revisjonsfunksjonalitet:

  • Den rene JPA-tilnærmingen er den mest grunnleggende og består i å bruke tilbakeringing av livssyklus. Du har imidlertid bare lov til å endre en enhets ikke-forholdstilstand. Dette gjør at @PreRemove tilbakeringing ubrukelig for våre formål, ettersom alle innstillinger du har gjort i metoden vil bli slettet sammen med enheten.
  • Envers er en moden revisjonsmodul levert av Hibernate. Den er svært konfigurerbar og mangler feilene ved den rene JPA-implementeringen. Dermed tillater det oss å revidere sletteoperasjonen, da den logger på andre tabeller enn enhetens tabell.
  • Spring Data JPA-tilnærmingen tar utgangspunkt i å jobbe med JPA-tilbakeringinger og gir praktiske merknader for revisjonsegenskaper. Den er også klar for integrering med Spring Security. Ulempen er at den arver de samme feilene ved JPA-tilnærmingen, slik at sletteoperasjonen ikke kan revideres.

Eksemplene for denne artikkelen er tilgjengelige i et GitHub-arkiv.


$config[zx-auto] not found$config[zx-overlay] not found