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.