יוצאים מהכלל במצב חורף

1. הקדמה

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

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

2. סקירה כללית על חריגות שינה

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

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

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

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

בואו נסתכל על כל אחד מאלה, אחד בכל פעם.

3. שגיאות מיפוי

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

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

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

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

בואו נסתכל על שגיאות אלה עם כמה דוגמאות.

3.1. MappingException

בעיה במיפוי התייחסות האובייקט גורם ל MappingException להיזרק:

חלל ציבורי כאשר QueryExecutedWithUnmappedEntity_thenMappingException () {throw.expectCause (isA (MappingException.class)); throw.expectMessage ("ישות לא ידועה: java.lang.String"); מושב מושב = sessionFactory.getCurrentSession (); שאילתת NativeQuery = session .createNativeQuery ("בחר שם מ- PRODUCT", String.class); query.getResultList (); }

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

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

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

באופן דומה, שגיאות אחרות יכולות גם לגרום לחריג זה:

  • ערבוב הערות על שדות ושיטות
  • לא מציין את @JoinTable למשך @ManyToMany אִרגוּן
  • קונסטרוקטור ברירת המחדל של המחלקה הממופה מציג חריג במהלך עיבוד המיפוי

יתר על כן, MappingException יש כמה מחלקות משנה שיכולות להצביע על בעיות מיפוי ספציפיות:

  • ביאור חריג - בעיה בהערה
  • DuplicateMappingException - מיפוי כפול עבור שם מחלקה, טבלה או נכס
  • חריג מיפוי לא חוקי - המיפוי אינו חוקי
  • MappingNotFoundException - לא ניתן למצוא משאב מיפוי
  • PropertyNotFoundException - לא ניתן למצוא כיתה או שיטת קובע בכיתה

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

3.2. ביאור חריג

כדי להבין את ביאור יוצא מן הכלל, בואו ניצור ישות ללא ביאור מזהה בשדה או בנכס כלשהו:

@Entity מחלקה ציבורית EntityWithNoId {מזהה פרטי פרטי; public int getId () {return id; } // מגדיר סטנדרטי}

מאז תרדמת שינה מצפה שלכל ישות יהיה מזהה, נקבל ביאור חריג כאשר אנו משתמשים בישות:

חלל ציבורי givenEntityWithoutId_whenSessionFactoryCreated_thenAnnotationException () {throw.expect (AnnotationException.class); throw.expectMessage ("לא צוין מזהה ליישות"); תצורה cfg = getConfiguration (); cfg.addAnnotatedClass (EntityWithNoId.class); cfg.buildSessionFactory (); }

יתר על כן, כמה סיבות אפשריות אחרות הן:

  • מחולל רצפים לא ידוע המשמש ב- @ GeneratedValue ביאור
  • @זְמַנִי הערה המשמשת עם Java 8 תַאֲרִיך/זְמַן מעמד
  • ישות יעד חסרה או לא קיימת עבור @ManyToOne אוֹ @אחד לרבים
  • שיעורי אוספים גולמיים המשמשים ביאורים על יחסים @אחד לרבים אוֹ @ManyToMany
  • שיעורי בטון המשמשים ביאורי האוסף @אחד לרבים, @ManyToMany אוֹ @ElementCollection כפי ש- Hibernate מצפה לממשקי האוסף

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

4. שגיאות ניהול סכמות

ניהול סכימות מסד נתונים אוטומטי הוא יתרון נוסף של מצב שינה. לדוגמה, הוא יכול ליצור הצהרות DDL כדי ליצור או לאמת אובייקטים של מסדי נתונים.

כדי להשתמש בתכונה זו, עלינו להגדיר את ה- hibernate.hbm2ddl.auto נכס כראוי.

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

4.1. SchemaManagementException

כל בעיה הקשורה בתשתית בביצוע ניהול סכמות גורמת ל- SchemaManagementException.

כדי להדגים, בוא ננחה את מצב שינה לאמת את סכימת מסד הנתונים:

חלל ציבורי givenMissingTable_whenSchemaValidated_thenSchemaManagementException () {throw.expect (SchemaManagementException.class); throw.expectMessage ("אימות סכמה: טבלה חסרה"); תצורה cfg = getConfiguration (); cfg.setProperty (AvailableSettings.HBM2DDL_AUTO, "תקף"); cfg.addAnnotatedClass (Product.class); cfg.buildSessionFactory (); }

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

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

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

4.2. CommandAcceptanceException

כל בעיה בהפעלת DDL המתאים לפקודה ספציפית לניהול סכמות עלולה לגרום ל- CommandAcceptanceException.

כדוגמה, בואו נציין את הניב הלא נכון בזמן הגדרת ה- SessionFactory:

חלל ציבורי כאשר WrongDialectSpecified_thenCommandAcceptanceException () {throw.expect (SchemaManagementException.class); throw.expectCause (isA (CommandAcceptanceException.class)); throw.expectMessage ("עצירה בשגיאה: שגיאה בביצוע DDL"); תצורה cfg = getConfiguration (); cfg.setProperty (AvailableSettings.DIALECT, "org.hibernate.dialect.MySQLDialect"); cfg.setProperty (AvailableSettings.HBM2DDL_AUTO, "עדכון"); cfg.setProperty (AvailableSettings.HBM2DDL_HALT_ON_ERROR, "נכון"); cfg.getProperties () .put (AvailableSettings.HBM2DDL_HALT_ON_ERROR, נכון); cfg.addAnnotatedClass (Product.class); cfg.buildSessionFactory (); }

כאן ציינו את הניב הלא נכון: MySQLDialect. כמו כן, אנו מורים ל- Hibernate לעדכן את אובייקטי הסכימה. כתוצאה מכך, הצהרות DDL שבוצעו על ידי Hibernate לעדכון מסד הנתונים H2 ייכשלו ונקבל חריג.

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

כדי להבטיח חריג שייגרם לשגיאה זו, הגדרנו את הנכס HBM2DDL_HALT_ON_ERROR ל נָכוֹן.

באופן דומה, אלה כמה סיבות נפוצות אחרות לשגיאה זו:

  • יש חוסר התאמה בשמות העמודות בין המיפוי למסד הנתונים
  • שתי כיתות ממופות לאותו טבלה
  • השם המשמש בכיתה או בטבלה הוא מילה שמורה במסד הנתונים, כמו מִשׁתַמֵשׁ, לדוגמה
  • המשתמש המשמש להתחברות למסד הנתונים אינו בעל ההרשאה הנדרשת

5. שגיאות ביצוע SQL

כאשר אנו מכניסים, מעדכנים, מוחקים או מבצעים שאילתות באמצעות Hibernate, הוא מבצע הצהרות DML כנגד מסד הנתונים באמצעות JDBC. ממשק API זה מעלה SQLException אם הפעולה מביאה לשגיאות או אזהרות.

מצב שינה ממיר את החריג הזה ל תפיסת JDBCE או אחת מתתי הסוגים המתאימים שלה:

  • ConstraintViolationException
  • DataException
  • JDBCConnectionException
  • LockAcquisitionException
  • פסימי נעילת נעילה
  • QueryTimeoutException
  • SQLGrammarException
  • כללי JDBCE

בואו נדון בשגיאות נפוצות.

5.1. תפיסת JDBCE

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

יתר על כן, אנו יכולים לאחזר את הבסיס SQLException עם ה getSQLException שיטה.

5.2. SQLGrammarException

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

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

חלל ציבורי givenMissingTable_whenQueryExecuted_thenSQLGrammarException () {throw.expect (isA (PersistenceException.class)); throw.expectCause (isA (SQLGrammarException.class)); throw.expectMessage ("SQLGrammarException: לא יכול היה להכין הצהרה"); מושב מושב = sessionFactory.getCurrentSession (); שאילתת NativeQuery = session.createNativeQuery ("בחר * מתוך NON_EXISTING_TABLE", Product.class); query.getResultList (); }

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

חלל ציבורי givenMissingTable_whenEntitySaved_thenSQLGrammarException () {throw.expect (isA (PersistenceException.class)); throw.expectCause (isA (SQLGrammarException.class)); נזרק .expectMessage ("SQLGrammarException: לא יכול היה להכין הצהרה"); תצורה cfg = getConfiguration (); cfg.addAnnotatedClass (Product.class); SessionFactory sessionFactory = cfg.buildSessionFactory (); מושב מושב = null; עסקת עסקה = אפס; נסה את {session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר מוצר = מוצר חדש (); product.setId (1); product.setName ("מוצר 1"); session.save (מוצר); transaction.commit (); } לתפוס (חריג e) {rollbackTransactionQuietly (עסקה); לזרוק (ה); } סוף סוף {closeSessionQuietly (session); closeSessionFactoryQuietly (sessionFactory); }}

גורמים אפשריים אחרים הם:

  • אסטרטגיית השמות בה נעשה שימוש אינה ממפה את הכיתות לטבלאות הנכונות
  • העמודה שצוינה ב @JoinColumn לא קיים

5.3. ConstraintViolationException

א ConstraintViolationException מציין כי פעולת ה- DML המבוקשת גרמה להפרת מגבלת תקינות. אנו יכולים לקבל את שם האילוץ הזה על ידי קריאת ה- getConstraintName שיטה.

סיבה נפוצה לחריג זה היא ניסיון לשמור רשומות כפולות:

חלל ציבורי כאשרDuplicateIdSaved_thenConstraintViolationException () {throw.expect (isA (PersistenceException.class)); throw.expectCause (isA (ConstraintViolationException.class)); throw.expectMessage ("ConstraintViolationException: לא יכול היה לבצע הצהרה"); מושב מושב = null; עסקת עסקה = אפס; עבור (int i = 1; i <= 2; i ++) {נסה {session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר מוצר = מוצר חדש (); product.setId (1); product.setName ("מוצר" + i); session.save (מוצר); transaction.commit (); } לתפוס (חריג e) {rollbackTransactionQuietly (עסקה); לזרוק (ה); } סוף סוף {closeSessionQuietly (session); }}}

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

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

5.4. DataException

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

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

חלל ציבורי givenQueryWithDataTypeMismatch_WhenQueryExecuted_thenDataException () {throw.expectCause (isA (DataException.class)); throw.expectMessage ("org.hibernate.exception.DataException: לא הצליח להכין הצהרה"); מושב מושב = sessionFactory.getCurrentSession (); שאילתת NativeQuery = session.createNativeQuery ("בחר * מתוך PRODUCT איפה", Product.class); query.getResultList (); }

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

5.5. JDBCConnectionException

א JDBCConectionException מציין בעיות בתקשורת עם מסד הנתונים.

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

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

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

5.6. QueryTimeoutException

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

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

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

  • הגדר את פסק זמן אלמנט בא @NamedQuery אוֹ @NamedNativeQuery ביאור
  • קרא את setHint שיטה של השאילתה מִמְשָׁק
  • תתקשר ל setTimeout שיטת ה- עִסקָה מִמְשָׁק
  • קרא את setTimeout שיטת ה- שאילתא מִמְשָׁק

6. שגיאות הקשורות למושב

בואו נבדוק כעת שגיאות עקב שגיאות שימוש במצב שינה.

6.1. NonUniqueObjectException

מצב שינה אינו מאפשר שני אובייקטים עם אותו מזהה בפגישה אחת.

אם ננסה לשייך שני מופעים מאותה מחלקת Java לאותו מזהה בסשן אחד, נקבל NonUniqueObjectException. אנו יכולים לקבל את השם והמזהה של הישות על ידי קריאה ל- getEntityName () ו getIdentifier () שיטות.

כדי לשחזר שגיאה זו, ננסה לשמור שני מופעים של מוצר עם אותו מזהה עם הפעלה:

חלל ציבורי givenSessionContainingAnId_whenIdAssociatedAgain_thenNonUniqueObjectException () {throw.expect (isA (NonUniqueObjectException.class)); throw.expectMessage ("אובייקט אחר עם אותו ערך מזהה כבר היה משויך להפעלה"); מושב מושב = null; עסקת עסקה = אפס; נסה את {session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר מוצר = מוצר חדש (); product.setId (1); product.setName ("מוצר 1"); session.save (מוצר); מוצר = מוצר חדש (); product.setId (1); product.setName ("מוצר 2"); session.save (מוצר); transaction.commit (); } לתפוס (חריג e) {rollbackTransactionQuietly (עסקה); לזרוק (ה); } סוף סוף {closeSessionQuietly (session); }}

נקבל NonUniqueObjectException, כצפוי.

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

6.2. StaleStateException

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

לפעמים זה נעטף לתוך OptimisticLockException.

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

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

חלל ציבורי כאשרUpdatingNonExistingObject_thenStaleStateException () {throw.expect (isA (OptimisticLockException.class)); throw.expectMessage ("עדכון אצווה החזיר ספירת שורות לא צפויה מהעדכון"); throw.expectCause (isA (StaleStateException.class)); מושב מושב = null; עסקת עסקה = אפס; נסה את {session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר מוצר = מוצר חדש (); product.setId (15); product.setName ("Product1"); session.update (מוצר); transaction.commit (); } לתפוס (חריג e) {rollbackTransactionQuietly (עסקה); לזרוק (ה); } סוף סוף {closeSessionQuietly (session); }}

כמה תרחישים אפשריים אחרים הם:

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

7. שגיאות אתחול עצלן

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

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

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

7.1. LazyInitializationException

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

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

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

ישנן דרכים רבות לפתור חריג זה.

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

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

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

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

7.2. אתחול מערכות יחסים עצלניות רלוונטיות ברובד העסקי

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

אפשרות אחת היא לאתחל אותם על ידי הפעלת השיטות המתאימות לישות. במקרה זה, Hibernate תנפיק מספר שאילתות מסדי נתונים הגורמות לביצועים מושפלים. אנו מכנים אותה כבעיית "N + 1 SELECT".

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

סוף כל סוף, אנו יכולים להשתמש בגרפים של ישויות כדי להגדיר את כל התכונות שיש להביא. אנחנו יכולים להשתמש בהערות @NamedEntityGraph, @NamedAttributeNode, ו @NamedEntitySubgraph להגדרת הצהרת גרף היישות. אנו יכולים גם להגדיר אותם באופן פרוגרמטי באמצעות JPA API. לאחר מכן, אנו מאחזרים את כל הגרף בשיחה אחת על ידי ציון פעולת האחזור.

8. בעיות עסקה

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

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

בדרך כלל אנו מקבלים א TransactionException או IllegalArgumentException תלוי במנהל העסקאות.

כהמחשה, בואו ננסה לבצע עסקה שסומנה להחזר:

חלל ציבורי givenTxnMarkedRollbackOnly_whenCommitted_thenTransactionException () {throw.expect (isA (TransactionException.class)); throw.expectMessage ("העסקה סומנה להחזרה בלבד; לא יכולה להתחייב"); מושב מושב = null; עסקת עסקה = אפס; נסה את {session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר מוצר = מוצר חדש (); product.setId (15); product.setName ("Product1"); session.save (מוצר); transaction.setRollbackOnly (); transaction.commit (); } לתפוס (חריג e) {rollbackTransactionQuietly (עסקה); לזרוק (ה); } סוף סוף {closeSessionQuietly (session); }}

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

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

9. סוגיות במקביל

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

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

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

חלל ציבורי כאשר מחיקת מחיקה אובייקט_אז אופטימיסטית נעילה () {זרוק.expect (isA (OptimisticLockException.class)); throw.expectMessage ("עדכון אצווה החזיר ספירת שורות לא צפויה מהעדכון"); throw.expectCause (isA (StaleStateException.class)); מושב מושב = null; עסקת עסקה = אפס; נסה את {session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר מוצר = מוצר חדש (); product.setId (12); product.setName ("מוצר 12"); session.save (product1); transaction.commit (); session.close (); session = sessionFactory.openSession (); עסקה = session.beginTransaction (); מוצר = session.get (Product.class, 12); session.createNativeQuery ("מחק מהמוצר שבו id = 12") .executeUpdate (); // עלינו לרענן כדי לתקן את השגיאה. // session.refresh (מוצר); session.delete (מוצר); transaction.commit (); } לתפוס (חריג e) {rollbackTransactionQuietly (עסקה); לזרוק (ה); } סוף סוף {closeSessionQuietly (session); }}

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

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

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

10. מסקנה

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

כרגיל, ניתן למצוא את קוד המקור המלא ב- GitHub.


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