עדכון נתונים חלקי עם נתוני אביב

1. הקדמה

נתוני אביב CrudRespository # שמור הוא ללא ספק פשוט, אך תכונה אחת יכולה להיות חסרון: היא מעדכנת כל עמודה בטבלה. כאלה הם הסמנטיקה של ה- U ב- CRUD, אבל מה אם נרצה לעשות PATCH במקום?

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

2. בעיה

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

אם היינו מסתכלים על ORM, ישנם תיקונים מסוימים, כמו:

  • מצב שינה @DynamicUpdatביאור אלקטרוני, שכותב מחדש באופן דינמי את שאילתת העדכון
  • JPA @טור ביאור, מכיוון שנוכל לא לאפשר עדכונים בעמודות ספציפיות באמצעות ניתן לעדכן פָּרָמֶטֶר

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

3. המקרה שלנו

ראשית, בואו לבנות a צרכן יֵשׁוּת:

לקוח בכיתה ציבורית @Entity {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) מזהה ארוך ציבורי; שם מחרוזת ציבורי; טלפון מיתרי ציבורי; } 

לאחר מכן, אנו מגדירים מאגר CRUD פשוט:

ממשק ציבורי @Repository CustomerRepository מרחיב את CrudRepository {לקוח findById (מזהה ארוך); }

לבסוף, אנו מכינים א שירות לקוחות:

@Service במחלקה הציבורית CustomerService {@ Repo ClientRepository repo; ריק ריק addCustomer (שם מחרוזת) {לקוח c = לקוח חדש (); c.name = שם; repo.save (c); }}

4. טען ושמור גישה

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

למרות שזה פשוט וברור, זה מהגישות הפשוטות ביותר שאנחנו יכולים להשתמש בהן.

בואו להוסיף שיטה בשירות שלנו לעדכון נתוני הקשר של הלקוחות שלנו.

עדכון חלל ציבורי CustomerContacts (מזהה ארוך, טלפון מחרוזת) {לקוח myCustomer = repo.findById (id); myCustomer.phone = טלפון; repo.save (myCustomer); }

אנחנו נקרא findById לשיטה ולאחזר את הישות התואמת, ואז נמשיך ונעדכן את השדות הנדרשים ומתמיד את הנתונים.

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

מה יקרה עם עשרות שדות לעדכון?

4.1. אסטרטגיית מיפוי

כאשר לאובייקטים שלנו יש מספר רב של שדות עם רמות גישה שונות, זה די נפוץ ליישם את דפוס ה- DTO.

עכשיו, נניח שיש לנו יותר ממאה מכשיר טלפון שדות באובייקט שלנו. כתיבת שיטה ששופכת את הנתונים מ- DTO ליישות שלנו, כפי שעשינו בעבר, יכולה להיות מטרידה ודי בלתי ניתנת לניהול.

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

בואו ניצור CustomerDto:

מחלקה ציבורית CustomerDto {מזהה פרטי ארוך; שם מחרוזת ציבורי; טלפון מיתרי ציבורי; // ... טלפון מחרוזת פרטי 99; }

וגם א CustomerMapper:

@Mapper (componentModel = "spring") ממשק ציבורי CustomerMapper {void updateCustomerFromDto (CustomerDto dto, @MappingTarget ישות לקוחות); }

ה @MappingTarget ביאור מאפשר לנו לעדכן אובייקט קיים, ומציל אותנו מכאב כתיבת קוד רב.

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

@BeanMapping (nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)

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

אז בואו להוסיף שיטה לשירות שלנו, שתקרא למפות שלנו:

update upoid publicCustomer (CustomerDto dto) {Customer myCustomer = repo.findById (dto.id); mapper.updateCustomerFromDto (dto, myCustomer); repo.save (myCustomer); }

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

4.2. ישויות פשוטות יותר

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

חשוב להגדיר את הישויות שלנו להיות קטנות ככל האפשר.

בואו נסתכל על שלנו צרכן יֵשׁוּת. מה אם נבנה אותו מעט ונחלץ את כל ה מכשיר טלפון שדות ל צור קשר עם טלפון ישויות ולהיות במערכת יחסים של אחד לרבים?

מעמד ציבורי @Entity CustomerStructured {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) מזהה ציבורי ארוך; שם מחרוזת ציבורי; @OneToMany (fetch = FetchType.EAGER, targetEntity = ContactPhone.class, mappedBy = "customerId") פרטי רשימת contactPhones; }

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

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

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

5. שאילתה מותאמת אישית

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

למעשה, JPA מגדיר שתי הערות, @ שינוי ו @שאילתא, המאפשרים לנו לכתוב את הצהרת העדכון שלנו במפורש.

כעת אנו יכולים להגיד ליישום שלנו כיצד להתנהג במהלך עדכון, מבלי להשאיר את הנטל על ה- ORM.

בואו להוסיף את שיטת העדכון המותאמת אישית שלנו במאגר:

@Modifying @Query ("עדכן את הלקוח להגדיר u.phone =: טלפון שבו u.id =: id") בטל updatePhone (@Param (value = "id") מזהה ארוך, @Param (value = "טלפון") מחרוזת מכשיר טלפון); 

כעת נוכל לשכתב את שיטת העדכון שלנו:

עדכון חלל ציבורי CustomerContacts (מזהה ארוך, טלפון מחרוזת) {repo.updatePhone (מזהה, טלפון); } 

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

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

6. מסקנה

עדכון הנתונים החלקי הוא פעולה מהותית למדי; בעוד שאנו יכולים לקבל את ה- ORM שלנו לטפל בזה, לפעמים זה יכול להיות רווחי לקבל שליטה מלאה עליו.

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

כרגיל, קוד המקור של מאמר זה זמין ב- GitHub.


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