JaVers használata az adatmodell-auditáláshoz a tavaszi adatokban

1. Áttekintés

Ebben az oktatóanyagban megtudhatjuk, hogyan állíthat be és használhat JaVers-t egy egyszerű Spring Boot alkalmazásban az entitások változásainak nyomon követésére.

2. JaVers

A mutábilis adatok kezelésekor általában csak egy entitás utolsó állapota van adatbázisban tárolva. Fejlesztőként sok időt töltünk egy alkalmazás hibakeresésével, és naplófájlokban keresünk egy állapotot megváltoztató eseményt. Ez még bonyolultabb lesz a termelési környezetben, amikor sok különböző felhasználó használja a rendszert.

Szerencsére vannak olyan remek eszközeink, mint a JaVers. A JaVers egy napló keretrendszer, amely segít nyomon követni az entitások változását az alkalmazásban.

Az eszköz használata nem korlátozódik csak a hibakeresésre és az auditálásra. Sikeresen alkalmazható elemzés elvégzésére, biztonsági irányelvek kényszerítésére és az eseménynapló karbantartására is.

3. Projekt beállítása

Először is, a JaVers használatának megkezdéséhez be kell állítanunk az ellenőrzési adattárat az entitások folyamatos pillanatképeinek elkészítéséhez. Másodsorban módosítanunk kell a JaVers néhány konfigurálható tulajdonságát. Végül kitérünk arra is, hogyan konfigurálhatjuk megfelelően a tartománymodelljeinket.

De érdemes megemlíteni, hogy a JaVers alapértelmezett konfigurációs beállításokat biztosít, így szinte konfiguráció nélkül elkezdhetjük használni.

3.1. Függőségek

Először hozzá kell adnunk a JaVers Spring Boot starter függőségét a projektünkhöz. A perzisztencia tárolásának típusától függően két lehetőségünk van: org.javers: javers-spring-boot-starter-sql és org.javers: javers-spring-boot-starter-mongo. Ebben az oktatóanyagban a Spring Boot SQL indítót fogjuk használni.

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

Mivel a H2 adatbázist fogjuk használni, tegyük bele ezt a függőséget is:

 com.h2adatbázis h2 

3.2. JaVers Repository Setup

A JaVers a lerakat absztrakcióját használja a véglegesítések és a sorosított entitások tárolására. Minden adatot JSON formátumban tárolunk. Ezért megfelelő lehet egy NoSQL-tároló használata. Az egyszerűség kedvéért azonban egy H2 memóriában lévő példányt fogunk használni.

Alapértelmezés szerint a JaVers kihasználja a memóriában lévő lerakat megvalósítását, és ha a Spring Boot alkalmazást használjuk, nincs szükség további konfigurálásra. Továbbá, a Spring Data indítók használata közben a JaVers újra felhasználja az alkalmazás adatbázis-konfigurációját.

A JaVers két indítót kínál az SQL és a Mongo perzisztenciahalmazokhoz. Kompatibilisek a Spring Data-val, és alapértelmezés szerint nem igényelnek további konfigurációt. Az alapértelmezett konfigurációs babokat azonban mindig felülírhatjuk: JaversSqlAutoConfiguration.java és JaversMongoAutoConfiguration.java illetőleg.

3.3. JaVers tulajdonságai

A JaVers lehetővé teszi több opció konfigurálását, bár a tavaszi indítás alapértelmezett értékei a legtöbb használati esetben elegendőek.

Csak egyet írjunk felül, newObjectSnapshot, hogy pillanatképeket kaphassunk az újonnan létrehozott objektumokról:

javers.newObjectSnapshot = true 

3.4. JaVers tartománykonfiguráció

A JaVers belül meghatározza a következő típusokat: Entitások, Értékobjektumok, Értékek, Tárolók és Primitívek. Ezen kifejezések egy része a DDD (Domain Driven Design) terminológiából származik.

A többféle típus fő célja az, hogy a típustól függően különböző diff algoritmusokat nyújtson. Minden típusnak megvan a megfelelő diff stratégiája. Ennek eredményeként, ha az alkalmazásosztályok helytelenül vannak konfigurálva, kiszámíthatatlan eredményeket kapunk.

Több lehetőségünk van megmondani JaVersnek, hogy milyen típust használjon egy osztályhoz:

  • Kifejezetten - az első lehetőség a kifejezett használat Regisztráció* módszerei JaversBuilder osztály - a második módszer az annotációk használata
  • Hallgatólagosan - A JaVers algoritmusokat kínál a típusok automatikus felismerésére az osztálykapcsolatok alapján
  • Alapértelmezések - alapértelmezés szerint a JaVers az összes osztályt úgy kezeli ValueObjects

Ebben az oktatóanyagban kifejezetten konfiguráljuk a JaVers alkalmazást, az annotációs módszer használatával.

A nagy dolog az A JaVers kompatibilis a következővel: javax.kitartás annotációk. Ennek eredményeként nem kell használnunk JaVers-specifikus jegyzeteket entitásainkon.

4. Mintaprojekt

Most egy egyszerű alkalmazást fogunk létrehozni, amely több domain entitást is tartalmaz, amelyeket ellenőrizni fogunk.

4.1. Domain modellek

A domainünkbe beletartoznak a termékekkel rendelkező üzletek.

Határozzuk meg a Bolt entitás:

@Entity public class Store {@Id @GeneratedValue private int id; privát karakterlánc neve; @ Beágyazott privát cím címe; @OneToMany (mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true) private List termékek = új ArrayList (); // kivitelezők, szerelők, telepítők}

Felhívjuk figyelmét, hogy alapértelmezett JPA-jelöléseket használunk. JaVers a következő módon térképezi fel őket:

  • @ javax.persistence.Entity fel van térképezve @ org.javers.core.metamodel.annotation.Entity
  • @ javax.persistence.Embeddable fel van térképezve @ org.javers.core.metamodel.annotation.ValueObject.

A beágyazható osztályok meghatározása a szokásos módon történik:

@ Beágyazható nyilvános osztály Cím {private String address; privát egész szám irányítószám; }

4.2. Adattárak

A JPA adattárainak ellenőrzéséhez a JaVers biztosítja a @JaversSpringDataAuditable annotáció.

Határozzuk meg a StoreRepository ezzel a kommentárral:

@JaversSpringDataAuditable nyilvános felület StoreRepository kiterjeszti a CrudRepository {}

Továbbá meglesz a ProductRepository, de nincs megjegyzéssel:

nyilvános felület A ProductRepository kiterjeszti a CrudRepository {}

Vegyünk egy olyan esetet, amikor nem a Spring Data adattárakat használjuk. A JaVers-nek van egy másik módszer szintű annotációja erre a célra: @JaversAuditable.

Például meghatározhatunk egy módszert a termék fennmaradására az alábbiak szerint:

@JaversAuditable public void saveProduct (Product product) {// objektum mentése}

Alternatív megoldásként ezt a megjegyzéseket közvetlenül hozzáadhatjuk egy módszer fölé a tárház felületén:

nyilvános felület A ProductRepository kiterjeszti a CrudRepository {@Override @JaversAuditable S save (S); }

4.3. Szerző Szolgáltató

A JaVers minden elkövetett változásának meg kell írnia a szerzőjét. Sőt, a JaVers már a dobozon kívül támogatja a Spring Security szolgáltatást.

Ennek eredményeként minden elkötelezettséget egy meghatározott hitelesített felhasználó hajt végre. Ehhez a bemutatóhoz azonban létrehozunk egy nagyon egyszerű egyéni megvalósítást a AuthorProvider Felület:

privát statikus osztály A SimpleAuthorProvider végrehajtja az AuthorProvider {@Orride public String provide () {return "Baeldung Author"; }}

És utolsó lépésként, hogy a JaVers felhasználhassa az egyéni megvalósítást, felül kell írnunk az alapértelmezett konfigurációs komponenst:

@Bean public AuthorProvider biztosítjaJaversAuthor () {return new SimpleAuthorProvider (); }

5. JaVers Audit

Végül készen állunk alkalmazásunk ellenőrzésére. Egy egyszerű vezérlőt fogunk használni az alkalmazásunkban bekövetkező változások elküldéséhez és a JaVers végrehajtási napló lekéréséhez. Alternatív megoldásként elérhetjük a H2 konzolt is, és megtekinthetjük adatbázisunk belső felépítését:

Néhány kezdeti mintadat megadásához használjunk egy EventListener hogy feltöltse adatbázisunkat néhány termékkel:

@EventListener public void appReady (ApplicationReadyEvent event) {Store store = new Store ("Baeldung store", new Address ("Some street", 22222)); for (int i = 1; i <3; i ++) {termék termék = új termék ("termék #" + i, 100 * i); store.addProduct (termék); } storeRepository.save (store); }

5.1. Kezdeti kötelezettségvállalás

Objektum létrehozásakor JaVers először elkötelezi magát a A KEZDETI típus.

Ellenőrizzük a pillanatképeket az alkalmazás indítása után:

@GetMapping ("/ stores / snapshots") public String getStoresSnapshots () {QueryBuilder jqlQuery = QueryBuilder.byClass (Store.class); Sorolja fel a pillanatképeket = javers.findSnapshots (jqlQuery.build ()); adja vissza a javers.getJsonConverter (). toJson-t (pillanatképek); }

A fenti kódban a JaVers-től kérünk pillanatképeket a Bolt osztály. Ha ezt a végpontot kérjük, az alábbihoz hasonló eredményt kapunk:

[{"kötelezettségMetadata": {"szerző": "Baeldung Szerző", "tulajdonságok": [], "elkötelezettség": "2019-08-26T07: 04: 06.776", "pühendDateInstant": "2019-08-26T04: 04: 06.776Z "," id ": 1.00}," globalId ": {" entitás ":" com.baeldung.springjavers.domain.Store "," cdoId ": 1}," state ": {" address ": {"valueObject": "com.baeldung.springjavers.domain.Address", "ownerId": {"entitás": "com.baeldung.springjavers.domain.Store", "cdoId": 1}, "fragment": " cím "}," név ":" Baeldung áruház "," id ": 1," termékek ": [{" entitás ":" com.baeldung.springjavers.domain.Product "," cdoId ": 2}, {" entitás ":" com.baeldung.springjavers.domain.Product "," cdoId ": 3}]}," changedProperties ": [" cím "," név "," id "," termékek "]," típus ": "INITIAL", "version": 1}]

Vegye figyelembe, hogy a fenti pillanatkép magában foglalja az összes terméket, amelyet a boltba adtak, annak ellenére, hogy a ProductRepository felület.

Alapértelmezés szerint a JaVers ellenőrizni fogja az összesített gyökér összes kapcsolódó modelljét, ha azok továbbra is fennmaradnak a szülővel együtt.

Azt mondhatjuk JaVersnek, hogy hagyja figyelmen kívül bizonyos osztályokat a DiffIgnore annotáció.

Például feljegyezhetjük a Termékek mező a Bolt entitás:

@DiffIgnore privát lista termékek = new ArrayList ();

Következésképpen a JaVers nem fogja nyomon követni a Bolt entitás.

5.2. Kötelezettség frissítése

A következő típusú elköteleződés a FRISSÍTÉS elkövetni. Ez a legértékesebb végrehajtási típus, mivel az objektum állapotának változását ábrázolja.

Határozzunk meg egy módszert, amely frissíti az üzlet entitását és a bolt összes termékét:

public void rebrandStore (int storeId, String updatedName) {Opcionális storeOpt = storeRepository.findById (storeId); storeOpt.ifPresent (store -> {store.setName (updatedName); store.getProducts (). forEach (termék -> {product.setNamePrefix (updatedName);}); storeRepository.save (store);}); }

Ha ezt a módszert futtatjuk, a következő sort kapjuk a hibakeresési kimenetben (ugyanazon termékek és üzletek száma esetén):

11: 29: 35.439 [http-nio-8080-exec-2] INFO org.javers.core.Javers - Commit (id: 2.0, pillanatképek: 3, szerző: Baeldung Szerző, változtatások - ValueChange: 3), 48 millisz (diff: 43, perzisztens: 5)

Mivel a JaVers sikeresen fenntartotta a változásokat, kérdezzük le a termékek pillanatképeit:

@GetMapping ("/ products / snapshots") public String getProductSnapshots () {QueryBuilder jqlQuery = QueryBuilder.byClass (Product.class); Sorolja fel a pillanatképeket = javers.findSnapshots (jqlQuery.build ()); adja vissza a javers.getJsonConverter (). toJson-t (pillanatképek); }

Előzővé válunk A KEZDETI elkövet és új FRISSÍTÉS elköveti:

 {"kötelezettségMetadata": {"szerző": "Baeldung Szerző", "tulajdonságok": [], "elkötelezettség": "2019-08-26T12: 55: 20.197", "pühendDateInstant": "2019-08-26T09: 55 : 20.197Z "," id ": 2.00}," globalId ": {" entitás ":" com.baeldung.springjavers.domain.Product "," cdoId ": 3}," állapot ": {" ár ": 200.0 , "name": "NewProduct # 2", "id": 3, "store": {"entitás": "com.baeldung.springjavers.domain.Store", "cdoId": 1}}}

Itt láthatunk minden információt a végrehajtott változásról.

Érdemes megjegyezni A JaVers nem hoz létre új kapcsolatokat az adatbázissal. Ehelyett újból felhasználja a meglévő kapcsolatokat. A JaVers adatait ugyanabban a tranzakcióban végzik el vagy visszagörgetik az alkalmazás adataival együtt.

5.3. Változtatások

JaVers az objektum verziói közötti atomi különbségekként rögzíti a változásokat. Amint a JaVers sémából láthatjuk, nincs külön táblázat a változások tárolására, így A JaVers dinamikusan kiszámítja a változásokat a pillanatképek közötti különbségként.

Frissítsük a termék árát:

public void updateProductPrice (Integer productId, Double price) {Opcionális productOpt = productRepository.findById (productId); productOpt.ifPresent (termék -> {product.setPrice (ár); productRepository.save (termék);}); }

Ezután kérdezzük meg a JaVers-t a változásokról:

@GetMapping ("/ products / {productId} / changes") public String getProductChanges (@PathVariable int productId) {Product product = storeService.findProductById (productId); QueryBuilder jqlQuery = QueryBuilder.byInstance (termék); Changes changes = javers.findChanges (jqlQuery.build ()); adja vissza a javers.getJsonConverter (). toJson-t (változtatások); }

A kimenet tartalmazza a megváltozott tulajdonságot és annak értékeit az előtt és után:

[{"changeType": "ValueChange", "globalId": {"entitás": "com.baeldung.springjavers.domain.Product", "cdoId": 2}, "pühendMetadata": {"szerző": "Baeldung Szerző "," tulajdonságok ": []," pühendDate ":" 2019-08-26T16: 22: 33.339 "," pühendDateInstant ":" 2019-08-26T13: 22: 33.339Z "," id ": 2.00}," tulajdonság ":" ár "," tulajdonságChangeType ":" PROPERTY_VALUE_CHANGED "," bal ": 100.0," jobb ": 3333.0}]

A változás típusának észleléséhez a JaVers összehasonlítja az objektum frissítésének későbbi pillanatképeit. A fenti esetben, amikor megváltoztattuk az entitás tulajdonságát, megvan PROPERTY_VALUE_CHANGED típus módosítása.

5.4. Árnyékok

Ezenkívül a JaVers egy másik nézetet ad az auditált entitásokról Árnyék. Az árnyék egy pillanatképekből visszaállított objektum állapotot képvisel. Ez a koncepció szorosan kapcsolódik az Események beszerzéséhez.

Az Árnyaknak négy különböző hatóköre van:

  • Sekély - az árnyékok egy JQL lekérdezésen belül kiválasztott pillanatképből készülnek
  • Gyermekérték-objektum - az árnyékok tartalmazzák a kijelölt entitások tulajdonában lévő összes gyermekérték objektumot
  • Elkötelezettségig - árnyékok jönnek létre a kiválasztott entitásokhoz kapcsolódó összes pillanatképből
  • Deep + - JaVers megpróbálja visszaállítani a teljes objektumgrafikonokat (esetleg) az összes objektummal.

Használjuk a Gyermekérték-objektum hatókört, és kapjunk árnyékot egyetlen üzlet számára:

@GetMapping ("/ stores / {storeId} / shadows") public String getStoreShadows (@PathVariable int storeId) {Store store = storeService.findStoreById (storeId); JqlQuery jqlQuery = QueryBuilder.byInstance (store) .withChildValueObjects (). Build (); Lista árnyékok = javers.findShadows (jqlQuery); adja vissza a javers.getJsonConverter (). toJson-t (shadows.get (0)); }

Ennek eredményeként megkapjuk a bolti entitást a Cím érték objektum:

{"kötelezettségMetadata": {"szerző": "Baeldung Szerző", "tulajdonságok": [], "elkötelezettség": "2019-08-26T16: 09: 20.674", "pühendDateInstant": "2019-08-26T13: 09 : 20,674Z "," id ": 1.00}," it ": {" id ": 1," name ":" Baeldung store "," address ": {" address ":" Some street "," zipCode ": 22222}, "termékek": []}}

Ahhoz, hogy termékeket kapjunk az eredményben, alkalmazhatjuk a Commit-deep hatókört.

6. Következtetés

Ebben az oktatóanyagban láthattuk, hogy a JaVers milyen könnyen integrálódik a Spring Boot és a Spring Data szolgáltatásokkal. Összességében a JaVers beállításához szinte nulla konfigurációra van szükség.

Összegzésként elmondhatom, hogy a JaVers-nek különböző alkalmazásai lehetnek, a hibakereséstől a komplex elemzésig.

A cikk teljes projektje elérhető a GitHub oldalon.


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