שימוש ב- JaVers לצורך ביקורת מודל נתונים בנתוני אביב

1. סקירה כללית

במדריך זה נראה כיצד להגדיר ולהשתמש ב- JaVers ביישום Spring Boot פשוט למעקב אחר שינויים בישויות.

2. ג'ברס

כאשר אנו עוסקים בנתונים משתנים, בדרך כלל יש לנו רק את המצב האחרון של ישות המאוחסנת במסד נתונים. כמפתחים אנו מקדישים זמן רב לניפוי באגים ביישום, בחיפוש בקבצי יומן אחר אירוע ששינה מצב. זה נהיה מסובך עוד יותר בסביבת הייצור כאשר משתמשים רבים משתמשים במערכת.

למרבה המזל, יש לנו כלים נהדרים כמו JaVers. JaVers היא מסגרת יומן ביקורת המסייעת במעקב אחר שינויים של ישויות ביישום.

השימוש בכלי זה אינו מוגבל לאיתור באגים וביקורת בלבד. ניתן ליישם אותו בהצלחה גם לניתוח, לאלץ מדיניות אבטחה ולתחזק את יומן האירועים.

3. הגדרת פרויקט

ראשית כל, כדי להתחיל להשתמש ב- JaVers עלינו להגדיר את מאגר הביקורת לצילומי תצלום קבועים של ישויות. שנית, עלינו להתאים כמה מאפיינים הניתנים להגדרה של JaVers. לבסוף, נסקור גם כיצד להגדיר את דגמי הדומיינים שלנו כראוי.

אך ראוי להזכיר כי JaVers מספקת אפשרויות ברירת מחדל לתצורה, כך שנוכל להתחיל להשתמש בה כמעט ללא תצורה.

3.1. תלות

ראשית, עלינו להוסיף את התלות המתחילה של JaVers Spring Boot לפרויקט שלנו. בהתאם לסוג אחסון ההתמדה, יש לנו שתי אפשרויות: org.javers: javers-spring-boot-starter-sql ו org.javers: javers-spring-boot-starter-mongo. במדריך זה נשתמש במתחיל ה- Spring Boot SQL.

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

כאשר אנו הולכים להשתמש במסד הנתונים H2, בואו נכלול גם את התלות הזו:

 com.h2database h2 

3.2. הגדרת מאגר JaVers

JaVers משתמשת בהפשטה של ​​מאגר לאחסון התחייבויות וישויות סדרתיות. כל הנתונים נשמרים בפורמט JSON. לכן, זה יכול להיות מתאים לשימוש באחסון NoSQL. עם זאת, למען הפשטות, נשתמש במופע H2 בזיכרון.

כברירת מחדל, JaVers מממש יישום מאגר בזיכרון, ואם אנו משתמשים באביב אתחול, אין צורך בתצורה נוספת. יתר על כן, תוך שימוש במתחילי Spring Data, JaVers עושה שימוש חוזר בתצורת מסד הנתונים של היישום.

חברת JaVers מספקת שני מנות ראשונות עבור ערימות התמדה של SQL ומונגו. הם תואמים לנתוני אביב ואינם דורשים תצורה נוספת כברירת מחדל. עם זאת, אנו תמיד יכולים לעקוף את שעועית התצורה המוגדרת כברירת מחדל: JaversSqlAutoConfiguration.java ו JaversMongoAutoConfiguration.java בהתאמה.

3.3. נכסי JaVers

JaVers מאפשרת להגדיר מספר אפשרויות, אם כי ברירות המחדל של Spring Boot מספיקות ברוב מקרי השימוש.

בואו נעקוף רק אחד, newObjectSnapshotכדי שנוכל לקבל תצלומים של אובייקטים שזה עתה נוצרו:

javers.newObjectSnapshot = נכון 

3.4. תצורת התחום של JaVers

JaVers מגדיר באופן פנימי את הסוגים הבאים: ישויות, אובייקטים ערכיים, ערכים, מיכלים ופרימיטיביים. חלק מהמונחים הללו מקורם במינוח DDD (Domain Driven Design).

המטרה העיקרית של מספר סוגים היא לספק אלגוריתמי דיפ שונים בהתאם לסוג. לכל סוג יש אסטרטגיית הבדל תואמת. כתוצאה מכך, אם שיעורי יישומים מוגדרים בצורה שגויה נקבל תוצאות בלתי צפויות.

כדי לספר ל- Javers באיזה סוג להשתמש בשיעור, יש לנו מספר אפשרויות:

  • בִּמְפוּרָשׁ - האפשרות הראשונה היא להשתמש במפורש להירשם* שיטות של JaversBuilder class - הדרך השנייה היא להשתמש בהערות
  • באופן מרומז - JaVers מספק אלגוריתמים לזיהוי סוגים באופן אוטומטי על בסיס יחסי מעמדות
  • ברירות מחדל - כברירת מחדל, JaVers יתייחס לכל השיעורים כאל ValueObjects

במדריך זה, נגדיר את JaVers באופן מפורש, באמצעות שיטת ההערה.

הדבר הגדול הוא זה JaVers תואם ל- javax. התמדה ביאורים. כתוצאה מכך, לא נצטרך להשתמש בהערות ספציפיות ל- JaVers על הישויות שלנו.

4. פרויקט לדוגמא

כעת ניצור יישום פשוט שיכלול מספר ישויות דומיין שנבקר.

4.1. מודלים מתחום

התחום שלנו יכלול חנויות עם מוצרים.

בואו נגדיר את חנות יֵשׁוּת:

חנות בכיתה ציבורית @Entity {@Id @GeneratedValue מזהה פרטי; שם מחרוזת פרטי; @ כתובת כתובת פרטית משובצת; @OneToMany (mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true) פרטי רשימת מוצרים = ArrayList חדש (); // בונים, גטרים, סטרים}

שים לב שאנחנו משתמשים בהערות ברירת מחדל של JPA. JaVers ממפה אותם באופן הבא:

  • @ javax.persistence.Entity ממופה ל @ org.javers.core.metamodel.annotation.Entity
  • @ javax.persistence. ניתן להטמיעה ממופה ל @ org.javers.core.metamodel.annotation.ValueObject.

שיעורים להטמעה מוגדרים באופן הרגיל:

@ Embeddable class class Address {פרטי כתובת מחרוזת; פרטי שלם zipCode; }

4.2. מאגרי נתונים

על מנת לבצע ביקורת על מאגרי JPA, חברת JaVers מספקת את @JaversSpringDataAuditable ביאור.

בואו נגדיר את StoreRepository עם ההערה הזו:

ממשק ציבורי @JaversSpringDataAuditable StoreRepository מרחיב את CrudRepository {}

יתר על כן, יהיה לנו את מאגר מוצר, אך לא ביאור:

ממשק ציבורי ProductRepository מרחיב את CrudRepository {}

כעת שקול מקרה בו איננו משתמשים במאגרי Spring Data. ל- JaVers יש הערה נוספת ברמת השיטה למטרה זו: @JaversAuditable.

לדוגמה, אנו יכולים להגדיר שיטה להתמדת מוצר באופן הבא:

@Javers ציבורי מבודד בטל saveProduct (מוצר מוצר) {// שמור אובייקט}

לחלופין, אנו יכולים אפילו להוסיף הערה זו ישירות מעל שיטה בממשק המאגר:

ממשק ציבורי ProductRepository מרחיב את CrudRepository {@Override @JaversAuditable S save (S s); }

4.3. ספק מחבר

כל שינוי מחויב ב- JaVers צריך להיות המחבר שלו. יתר על כן, JaVers תומך באביב ביטחון מהקופסה.

כתוצאה מכך, כל התחייבות מתבצעת על ידי משתמש מאומת ספציפי. עם זאת, עבור הדרכה זו ניצור יישום מותאם אישית פשוט מאוד של ה- מחבר ספק מִמְשָׁק:

מחלקה סטטית פרטית SimpleAuthorProvider מיישם את AuthorProvider {@ Override public String supply () {return "Baeldung Author"; }}

וכצעד האחרון, לגרום ל- JaVers להשתמש ביישום המותאם אישית שלנו, עלינו לבטל את שעועית התצורה המוגדרת כברירת מחדל:

@Bean ציבורי AuthorProvider provideJaversAuthor () {להחזיר SimpleAuthorProvider חדש (); }

5. ביקורת ג'ברס

לבסוף, אנו מוכנים לבחון את היישום שלנו. נשתמש בבקר פשוט לשיגור שינויים ביישום שלנו ולאחזור יומן ההתחייבות של JaVers. לחלופין, אנו יכולים גם לגשת למסוף H2 כדי לראות את המבנה הפנימי של מסד הנתונים שלנו:

כדי לקבל כמה נתונים לדוגמה ראשוניים, בואו נשתמש ב- EventListener לאכלס את מסד הנתונים שלנו בכמה מוצרים:

@EventListener public void appReady (ApplicationReadyEvent event) {חנות חנות = חנות חדשה ("חנות באלדונג", כתובת חדשה ("רחוב כלשהו", 22222)); עבור (int i = 1; i <3; i ++) {מוצר מוצר = מוצר חדש ("מוצר #" + i, 100 * i); store.addProduct (מוצר); } storeRepository.save (חנות); }

5.1. התחייבות ראשונית

כאשר נוצר אובייקט, JaVers קודם מתחייב על התחלתי סוּג.

בואו נבדוק את תמונות הצילום לאחר הפעלת היישום:

@GetMapping ("/ stores / snapshots") מחרוזת ציבורית getStoresSnapshots () {QueryBuilder jqlQuery = QueryBuilder.byClass (Store.class); רשימת תצלומים = javers.findSnapshots (jqlQuery.build ()); להחזיר javers.getJsonConverter (). toJson (תמונות); }

בקוד שלעיל, אנו שואלים את JaVers לתמונות עבור ה- חנות מעמד. אם אנו מגישים בקשה לנקודת קצה זו נקבל תוצאה כמו זו שלמטה:

[{"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 ": {" address ": {"valueObject": "com.baeldung.springjavers.domain.Address", "ownerId": {"entity": "com.baeldung.springjavers.domain.Store", "cdoId": 1}, "fragment": " כתובת "}," name ":" חנות Baeldung "," id ": 1," products ": [{" entity ":" com.baeldung.springjavers.domain.Product "," cdoId ": 2}, {" ישות ":" com.baeldung.springjavers.domain.Product "," cdoId ": 3}]}," changedProperties ": [" address "," name "," id "," products "]," type ": "INITIAL", "גרסה": 1}]

שים לב כי תמונת המצב שלמעלה כולל את כל המוצרים שנוספו לחנות למרות ההערה החסרה של מאגר מוצר מִמְשָׁק.

כברירת מחדל, JaVers יבקר את כל המודלים הקשורים של שורש מצטבר אם הם קיימים יחד עם ההורה.

אנו יכולים לומר ל- JaVers להתעלם משיעורים ספציפיים באמצעות ה- DiffIgnore ביאור.

למשל, אנו עשויים להוסיף הערות ל- מוצרים שדה עם ההערה ב חנות יֵשׁוּת:

@DiffIgnore רשימת מוצרים פרטיים = ArrayList חדש ();

כתוצאה מכך, JaVers לא יעקוב אחר שינויים במוצרים שמקורם ב חנות יֵשׁוּת.

5.2. עדכן התחייבות

הסוג הבא של התחייבות הוא עדכון לְבַצֵעַ. זהו סוג ההתחייבות החשוב ביותר מכיוון שהוא מייצג שינויים במצב האובייקט.

בואו נגדיר שיטה שתעדכן את ישות החנות ואת כל המוצרים בחנות:

public roid rebrandStore (int storeId, String updatedName) {OptionOpt = storeRepository.findById (storeId) אופציונלי; storeOpt.ifPresent (store -> {store.setName (updatedName); store.getProducts (). forEach (product -> {product.setNamePrefix (updatedName);}); storeRepository.save (store);}); }

אם נפעיל שיטה זו נקבל את השורה הבאה בפלט הבאגים (במקרה של אותם מוצרים וחנויות):

11: 29: 35.439 [http-nio-8080-exec-2] INFO org.javers.core.Javers - התחייבות (id: 2.0, תמונות: 3, מחבר: Baeldung מחבר, שינויים - ValueChange: 3), נעשה ב- 48 מיליס (הבדל: 43, נמשך: 5)

מכיוון ש- JaVers התמיד בשינויים בהצלחה, בואו ונשאול את תמונות הצילום של מוצרים:

@GetMapping ("/ products / snapshots") ציבורי מחרוזת getProductSnapshots () {QueryBuilder jqlQuery = QueryBuilder.byClass (Product.class); רשימת תמונות רשימות = javers.findSnapshots (jqlQuery.build ()); להחזיר javers.getJsonConverter (). toJson (תצלומים); }

נקבל קודמות התחלתי מתחייב וחדש עדכון מתחייב:

 {"commitMetadata": {"author": "מחבר Baeldung", "נכסים": [], "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}}}

כאן נוכל לראות את כל המידע על השינוי שביצענו.

ראוי לציין זאת JaVers לא יוצר חיבורים חדשים למסד הנתונים. במקום זאת, הוא עושה שימוש חוזר בקשרים קיימים. נתוני JaVers מבוצעים או מוחזרים לאחור יחד עם נתוני יישומים באותה עסקה.

5.3. שינויים

JaVers מתעד שינויים כהבדלים אטומיים בין גרסאות של אובייקט. כפי שאנו רואים מתכנית JaVers, אין טבלה נפרדת לאחסון שינויים, כך JaVers מחשבת את השינויים באופן דינמי כהבדל בין תמונות.

בואו נעדכן מחיר מוצר:

עדכון חלל ציבוריProductPrice (מוצר שלם שלם, מחיר כפול) {Optional productOpt = productRepository.findById (productId); productOpt.ifPresent (product -> {product.setPrice (price); productRepository.save (product);}); }

לאחר מכן, בוא ונשאול את JaVers לשינויים:

@GetMapping ("/ products / {productId} / שינויים") ציבורי מחרוזת getProductChanges (@PathVariable int productId) {מוצר מוצר = storeService.findProductById (productId); QueryBuilder jqlQuery = QueryBuilder.byInstance (מוצר); שינויים שינויים = javers.findChanges (jqlQuery.build ()); החזר javers.getJsonConverter (). toJson (שינויים); }

הפלט מכיל את המאפיין ששונה ואת ערכיו לפני ואחרי:

[{"changeType": "ValueChange", "globalId": {"entity": "com.baeldung.springjavers.domain.Product", "cdoId": 2}, "commitMetadata": {"author": "מחבר Baeldung "," נכסים ": []," commitDate ":" 2019-08-26T16: 22: 33.339 "," commitDateInstant ":" 2019-08-26T13: 22: 33.339Z "," id ": 2.00}," property ":" price "," propertyChangeType ":" PROPERTY_VALUE_CHANGED "," left ": 100.0," right ": 3333.0}]

כדי לאתר סוג של שינוי JaVers משווה תמונות עוקבות אחר העדכונים של האובייקט. במקרה שלעיל כיוון ששינינו את המאפיין של הישות שקיבלנו את PROPERTY_VALUE_CHANGED שנה סוג.

5.4. צללים

יתר על כן, חברת JaVers מספקת תצוגה נוספת לגופים מבוקרים שנקראים צֵל. צל מייצג מצב אובייקט ששוחזר מצילומי תצלום. מושג זה קשור קשר הדוק למקורות אירועים.

ישנם ארבעה טווחים שונים עבור צללים:

  • רָדוּד - צללים נוצרים מתמונה שנבחרה בשאילתת JQL
  • ילד-ערך-אובייקט - צללים מכילים את כל האובייקטים הערכים של ילדים שבבעלות ישויות נבחרות
  • התחייבות עמוקה - נוצרים צללים מכל התמונות הקשורות לגופים שנבחרו
  • עמוק + - JaVers מנסה לשחזר גרפים מלאים של אובייקטים כאשר (אולי) כל האובייקטים נטענים.

בואו נשתמש בהיקף Child-value-object ואנחנו מקבלים צל לחנות אחת:

@GetMapping ("/ stores / {storeId} / shadows") מחרוזת ציבורית getStoreShadows (@PathVariable int storeId) {Store store = storeService.findStoreById (storeId); JqlQuery jqlQuery = QueryBuilder.byInstance (store) .withChildValueObjects (). Build (); רשימה צללים = javers.findShadows (jqlQuery); להחזיר javers.getJsonConverter (). toJson (shadows.get (0)); }

כתוצאה מכך נקבל את ישות החנות עם ה- כתובת אובייקט ערך:

{"commitMetadata": {"author": "מחבר Baeldung", "נכסים": [], "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 ":" רחוב כלשהו "," zipCode ": 22222}, "מוצרים": []}}

כדי לקבל מוצרים בתוצאה אנו עשויים להחיל את היקף ההתחייבות העמוקה.

6. מסקנה

במדריך זה ראינו באיזו קלות משתלב JaVers עם Spring Boot ו Spring Data בפרט. בסך הכל, JaVers דורשת הגדרה כמעט אפסית.

לסיום, ל- JaVers יכולות להיות יישומים שונים, החל מאיתור באגים וכלה בניתוח מורכב.

הפרויקט המלא של מאמר זה זמין באתר GitHub.


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