מדריך מהיר ל- EntityManager # getReference ()

1. הקדמה

ה getReference () שיטת ה- EntityManager class היה חלק ממפרט ה- JPA מאז הגרסה הראשונה. עם זאת, שיטה זו מבלבלת מפתחים מסוימים מכיוון שהתנהגותה משתנה בהתאם לספק ההתמדה הבסיסי.

במדריך זה, אנו הולכים להסביר כיצד להשתמש ב- getReference () שיטה ב מצב שינה EntityManager.

2. EntityManager להביא פעולות

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

2.1. למצוא()

למצוא() היא השיטה הנפוצה ביותר לאסוף ישויות:

משחק משחק = entityManager.find (Game.class, 1L); 

שיטה זו מאתחלת את היישות כשאנחנו מבקשים זאת.

2.2. getReference ()

דומה ל למצוא() שיטה, getReference () היא גם דרך נוספת לאחזור ישויות:

משחק משחק = entityManager.getReference (Game.class, 1L); 

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

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

3. מקרה שימוש לדוגמא

על מנת להפגין EntityManager להביא פעולות, ניצור שני מודלים, מִשְׂחָק ו שחקן, כתחום שלנו ששחקנים רבים יכולים להיות מעורבים באותו משחק.

3.1. מודל תחום

ראשית, נגדיר ישות שנקראת מִשְׂחָק:

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

לאחר מכן, אנו מגדירים את שלנו שחקן יֵשׁוּת:

@Entity Class Player נגן {@ Id פרטי מזהה ארוך; שם מחרוזת פרטי; // בונים סטנדרטיים, גטרים, סטרים} 

3.2. קביעת תצורה של מערכות יחסים

אנחנו צריכים להגדיר א @ManyToOne יחס מ שחקן ל מִשְׂחָק. אז בואו נוסיף a מִשְׂחָק רכוש שלנו שחקן יֵשׁוּת:

משחק משחק פרטי @ManyToOne; 

4. מקרי מבחן

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

entityManager.getTransaction (). start (); entityManager.persist (משחק חדש (1L, "משחק 1")); entityManager.persist (משחק חדש (2L, "משחק 2")); entityManager.persist (נגן חדש (1 ליטר, "נגן 1")); entityManager.persist (נגן חדש (2L, "Player 2")); entityManager.persist (נגן חדש (3L, "נגן 3")); entityManager.getTransaction (). commit (); 

בנוסף, כדי לבחון שאילתות SQL הבסיסיות, עלינו לעשות זאת להגדיר את מצב שינה hibernate.show_sql תכונה בשלנו התמדה.קסמל:

4.1. עדכון שדות ישויות

ראשית, נבדוק את הדרך הנפוצה ביותר לעדכון ישות באמצעות ה- למצוא() שיטה.

אז בואו נכתוב שיטת בדיקה להבאת ה- מִשְׂחָק תחילה, ואז פשוט עדכן את שֵׁם שדה:

משחק game1 = entityManager.find (Game.class, 1L); game1.setName ("המשחק עודכן 1"); entityManager.persist (game1); 

הפעלת שיטת הבדיקה מראה לנו את שאילתות ה- SQL המבוצעות:

שינה: בחר game0_.id כ- id1_0_0_, game0_.name כשם2_0_0_ מתוך game game0_ איפה game0_.id =? מצב שינה: עדכן שם ערכת המשחק =? איפה id =? 

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

אז בואו נראה איך ה getReference () שיטה מתנהגת באותו תרחיש:

משחק game1 = entityManager.getReference (Game.class, 1L); game1.setName ("המשחק עודכן 2"); entityManager.persist (game1); 

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

כפי שאנו רואים, מצב שינה אכן מבצע בחר שאילתה כאשר אנו משתמשים getReference () לעדכון שדה ישות.

לָכֵן, משתמש ב getReference () השיטה אינה מונעת את התוספת בחר שאילתה אם אנו מבצעים מגדיר כלשהו משדות ה- proxy של הישות.

4.2. מחיקת ישויות

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

נגדיר שתי שיטות בדיקה נוספות למחיקת a שחקן יֵשׁוּת:

Player player2 = entityManager.find (Player.class, 2L); entityManager.remove (player2); 
Player player3 = entityManager.getReference (Player.class, 3L); entityManager.remove (player3); 

הפעלת שיטות בדיקה אלה מציגה לנו את אותן השאילתות:

שינה: בחר שחקן 0_.id כ- id1_1_0_, player0_.game_id כ- game_id3_1_0_, player0_.name כשם2_1_0_, game1_.id כ- id1_0_1_, game1_.name כשם2_0_1_ מנגן Player0_ שמאל חיצוני הצטרף למשחק game1_ על player0_.game_id = משחק_ .id =? שינה: מחק מהנגן איפה id =? 

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

לָכֵן, אין הבדל אם אנחנו בוחרים getReference () אוֹ למצוא() כאשר אנו מוחקים ישות קיימת.

בשלב זה, אנו תוהים, כן getReference () לעשות בכלל את ההבדל? בואו נעבור לקשרי ישויות ונברר.

4.3. עדכון קשרי ישויות

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

בואו נוסיף שיטה נוספת להדגמת א שחקןהשתתפות א מִשְׂחָק פשוט על ידי עדכון שחקןשל מִשְׂחָק תכונה:

משחק game1 = entityManager.find (Game.class, 1L); Player player1 = entityManager.find (Player.class, 1L); player1.setGame (game1); entityManager.persist (player1); 

הפעלת המבחן נותנת לנו תוצאה דומה פעם נוספת ו אנחנו עדיין יכולים לראות את בחר שאילתות בעת השימוש ב- למצוא() שיטה:

שינה: בחר game0_.id כ- id1_0_0_, game0_.name כשם2_0_0_ מתוך game game0_ איפה game0_.id =? שינה: בחר שחקן 0_.id כ- id1_1_0_, player0_.game_id כ- game_id3_1_0_, player0_.name כשם2_1_0_, game1_.id כ- id1_0_1_, game1_.name כשם2_0_1_ מנגן Player0_ שמאל חיצוני הצטרף למשחק game1_ על player0_.game_id = משחק_ .id =? מצב שינה: עדכן את נגן המשחק game_id = ?, name =? איפה id =? 

עכשיו, בואו נגדיר בדיקה נוספת לראות איך getReference () השיטה עובדת במקרה זה:

משחק game2 = entityManager.getReference (Game.class, 2L); Player player1 = entityManager.find (Player.class, 1L); player1.setGame (game2); entityManager.persist (player1); 

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

שינה: בחר שחקן 0_.id כ- id1_1_0_, player0_.game_id כ- game_id3_1_0_, player0_.name כשם2_1_0_, game1_.id כ- id1_0_1_, game1_.name כשם2_0_1_ מנגן Player0_ שמאל חיצוני הצטרף למשחק game1_ על player0_.game_id = משחק_ .id =? מצב שינה: עדכן את נגן המשחק game_id = ?, name =? איפה id =? 

ואנחנו רואים, מצב שינה אינו מבצע א בחר שאילתה עבור מִשְׂחָק ישות כאשר אנו משתמשים getReference () הפעם.

אז זה נראה כמו נוהג טוב לבחור getReference () במקרה הזה. זה בגלל שפרוקסי מִשְׂחָק ישות מספיקה כדי ליצור את הקשר מה- שחקן הישות - מִשְׂחָק אין צורך לאתחל את הישות.

כתוצאה מכך, באמצעות getReference () יכול למנוע ביטולים מיותרים למסד הנתונים שלנו כאשר אנו מעדכנים את יחסי הישות.

5. מצב מטמון ברמה ראשונה במצב שינה

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

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

entityManager.getTransaction (). start (); entityManager.persist (משחק חדש (1L, "משחק 1")); entityManager.persist (נגן חדש (1 ליטר, "נגן 1")); entityManager.getTransaction (). commit (); entityManager.getTransaction (). start (); משחק game1 = entityManager.getReference (Game.class, 1L); Player player1 = entityManager.find (Player.class, 1L); player1.setGame (game1); entityManager.persist (player1); entityManager.getTransaction (). commit (); 

הפעלת הבדיקה מראה שרק שאילתת העדכון בוצעה:

מצב שינה: עדכן את נגן המשחק game_id = ?, name =? איפה id =? 

במקרה כזה, עלינו לשים לב לכך אנחנו לא רואים כאלה בחר שאילתות, אם אנו משתמשים למצוא() אוֹ getReference (). הסיבה לכך היא שהישויות שלנו נשמרות במטמון במטמון ברמה הראשונה של מצב שינה.

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

6. יישומי JPA שונים

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

על פי מפרט JPA 2, ספק ההתמדה רשאי לזרוק את EntityNotFoundException כאשר getReference () שיטה נקראת. לפיכך, זה עשוי להיות שונה עבור ספקי התמדה אחרים ואנחנו עשויים להיתקל בהם EntityNotFoundException כשאנחנו משתמשים getReference ().

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

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

במקרה כזה, אנו עשויים לשקול הגדרת ה- hibernate.jpa.compliance.proxy נכס ל נָכוֹן:

עם הגדרה זו, מצב שינה מאתחל את ה- proxy של היישות בכל מקרה, כלומר, הוא מבצע א בחר שאילתה גם כשאנחנו משתמשים getReference ().

7. מסקנה

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

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


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