Auditing med JPA, Hibernate og Spring Data JPA

1. Oversigt

I forbindelse med ORM betyder databaseavisering sporing og logning af begivenheder relateret til vedvarende enheder eller simpelthen enhedsversionering. Inspireret af SQL-udløsere indsættes begivenhederne, opdateres og slettes på enheder. Fordelene ved databaseovervågning er analoge med dem, der leveres med kildeversionskontrol.

Vi vil demonstrere tre tilgange til introduktion af revision i en applikation. Først implementerer vi det ved hjælp af standard JPA. Dernæst vil vi se på to JPA-udvidelser, der giver deres egen revisionsfunktionalitet: en leveret af Hibernate, en anden af ​​Spring Data.

Her er eksempler relaterede enheder, Bar og Foo, der vil blive brugt i dette eksempel:

2. Revision med JPA

JPA indeholder ikke eksplicit en auditerings-API, men funktionaliteten kan opnås ved hjælp af enhedens livscyklushændelser.

2.1. @PrePersist,@PreUpdate og @PreRemove

I JPA Enhed klasse, kan en metode specificeres som en tilbagekaldelse, der påberåbes under en bestemt enhedens livscyklushændelse. Da vi er interesseret i tilbagekald, der udføres før de tilsvarende DML-operationer, er der @PrePersist, @PreUpdate og @PreRemove tilbagekaldsnotater tilgængelige til vores formål:

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

Interne tilbagekaldsmetoder skal altid være ugyldige og tage ingen argumenter. De kan have ethvert navn og ethvert adgangsniveau, men bør ikke være statiske.

Vær opmærksom på, at @Version annotering i JPA er ikke strengt relateret til vores emne - det har at gøre med optimistisk låsning mere end med revisionsdata.

2.2. Implementering af tilbagekaldsmetoder

Der er dog en betydelig begrænsning med denne tilgang. Som anført i JPA 2-specifikationen (JSR 317):

Generelt bør livscyklusmetoden for en bærbar applikation ikke påberåbes EntityManager eller Forespørgsel operationer, få adgang til andre enhedsforekomster eller ændre forhold inden for den samme persistens-kontekst. En tilbagekaldningsmetode til livscyklus kan ændre tilstanden for ikke-forholdet for den enhed, som den påberåbes.

I mangel af en revisionsramme skal vi vedligeholde databaseskemaet og domænemodellen manuelt. For vores enkle brugssag, lad os tilføje to nye egenskaber til enheden, da vi kun kan styre "enhedens ikke-forholdstilstand". En operation ejendom gemmer navnet på den udførte handling og a tidsstempel ejendom er til tidsstemplet for operationen:

@Entity public class Bar {// ... @Column (name = "operation") privat strenghandling; @Kolonne (navn = "tidsstempel") privat lang tidsstempel; // ... // standard settere og getters for de nye egenskaber // ... @PrePersist public ugyldig onPrePersist () {audit ("INSERT"); } @PreUpdate offentlig ugyldighed onPreUpdate () {audit ("UPDATE"); } @PreRemove offentligt ugyldigt onPreRemove () {audit ("SLET"); } privat ugyldig revision (strengoperation) {setOperation (operation); setTimestamp ((ny dato ()). getTime ()); }}

Hvis du har brug for at tilføje sådan revision til flere klasser, kan du bruge @EntityListeners at centralisere 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 dvale kunne vi gøre brug af Opfangere og EventListeners såvel som databaseudløsere for at udføre revision. Men ORM-rammen tilbyder Envers, et modul, der implementerer revision og versionering af vedvarende klasser.

3.1. Kom godt i gang med Envers

For at konfigurere Envers skal du tilføje dvale-envers JAR ind på din klassesti:

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

Derefter skal du blot tilføje @Audited kommentar enten på en @Enhed (til at revidere hele virksomheden) eller specifikt @Kolonnes (hvis du kun skal kontrollere specifikke egenskaber):

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

Noter det Bar har et en-til-mange forhold til Foo. I dette tilfælde skal vi enten revidere Foo samt ved at tilføje @AuditedFoo eller indstil @NotAudited på forholdets ejendom i Bar:

@OneToMany (mappedBy = "bar") @NotAudited privat sæt fooSet;

3.2. Oprettelse af revisionslogborde

Der er flere måder at oprette revisionstabeller på:

  • sæt dvale.hbm2ddl.auto til skab, Opret-slip eller opdatering, så Envers kan oprette dem automatisk
  • brug org.hibernate.tool.EnversSchemaGenerator for at eksportere det komplette databaseskema programmatisk
  • Brug en Ant-opgave til at generere passende DDL-udsagn
  • brug et Maven-plugin til at generere et databaseskema fra dine tilknytninger (f.eks. Juplo) for at eksportere Envers-skemaet (fungerer med Hibernate 4 og højere)

Vi går den første rute, da den er den mest ligetil, men vær opmærksom på, at brug dvale.hbm2ddl.auto er ikke sikker i produktionen.

I vores tilfælde bar_AUD og foo_AUD (hvis du har indstillet Foo som @Audited også) tabeller skal genereres automatisk. Revisionstabellerne kopierer alle reviderede felter fra enhedens tabel med to felter, REVTYPE (værdier er: "0" for tilføjelse, "1" for opdatering, "2" for fjernelse af en enhed) og REV.

Udover disse, et ekstra bord navngivet REVINFO genereres som standard, den indeholder to vigtige felter, REV og REVTSTMP og registrerer tidsstemplet for hver revision. Og som du kan gætte, bar_AUD.REV og foo_AUD.REV er faktisk udenlandske nøgler til REVINFO.REV.

3.3. Konfiguration af Envers

Du kan konfigurere Envers-egenskaber ligesom enhver anden dvale-ejendom.

Lad os f.eks. Ændre suffikset til revisionstabellen (som standard er "_AUD") til "_AUDIT_LOG“. Sådan indstilles værdien for den tilsvarende ejendom org.hibernate.envers.audit_table_suffix:

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

En fuld liste over tilgængelige ejendomme kan findes i Envers-dokumentationen.

3.4. Adgang til enhedshistorik

Du kan forespørge efter historiske data på en måde, der ligner forespørgsel om data via API'et Hibernate criteria. En enheds revisionshistorik kan tilgås ved hjælp af AuditLæser interface, som kan opnås med en åben EntityManager eller Session via AuditReaderFactory:

AuditReader-læser = AuditReaderFactory.get (session);

Envers giver AuditQueryCreator (returneret af AuditReader.createQuery ()) for at oprette revisionsspecifikke forespørgsler. Den følgende linje returnerer alle Bar forekomster ændret ved revision nr. 2 (hvor bar_AUDIT_LOG.REV = 2):

AuditQuery-forespørgsel = reader.createQuery (). ForEntitiesAtRevision (Bar.class, 2)

Her er, hvordan man forespørger om Bar'S revisioner, dvs. det vil resultere i at få en liste over alle Bar tilfælde i alle deres stater, der blev revideret:

AuditQuery-forespørgsel = reader.createQuery (). ForRevisionsOfEntity (Bar.class, true, true);

Hvis den anden parameter er falsk, forbindes resultatet med REVINFO ellers returneres kun enhedsforekomster. Den sidste parameter angiver, om slettet skal returneres Bar tilfælde.

Derefter kan du specificere begrænsninger ved hjælp af AuditEntity fabriksklasse:

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

4. Spring Data JPA

Spring Data JPA er en ramme, der udvider JPA ved at tilføje et ekstra abstraktionslag øverst på JPA-udbyderen. Dette lag giver mulighed for understøttelse af oprettelse af JPA-repositorier ved at udvide Spring JPA repository-grænseflader.

Til vores formål kan du udvide CrudRepository, grænsefladen til generiske CRUD-operationer. Så snart du har oprettet og indsprøjtet dit lager til en anden komponent, leverer Spring Data implementeringen automatisk, og du er klar til at tilføje revisionsfunktionalitet.

4.1. Aktivering af JPA-revision

For at starte, ønsker vi at aktivere revision via annoteringskonfiguration. For at gøre det skal du bare tilføje @EnableJpaAuditing på din @Konfiguration klasse:

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

4.2. Tilføjelse af Spring's Entity Callback Listener

Som vi allerede ved, leverer JPA den @EntityListeners kommentar for at specificere tilbagekaldelseslytteklasser. Spring Data giver sin egen JPA-enhed lytterklasse: AuditingEntityListener. Så lad os specificere lytteren til Bar enhed:

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

Nu bliver revisionsoplysninger fanget af lytteren ved vedvarende og opdatering af Bar enhed.

4.3. Sporing oprettet og sidst ændrede datoer

Dernæst vil vi tilføje to nye egenskaber til lagring af de oprettede og sidst ændrede datoer til vores Bar enhed. Ejendommene er kommenteret af @CreatedDate og @LastModifiedDate annoteringer i overensstemmelse hermed, og deres værdier indstilles 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; // ...}

Generelt vil du flytte egenskaberne til en basisklasse (kommenteret af @MappedSuperClass) som vil blive udvidet af alle dine reviderede enheder. I vores eksempel føjer vi dem direkte til Bar af enkelheds skyld.

4.4. Revision af forfatteren til ændringer med forårssikkerhed

Hvis din app bruger Spring Security, kan du ikke kun spore, hvornår der blev foretaget ændringer, men også hvem der lavede dem:

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

Kolonnerne kommenteret med @Lavet af og @LastModifiedBy er udfyldt med navnet på den hovedstol, der oprettede eller sidst ændrede enheden. Oplysningerne hentes fra Sikkerhedskontekst'S Godkendelse eksempel. Hvis du vil tilpasse værdier, der er indstillet til de kommenterede felter, kan du implementere AuditorAware grænseflade:

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

For at konfigurere appen til at bruge AuditorAwareImpl at slå den nuværende rektor op, erklære en bønne af AuditorAware type initialiseret med en forekomst af AuditorAwareImpl og angiv bønnens navn som auditorAwareRef parameterens værdi i @EnableJpaAuditing:

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

5. Konklusion

Vi har overvejet tre tilgange til implementering af revisionsfunktionalitet:

  • Den rene JPA-tilgang er den mest grundlæggende og består i at bruge tilbagekald fra livscyklus. Du har dog kun tilladelse til at ændre en enheds ikke-forholdstilstand. Dette gør @PreRemove tilbagekald ubrugelig til vores formål, da alle indstillinger, du har foretaget i metoden, slettes sammen med enheden.
  • Envers er et modent revisionsmodul leveret af Hibernate. Det er meget konfigurerbart og mangler manglerne ved den rene JPA-implementering. Således giver det os mulighed for at kontrollere sletteoperationen, da den logger på andre tabeller end enhedens tabel.
  • Spring Data JPA-tilgang abstrakterer arbejde med JPA-tilbagekald og giver praktiske kommentarer til revisionsejendomme. Det er også klar til integration med Spring Security. Ulempen er, at den arver de samme fejl ved JPA-tilgangen, så sletningen kan ikke revideres.

Eksemplerne til denne artikel er tilgængelige i et GitHub-arkiv.


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