תרדמת שינה: שמור, התמיד, עדכן, מיזוג, שמור או עדכון

1. הקדמה

במאמר זה נדון בהבדלים בין מספר שיטות של מוֹשָׁב מִמְשָׁק: לשמור, להתמיד, עדכון, לְמַזֵג, saveOrUpdate.

זה לא מבוא ל- Hibernate וכדאי כבר לדעת את היסודות של תצורה, מיפוי יחסי אובייקטים ועבודה עם מופעי ישויות. לקבלת מאמר היכרות ל- Hibernate, בקרו במדריך שלנו בנושא Hibernate 4 with Spring.

2. מושב כהקשר התמדה יישום

ה מוֹשָׁב לממשק יש כמה שיטות שבסופו של דבר גורמות לשמירת נתונים במסד הנתונים: להתמיד, לשמור, עדכון, לְמַזֵג, saveOrUpdate. כדי להבין את ההבדל בין שיטות אלה, עלינו לדון תחילה במטרת ה מוֹשָׁב כהקשר התמדה וההבדל בין מצבי ישויות המקרים ביחס ל מוֹשָׁב.

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

2.1. ניהול מופעי ישויות

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

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

במצב שינה, הקשר ההתמדה מיוצג על ידי org.hibernate.Session למשל. עבור JPA, זה ה- javax.persistence.EntityManager. כאשר אנו משתמשים ב- Hibernate כספק JPA ופועלים באמצעות EntityManager ממשק, היישום של ממשק זה בעצם עוטף את הבסיס מוֹשָׁב לְהִתְנַגֵד. עם זאת, שינה מוֹשָׁב מספק ממשק עשיר יותר עם אפשרויות רבות יותר ולכן לפעמים כדאי לעבוד איתו מושב ישירות.

2.2. מקרים של מדינות ישויות

כל מופע של יישות ביישום שלך מופיע באחת משלוש המדינות העיקריות ביחס ל- מוֹשָׁב הקשר התמדה:

  • חולף - מקרה זה לא קשור, ומעולם לא, מוֹשָׁב; למופע זה אין שורות תואמות במסד הנתונים; זה בדרך כלל רק אובייקט חדש שיצרת כדי לשמור במסד הנתונים;
  • מַתְמִיד - מקרה זה נקשר עם ייחודי מוֹשָׁב לְהִתְנַגֵד; עם שטיפת ה- מוֹשָׁב למסד הנתונים מובטח כי ישות זו תהיה רשומה עקבית תואמת במסד הנתונים;
  • מְנוּתָק - מקרה זה הוצמד פעם ל- מוֹשָׁבמַתְמִיד המדינה), אבל עכשיו זה לא; מופע נכנס למצב זה אם אתה מגרש אותו מההקשר, מנקה או סוגר את ההפעלה, או מעביר את המופע בתהליך סידורי / עריק.

להלן תרשים מצב פשוט עם הערות מוֹשָׁב שיטות שגורמות למעברי המדינה לקרות.

כאשר מופע הישות נמצא ב- מַתְמִיד המדינה, כל השינויים שתבצע בשדות הממופים של מופע זה יחולו על רשומות ושדות הנתונים המתאימים עם שטיפת ה- מוֹשָׁב. ה מַתְמִיד ניתן לחשוב על מופע "מקוון", ואילו מְנוּתָק מופע "לא מקוון" ואינו מנוטר אחר שינויים.

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

2.3. תאימות למפרט JPA

מצב שינה היה ההטמעה המוצלחת ביותר של Java ORM. לא פלא שהמפרט עבור API להתמדה של Java (JPA) הושפע רבות מ- API Hibernate. למרבה הצער, היו גם הבדלים רבים: חלקם גדולים, חלקם עדינים יותר.

כדי לשמש כמימוש של תקן JPA, היה צורך לשנות את ממשקי ה- API של Hibernate. לממשק Session נוספו כמה שיטות כדי להתאים לממשק EntityManager. שיטות אלה משרתות את אותה מטרה כמו השיטות "המקוריות", אך תואמות את המפרט ובכך יש כמה הבדלים.

3. הבדלים בין הפעולות

חשוב להבין מההתחלה שכל השיטות (להתמיד, לשמור, עדכון, לְמַזֵג, saveOrUpdate) אל תביא באופן מיידי ל- SQL המתאים עדכון אוֹ לְהַכנִיס הצהרות. השמירה בפועל של נתונים במסד הנתונים מתבצעת בעת ביצוע העסקה או שטיפה מוֹשָׁב.

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

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

אדם בכיתה ציבורית @Entity {@Id @GeneratedValue פרטי מזהה ארוך; שם מחרוזת פרטי; // ... גטרים וקובעים}

3.1. להתמיד

ה להתמיד השיטה מיועדת להוספת מופע ישות חדש להקשר ההתמדה, כלומר לעבור מופע מחולף ל מַתְמִיד מדינה.

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

אדם אדם = אדם חדש (); person.setName ("ג'ון"); session.persist (אדם);

מה קורה אחרי להתמיד שיטה נקראת? ה אדם האובייקט עבר מ חולף ל מַתְמִיד מדינה. האובייקט נמצא בהקשר ההתמדה כעת, אך עדיין לא נשמר במסד הנתונים. הדור של לְהַכנִיס הצהרות יתרחשו רק עם ביצוע העסקה, שטיפה או סגירת הפגישה.

שימו לב שה- להתמיד לשיטה יש בָּטֵל סוג החזרה. הוא פועל על האובייקט שעבר "במקום", ומשנה את מצבו. ה אדם משתנה מפנה לאובייקט המתמשך בפועל.

שיטה זו היא תוספת מאוחרת יותר לממשק Session. המאפיין המבדיל העיקרי של שיטה זו הוא שהיא תואמת את המפרט JSR-220 (התמדה של EJB). הסמנטיקה של שיטה זו מוגדרת בקפדנות במפרט, הקובע בעצם כי:

  • א חולף מופע הופך מַתְמִיד (והמבצע נופל על כל קשריו עם מפל = PERSIST אוֹ מפל = הכל),
  • אם מופע כבר קיים מַתְמִיד, אז לקריאה זו אין כל השפעה על מקרה מסוים זה (אך היא עדיין נופלת ליחסיו עם מפל = PERSIST אוֹ מפל = הכל),
  • אם מופע הוא מְנוּתָק, אתה צריך לצפות לחריג, אם קוראים לשיטה זו או עם ביצוע הסמכה או שטיפה.

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

אתה יכול לקרוא לשיטה זו כבר מַתְמִיד למשל, ושום דבר לא קורה. אבל אם אתה מנסה להתמיד א מְנוּתָק למשל, היישום חייב להטיל חריג. בדוגמה הבאה אנו להתמיד הישות, לְפַנוֹת זה מההקשר כך שהוא הופך להיות מְנוּתָקואז נסה להתמיד שוב. השיחה השנייה ל session.persist () גורם לחריג, ולכן הקוד הבא לא יעבוד:

אדם אדם = אדם חדש (); person.setName ("ג'ון"); session.persist (אדם); session.evict (אדם); session.persist (אדם); // חריגות התמדה!

3.2. להציל

ה לשמור שיטה היא שיטת שינה "מקורית" שאינה תואמת את המפרט JPA.

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

אדם אדם = אדם חדש (); person.setName ("ג'ון"); מזהה ארוך = (ארוך) session.save (אדם);

ההשפעה של שמירת מופע שנמשך כבר זהה לזו של להתמיד. ההבדל מגיע כשאתה מנסה לשמור א מְנוּתָק למשל:

אדם אדם = אדם חדש (); person.setName ("ג'ון"); ארוך id1 = (ארוך) session.save (אדם); session.evict (אדם); ארוך id2 = (ארוך) session.save (אדם);

ה id2 משתנה יהיה שונה מ id1. קריאת השמירה על א מְנוּתָק מופע יוצר חדש מַתְמִיד למשל ומקצה לו מזהה חדש, שמביא לתיעוד כפול במסד נתונים עם ביצוע או שטיפה.

3.3. לְמַזֵג

הכוונה העיקרית של לְמַזֵג השיטה היא לעדכן א מַתְמִיד מופע ישות עם ערכי שדה חדשים מ- מְנוּתָק מופע ישות.

לדוגמה, נניח שיש לך ממשק RESTful עם שיטה לאחזור אובייקט מסדרת JSON על ידי המזהה שלו למתקשר ושיטה המקבלת גרסה מעודכנת של האובייקט הזה מהמתקשר. ישות שעברה סדרת / עריק כזה, תופיע ב- מְנוּתָק מדינה.

לאחר ערעור מחדש של מופע ישות זה, עליך להשיג מַתְמִיד מופע ישות מהקשר התמדה ועדכן את שדותיו בערכים חדשים מזה מְנוּתָק למשל. אז ה לְמַזֵג השיטה עושה בדיוק את זה:

  • מוצא מופע של ישות לפי מזהה שנלקח מהאובייקט שהועבר (או שמא מופע ישות קיים מהקשר ההתמדה או שהוא אוחזר מופע חדש ממסד הנתונים);
  • מעתיק שדות מהאובייקט שהועבר למופע זה;
  • מחזיר מופע חדש.

בדוגמה הבאה אנו לְפַנוֹת (לנתק) את הישות השמורה מההקשר, לשנות את שֵׁם שדה, ואז לְמַזֵג ה מְנוּתָק יֵשׁוּת.

אדם אדם = אדם חדש (); person.setName ("ג'ון"); session.save (אדם); session.evict (אדם); person.setName ("מרי"); Person mergedPerson = (Person) session.merge (person);

שים לב שה- לְמַזֵג השיטה מחזירה אובייקט - הוא ה mergedPerson אובייקט שהועמס בהקשר ההתמדה ועדכן, ולא האובייקט אדם חפץ שהעברת כוויכוח. אלה שני עצמים שונים, וה אדם בדרך כלל צריך להשליך את האובייקט (בכל מקרה, אל תסמוך שהוא יצורף להקשר ההתמדה).

כמו עם להתמיד השיטה, לְמַזֵג השיטה מוגדרת על ידי JSR-220 כדי שיהיה לך סמנטיקה מסוימת שאתה יכול לסמוך עליה:

  • אם הישות היא מְנוּתָק, הוא מועתק על גבי קיים מַתְמִיד יֵשׁוּת;
  • אם הישות היא חולף, הוא מועתק על גבי חדש שנוצר מַתְמִיד יֵשׁוּת;
  • פעולה זו מפליגה על כל היחסים עם מפל = מיזוג אוֹ מפל = הכל מיפוי;
  • אם הישות היא מַתְמִיד, אז לשיחת שיטה זו אין השפעה עליה (אך המדורגים עדיין מתקיימים).

3.4. עדכון

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

  • הוא פועל על אובייקט שעבר (סוג ההחזרה שלו הוא בָּטֵל); ה עדכון השיטה עוברת מהאובייקט שעבר מְנוּתָק ל מַתְמִיד מדינה;
  • שיטה זו מציגה חריג אם מעבירים אותה א חולף יֵשׁוּת.

בדוגמה הבאה אנו לשמור האובייקט, אם כן לְפַנוֹת (לנתק) את זה מההקשר ואז לשנות את שלו שֵׁם ולהתקשר עדכון. שימו לב שאנחנו לא שמים את התוצאה של עדכון פעולה במשתנה נפרד, מכיוון שה- עדכון מתרחש ב אדם החפץ עצמו. בעיקרון אנו מצרפים מחדש את מופע הישות הקיים להקשר ההתמדה - דבר שמפרט ה- JPA אינו מאפשר לנו לעשות.

אדם אדם = אדם חדש (); person.setName ("ג'ון"); session.save (אדם); session.evict (אדם); person.setName ("מרי"); session.update (אדם);

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

אדם אדם = אדם חדש (); person.setName ("ג'ון"); session.update (אדם); // חריגות התמדה!

3.5. שמור או עדכן

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

למעשה, הפנימי DefaultUpdateEventListener כיתה המעבדת את עדכון השיטה היא תת-מחלקה של DefaultSaveOrUpdateListener, פשוט עוקף פונקציונליות כלשהי. ההבדל העיקרי של saveOrUpdate השיטה היא שהיא אינה זורקת חריגה כאשר היא מוחלת על חולף למשל; במקום זאת, זה עושה את זה חולף למשל מַתְמִיד. הקוד הבא יימשך במופע החדש שנוצר של אדם:

אדם אדם = אדם חדש (); person.setName ("ג'ון"); session.saveOrUpdate (אדם);

אתה עשוי לחשוב על שיטה זו ככלי אוניברסלי ליצירת אובייקט מַתְמִיד בלי קשר למצב שלה חולף אוֹ מְנוּתָק.

4. במה להשתמש?

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

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

5. מסקנה

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

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