Používanie JaVers na auditovanie dátových modelov v jarných dátach

1. Prehľad

V tomto tutoriáli sa dozvieme, ako nastaviť a používať JaVers v jednoduchej aplikácii Spring Boot na sledovanie zmien entít.

2. JaVers

Pri narábaní s premenlivými údajmi máme zvyčajne v databáze uložený iba posledný stav entity. Ako vývojári trávime veľa času ladením aplikácie a hľadaním udalostí, ktoré zmenili stav, v súboroch denníka. V produkčnom prostredí je to ešte zložitejšie, keď systém používa veľa rôznych používateľov.

Našťastie máme vynikajúce nástroje ako JaVers. JaVers je rámec protokolu auditu, ktorý pomáha sledovať zmeny entít v aplikácii.

Použitie tohto nástroja sa neobmedzuje iba na ladenie a audit. Môže byť úspešne použitý na vykonávanie analýz, vynútenie bezpečnostných politík a údržbu protokolu udalostí.

3. Nastavenie projektu

Najskôr, aby sme mohli začať používať JaVers, musíme nakonfigurovať úložisko auditu pre pretrvávajúce snímky entít. Po druhé, musíme upraviť niektoré konfigurovateľné vlastnosti JaVers. Nakoniec sa budeme venovať aj tomu, ako správne nakonfigurovať naše modely domén.

Ale stojí za zmienku, že JaVers poskytuje predvolené možnosti konfigurácie, takže ho môžeme začať používať takmer bez konfigurácie.

3.1. Závislosti

Najskôr do nášho projektu musíme pridať závislosť štartéra JaVers Spring Boot. V závislosti od typu trvalého úložiska máme dve možnosti: org.javers: javers-spring-boot-starter-sql a org.javers: javers-spring-boot-starter-mongo. V tomto tutoriále použijeme štartovací program Spring Boot SQL.

 org.javers javers-spring-boot-starter-sql 5.6.3 

Pretože budeme používať databázu H2, zahrňme aj túto závislosť:

 com.h2database h2 

3.2. Nastavenie úložiska JaVers

JaVers používa na ukladanie potvrdení a serializovaných entít abstrakciu úložiska. Všetky údaje sú uložené vo formáte JSON. Preto by mohlo byť dobré použiť úložisko NoSQL. Kvôli jednoduchosti však použijeme inštanciu H2 v pamäti.

V predvolenom nastavení JaVers využíva implementáciu úložiska v pamäti a ak používame Spring Boot, nie je potrebná žiadna ďalšia konfigurácia. Ďalej pri použití spúšťačov Spring Data JaVers znova používa konfiguráciu databázy pre aplikáciu.

JaVers poskytuje dva spúšťače pre zásobníky trvalosti SQL a Mongo. Sú kompatibilné s Spring Data a štandardne nevyžadujú ďalšiu konfiguráciu. Vždy však môžeme prepísať predvolené konfiguračné fazule: JaversSqlAutoConfiguration.java a JaversMongoAutoConfiguration.java resp.

3.3. JaVers Properties

JaVers umožňuje konfiguráciu niekoľkých možností, aj keď predvolené nastavenia Spring Boot sú vo väčšine prípadov dostatočné.

Prepíšeme iba jeden, newObjectSnapshot, aby sme mohli získať snímky novovytvorených objektov:

javers.newObjectSnapshot = true 

3.4. Konfigurácia domény JaVers

JaVers interne definuje nasledujúce typy: Entity, Hodnotové objekty, Hodnoty, Kontajnery a Primitívy. Niektoré z týchto výrazov pochádzajú z terminológie DDD (Domain Driven Design).

Hlavným účelom viacerých typov je poskytnúť rôzne algoritmy rozdielov v závislosti od typu. Každý typ má zodpovedajúcu stratégiu rozdielov. V dôsledku toho, ak sú triedy aplikácií nakonfigurované nesprávne, dostaneme nepredvídateľné výsledky.

Aby sme povedali JaVers, aký typ použiť pre triedu, máme niekoľko možností:

  • Explicitne - prvou možnosťou je výslovne použiť Registrovať* metódy JaversBuilder trieda - druhým spôsobom je použitie anotácií
  • Implicitne - JaVers poskytuje algoritmy na automatickú detekciu typov na základe vzťahov medzi triedami
  • Predvolené hodnoty - štandardne bude JaVers považovať všetky triedy za ValueObjects

V tomto tutoriáli nakonfigurujeme JaVers explicitne pomocou metódy anotácie.

Skvelé je, že JaVers je kompatibilný s javax.perzistencia anotácie. Vo výsledku nebudeme musieť na svoje entity používať poznámky špecifické pre JaVers.

4. Ukážkový projekt

Teraz vytvoríme jednoduchú aplikáciu, ktorá bude obsahovať niekoľko entít domény, ktoré budeme auditovať.

4.1. Doménové modely

Našou doménou budú obchody s produktmi.

Definujme Uložiť subjekt:

@Entity public class Store {@Id @GeneratedValue private int id; súkromné ​​meno reťazca; @ Integrovaná adresa súkromnej adresy; @OneToMany (mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true) private List products = new ArrayList (); // konštruktory, getre, setre}

Upozorňujeme, že používame predvolené anotácie JPA. JaVers ich mapuje nasledujúcim spôsobom:

  • @ javax.persistence.Entity je namapované na @ org.javers.core.metamodel.annotation.Entity
  • @ javax.persistence.Embeddable je namapované na @ org.javers.core.metamodel.annotation.ValueObject.

Vložené triedy sú definované obvyklým spôsobom:

@Embeddable verejná trieda Adresa {súkromná adresa reťazca; súkromné ​​celé číslo zipCode; }

4.2. Úložiská údajov

Za účelom auditu úložísk JPA poskytuje JaVers @JaversSpringDataAuditable anotácia.

Poďme definovať StoreRepository s touto anotáciou:

@JaversSpringDataAuditable verejné rozhranie StoreRepository rozširuje CrudRepository {}

Ďalej budeme mať Úložisko produktu, ale bez poznámok:

verejné rozhranie ProductRepository rozširuje CrudRepository {}

Teraz zvážime prípad, keď nepoužívame úložiská Spring Data. JaVers má na tento účel inú anotáciu na úrovni metódy: @JaversAuditable.

Napríklad môžeme definovať metódu pretrvávania produktu nasledovne:

@JaversAuditable public void saveProduct (produktový produkt) {// uložiť objekt}

Prípadne môžeme túto anotáciu pridať priamo nad metódu v rozhraní úložiska:

verejné rozhranie ProductRepository rozširuje CrudRepository {@Override @JaversAuditable S save (S s); }

4.3. Autor Poskytovateľ

Každá potvrdená zmena v JaVers by mala mať svojho autora. JaVers navyše podporuje Spring Security už po vybalení z krabice.

Výsledkom je, že každé potvrdenie vykoná konkrétny autentizovaný užívateľ. Pre tento tutoriál však vytvoríme skutočne jednoduchú vlastnú implementáciu AutorPoskytovateľ Rozhranie:

súkromná statická trieda SimpleAuthorProvider implementuje AuthorProvider {@Override public String provide () {return "Baeldung Author"; }}

A ako posledný krok, aby mohol JaVers používať našu vlastnú implementáciu, musíme prepísať predvolenú konfiguračnú fazuľu:

@Bean public AuthorProvider provideJaversAuthor () {vrátiť nový SimpleAuthorProvider (); }

5. Audit JaVers

Nakoniec sme pripravení vykonať audit našej žiadosti. Na odosielanie zmien do našej aplikácie a načítanie protokolu potvrdenia JaVers použijeme jednoduchý radič. Prípadne môžeme tiež prejsť na konzolu H2 a pozrieť sa na vnútornú štruktúru našej databázy:

Ak chcete získať nejaké počiatočné vzorové údaje, použijeme EventListener naplniť našu databázu niektorými produktmi:

@EventListener public void appReady (udalosť ApplicationReadyEvent) {Store store = new Store ("Baeldung store", new Address ("Some street", 22222)); for (int i = 1; i <3; i ++) {Product product = new Product ("Product #" + i, 100 * i); store.addProduct (produkt); } storeRepository.save (obchod); }

5.1. Počiatočný záväzok

Keď je objekt vytvorený, JaVers najskôr sa zaviaže POČIATOČNÉ typu.

Po spustení aplikácie skontrolujeme snímky:

@GetMapping ("/ stores / snapshots") public String getStoresSnapshots () {QueryBuilder jqlQuery = QueryBuilder.byClass (Store.class); Zoznam snímok = javers.findSnapshots (jqlQuery.build ()); return javers.getJsonConverter (). toJson (snímky); }

V kóde vyššie dopytujeme JaVers na snímky pre Uložiť trieda. Ak podáme žiadosť o tento koncový bod, dostaneme výsledok, ako je tento:

[{"commitMetadata": {"author": "Baeldung Author", "properties": [], "commitDate": "2019-08-26T07: 04: 06,776", "commitDateInstant": "2019-08-26T04: 04: 06.776Z "," id ": 1.00}," globalId ": {" entity ":" com.baeldung.springjavers.domain.Store "," cdoId ": 1}," state ": {" adresa ": {"valueObject": "com.baeldung.springjavers.domain.address", "ownerId": {"entity": "com.baeldung.springjavers.domain.Store", "cdoId": 1}, "fragment": " address "}," name ":" Baeldung store "," id ": 1," products ": [{" entity ":" com.baeldung.springjavers.domain.Product "," cdoId ": 2}, {" entity ":" com.baeldung.springjavers.domain.Product "," cdoId ": 3}]}," changedProperties ": [" address "," name "," id "," products "]," type ": "INITIAL", "version": 1}]

Upozorňujeme, že snímka hore zahŕňa všetky produkty pridané do obchodu napriek chýbajúcej anotácii pre Úložisko produktu rozhranie.

V predvolenom nastavení JaVers bude auditovať všetky súvisiace modely agregovaného koreňa, ak sú zachované spolu s rodičom.

Môžeme povedať JaVers, aby ignoroval konkrétne triedy pomocou DiffIgnore anotácia.

Napríklad môžeme anotovať Produkty pole s anotáciou v Uložiť subjekt:

@DiffIgnore private list products = new ArrayList ();

V dôsledku toho JaVers nebude sledovať zmeny produktov pochádzajúcich z Uložiť subjekt.

5.2. Aktualizovať potvrdenie

Ďalším typom potvrdenia je AKTUALIZÁCIA spáchať. Toto je najcennejší typ potvrdenia, pretože predstavuje zmeny stavu objektu.

Definujme metódu, ktorá aktualizuje entitu obchodu a všetky produkty v obchode:

public void rebrandStore (int storeId, reťazec updatedName) {voliteľné storeOpt = storeRepository.findById (storeId); storeOpt.ifPresent (store -> {store.setName (updatedName); store.getProducts (). forEach (product -> {product.setNamePrefix (updatedName);}); storeRepository.save (store);}); }

Ak spustíme túto metódu, dostaneme do výstupu ladenia nasledujúci riadok (v prípade rovnakých produktov a počtu obchodov):

11: 29: 35.439 [http-nio-8080-exec-2] INFO org.javers.core.Javers - Commit (id: 2.0, snímky: 3, autor: Baeldung Author, changes - ValueChange: 3), urobené v 48 millis (rozdiel: 43, pretrvávať: 5)

Pretože JaVers úspešne pretrvával zmeny, položme si dotaz na snímky produktov:

@GetMapping ("/ products / snapshots") public String getProductSnapshots () {QueryBuilder jqlQuery = QueryBuilder.byClass (Product.class); Zoznam snímok = javers.findSnapshots (jqlQuery.build ()); return javers.getJsonConverter (). toJson (snímky); }

Dostaneme predchádzajúce POČIATOČNÉ zaviaže a nové AKTUALIZÁCIA zaviaže:

 {"commitMetadata": {"author": "Baeldung Author", "properties": [], "commitDate": "2019-08-26T12: 55: 20.197", "commitDateInstant": "2019-08-26T09: 55 : 20.197Z "," id ": 2.00}," globalId ": {" entity ":" com.baeldung.springjavers.domain.Product "," cdoId ": 3}," state ": {" price ": 200.0 , "name": "NewProduct # 2", "id": 3, "store": {"entity": "com.baeldung.springjavers.domain.Store", "cdoId": 1}}}

Tu vidíme všetky informácie o zmene, ktorú sme vykonali.

Stojí za zmienku, že JaVers nevytvára nové pripojenia k databáze. Namiesto toho znova použije existujúce pripojenia. Údaje JaVers sú potvrdené alebo vrátené späť spolu s údajmi aplikácie v tej istej transakcii.

5.3. Zmeny

JaVers zaznamenáva zmeny ako atómové rozdiely medzi verziami objektu. Ako môžeme vidieť z JaVersovej schémy, neexistuje žiadna samostatná tabuľka na ukladanie zmien, takže JaVers počíta zmeny dynamicky ako rozdiel medzi snímkami.

Poďme aktualizovať cenu produktu:

public void updateProductPrice (Integer productId, dvojnásobná cena) {Optional productOpt = productRepository.findById (productId); productOpt.ifPresent (product -> {product.setPrice (price); productRepository.save (product);}); }

Potom požiadajme o zmeny JaVers:

@GetMapping ("/ products / {productId} / changes") public String getProductChanges (@PathVariable int productId) {Product product = storeService.findProductById (productId); QueryBuilder jqlQuery = QueryBuilder.byInstance (produkt); Zmeny changes = javers.findChanges (jqlQuery.build ()); return javers.getJsonConverter (). toJson (zmeny); }

Výstup obsahuje zmenenú vlastnosť a jej hodnoty pred a po:

[{"changeType": "ValueChange", "globalId": {"entity": "com.baeldung.springjavers.domain.Product", "cdoId": 2}, "commitMetadata": {"author": "Baeldung Autor "," properties ": []," commitDate ":" 2019-08-26T16: 22: 33,339 "," commitDateInstant ":" 2019-08-26T13: 22: 33.339Z "," id ": 2,00}," vlastnosť ":" cena "," propertyChangeType ":" PROPERTY_VALUE_CHANGED "," vľavo ": 100,0," vpravo ": 3333,0}]

Na zistenie typu zmeny JaVers porovnáva následné snímky aktualizácií objektu. V uvedenom prípade, keď sme zmenili vlastnosť entity, sme dostali PROPERTY_VALUE_CHANGED zmeniť typ.

5.4. Tiene

JaVers navyše poskytuje ďalší pohľad na auditované subjekty Tieň. Tieň predstavuje stav objektu obnovený zo snímok. Tento koncept úzko súvisí s obstarávaním udalostí.

Pre Shadows existujú štyri rôzne rozsahy:

  • Povrchné - tiene sa vytvárajú zo snímky vybranej v rámci dotazu JQL
  • Detská hodnota-objekt - tiene obsahujú všetky objekty podradenej hodnoty vlastnené vybranými entitami
  • Hlboký záväzok - tiene sa vytvárajú zo všetkých snímok súvisiacich s vybranými entitami
  • Hlboké + - JaVers sa pokúša obnoviť úplné grafy objektov s (pravdepodobne) všetkými načítanými objektmi.

Použime rozsah Child-value-object a získajme tieň pre jeden obchod:

@GetMapping ("/ stores / {storeId} / shadows") public String getStoreShadows (@PathVariable int storeId) {Store store = storeService.findStoreById (storeId); JqlQuery jqlQuery = QueryBuilder.byInstance (store) .withChildValueObjects (). Build (); Zoznam shadows = javers.findShadows (jqlQuery); return javers.getJsonConverter (). toJson (shadows.get (0)); }

Vo výsledku dostaneme entitu obchodu s Adresa hodnotový objekt:

{"commitMetadata": {"author": "Baeldung Author", "properties": [], "commitDate": "2019-08-26T16: 09: 20,674", "commitDateInstant": "2019-08-26T13: 09 : 20.674Z "," id ": 1.00}," it ": {" id ": 1," name ":" Baeldung store "," address ": {" address ":" Some street "," zipCode ": 22222}, "produkty": []}}

Ak chcete získať produkty vo výsledku, môžeme použiť rozsah záväzkov.

6. Záver

V tomto tutoriáli sme videli, ako ľahko sa JaVers integruje najmä s Spring Boot a Spring Data. Celkovo možno povedať, že JaVers vyžaduje na nastavenie takmer nulovú konfiguráciu.

Na záver možno povedať, že JaVers môže mať rôzne aplikácie, od ladenia až po komplexnú analýzu.

Celý projekt tohto článku je k dispozícii na GitHub.


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