סקירה כללית של מזהים במצב שינה / JPA

1. הקדמה

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

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

2. מזהים פשוטים

הדרך הכי פשוטה להגדיר מזהה היא באמצעות @תְעוּדַת זֶהוּת ביאור.

מזהים פשוטים ממופים באמצעות @תְעוּדַת זֶהוּת למאפיין יחיד של אחד מהסוגים האלה: סוגי עטיפה פרימיטיביים ופרימיטיביים, מחרוזת, תאריך, BigDecimal, BigInteger.

בואו נראה דוגמה מהירה להגדרת ישות עם מפתח ראשי מסוג ארוך:

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

3. מזהים שנוצרו

אם אנו רוצים שערך המפתח הראשי ייווצר אוטומטית עבורנו, אנחנו יכולים להוסיף את @ GeneratedValue ביאור.

זה יכול להשתמש בארבעה סוגי דור: AUTO, IDENTITY, SEQUENCE, TABLE.

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

3.1. אוטומטי דוֹר

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

עבור ערכים מספריים, הדור מבוסס על רצף או מחולל טבלה, בעוד UUID ערכים ישתמשו ב- UUIDGenerator.

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

סטודנט בכיתה ציבורית @Entity {@Id @GeneratedValue סטודנט ארוך סטודנט; // ...}

במקרה זה, ערכי המפתח העיקריים יהיו ייחודיים ברמת מסד הנתונים.

תכונה מעניינת שהוצגה ב- Hibernate 5 היא UUIDGenerator. כדי להשתמש בזה, כל שעלינו לעשות הוא להכריז על מזהה מסוג UUID עם @ GeneratedValue ביאור:

קורס בכיתה ציבורית @Entity {@Id @GeneratedValue קורס UUID פרטי; // ...}

תרדמת שינה תיצור מזהה של הטופס "8dd5f315-9788-4d00-87bb-10eed9eff566".

3.2. זהות דוֹר

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

כדי להשתמש בסוג דור זה, עלינו רק להגדיר את ה- אִסטרָטֶגִיָה פָּרָמֶטֶר:

סטודנט בכיתה ציבורית @Entity {@Id @GeneratedValue (אסטרטגיה = GenerationType.IDENTITY) סטודנט ארוך פרטית; // ...}

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

3.3. סדר פעולות דוֹר

כדי להשתמש במזהה מבוסס רצף, Hibernate מספק את ה- SequenceStyleGenerator מעמד.

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

כדי להתאים אישית את שם הרצף, אנו יכולים להשתמש ב- @GenericGenerator ביאור עם אסטרטגיית SequenceStyleGenerator:

משתמש בכיתה ציבורית @Entity {@Id @GeneratedValue (מחולל = "מחולל רצפים") @ GenericGenerator (שם = "מחולל רצפים", אסטרטגיה = "org.hibernate.id. שם = "רצף_שם", ערך = "תוצאה של משתמש"), @Parameter (שם = "ערך_תחלתי", ערך = "4"), @Parameter (name = "increment_size", value = "1")}) userId פרטי ארוך; // ...}

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

סדר פעולות הוא סוג הדור המומלץ על ידי תיעוד ה- Hibernate.

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

3.4. דור הטבלה

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

בואו להתאים אישית את שם הטבלה באמצעות ה- @TableGenerator ביאור:

מחלקת מחלקה ציבורית @Entity {@Id @GeneratedValue (אסטרטגיה = GenerationType.TABLE, מחולל = "מחולל טבלה") @ TableGenerator (שם = "מחולל טבלה", טבלה = "dep_ids", pkColumnName = "seq_id", valueColumnName = "seq_value") פרטי ארוך depId; // ...}

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

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

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

3.5. גנרטור מותאם אישית

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

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

מחלקה ציבורית MyGenerator מיישם את IdentifierGenerator, ניתן להגדרה {קידומת מחרוזת פרטית; @Override ציבורי שניתן ליצור באמצעות Serial (הפעלת SharedSessionContractImplementor, אובייקט אובייקט) זורק HibernateException {String query = String.format ("בחר% s מ-% s", session.getEntityPersister (obj.getClass (). GetName (), obj) .getIdentifierPropertyName ( ), obj.getClass (). getSimpleName ()); הזרמת מזהים = session.createQuery (שאילתה) .stream (); מקסימום ארוך = ids.map (o -> o.replace (קידומת + "-", "")) .mapToLong (Long :: parseLong) .max () .orElse (0L); קידומת החזרה + "-" + (מקסימום + 1); } תצורת הריקות הציבורית של @Override (סוג סוג, מאפייני מאפיינים, ServiceRegistry serviceRegistry) זורק MappingException {prefix = properties.getProperty ("קידומת"); }}

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

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

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

לאחר מכן, בואו להוסיף את הגנרטור המותאם אישית ליישות. לזה, אנחנו יכולים להשתמש ב- @GenericGenerator ביאור עם א אִסטרָטֶגִיָה פרמטר המכיל את שם המחלקה המלא של מחלקת הגנרטורים שלנו:

מוצר בכיתה ציבורית @Entity {@Id @GeneratedValue (generator = "prod-generator") @ GenericGenerator (name = "prod-generator", parameters = @Parameter (name = "prefix", value = "prod"), אסטרטגיה = "com.baeldung.hibernate.pojo.generator.MyGenerator") פרטי מחרוזת prodId; // ...}

שים לב שהגדרנו את פרמטר הקידומת ל- "prod".

בואו נראה מבחן JUnit מהיר להבנה ברורה יותר של ערכי ה- ID שנוצרו:

@ מבחן ציבורי בטל כאשרSaveCustomGeneratedId_thenOk () {מוצר מוצר = מוצר חדש (); session.save (מוצר); מוצר מוצר 2 = מוצר חדש (); session.save (product2); assertThat (product2.getProdId ()). isEqualTo ("prod-2"); }

כאן, הערך הראשון שנוצר באמצעות הקידומת "prod" היה "prod-1", ואחריו "prod-2".

4. מזהים מורכבים

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

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

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

  • יש להגדיר זאת באמצעות @ EmbeddedId אוֹ @IdClass ביאורים
  • זה צריך להיות ציבורי, לסידור ולהיות בעל מבנה ציבורי ללא טענות
  • זה צריך ליישם שווים() ו hashCode () שיטות

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

4.1. @ EmbeddedId

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

@Embeddable בכיתה ציבורית OrderEntryPK מיישם Serializable {פרטי ארוך OrderId; productId פרטי ארוך; // קונסטרוקטור סטנדרטי, גטררים, סטרים // שווה () ו- hashCode ()}

לאחר מכן נוכל להוסיף מזהה מסוג OrderEntryPK לישות המשתמשת ב- @EmbeddedId:

@Entity מחלקה ציבורית OrderEntry {@EmbeddedId פרטי OrderEntryPK entryId; // ...}

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

@ מבחן ציבורי בטל כאשרSaveCompositeIdEntity_thenOk () {OrderEntryPK entryPK = OrderEntryPK חדש (); entryPK.setOrderId (1L); entryPK.setProductId (30L); הזנת OrderEntry = OrderEntry חדש (); entry.setEntryId (entryPK); session.save (ערך); assertThat (entry.getEntryId (). getOrderId ()). isEqualTo (1L); }

הנה ה הזנת הזמנות לאובייקט יש OrderEntryPK מזהה ראשוני המורכב משתי תכונות: מספר הזמנה ו מזהה מוצר.

4.2. @IdClass

ה @IdClass ההערה דומה ל- @ EmbeddedId, למעט התכונות מוגדרות במחלקת הישות הראשית באמצעות @תְעוּדַת זֶהוּת לכל אחד.

כיתת המפתח הראשי תיראה זהה לקודם.

בואו נכתוב מחדש את הזנת הזמנות דוגמה עם @IdClass:

@Entity @IdClass (OrderEntryPK.class) מחלקה ציבורית OrderEntry {@Id OrderId פרטי ארוך; @Id מוצר ארוך פרטי; // ...}

אז נוכל להגדיר את ערכי ה- id ישירות על ה- הזנת הזמנות לְהִתְנַגֵד:

@Test ציבורי בטל כאשרSaveIdClassEntity_thenOk () {OrderEntry entry = OrderEntry חדש (); entry.setOrderId (1L); entry.setProductId (30L); session.save (ערך); assertThat (entry.getOrderId ()). isEqualTo (1L); }

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

מצב שינה מאפשר גם הגדרת מקשים ראשיים המורכבים מ @ManyToOne עמותות בשילוב עם @תְעוּדַת זֶהוּת ביאור. במקרה זה, מחלקת הישות צריכה למלא גם את התנאים של מחלקה ראשונית.

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

5. מזהים נגזרים

מזהים נגזרים מתקבלים מאיגוד של ישות באמצעות ה- @MapsId ביאור.

ראשית, בואו ניצור a פרופיל משתמש ישות אשר שואבת את זהותה מקשר אחד לאחד עם מִשׁתַמֵשׁ יֵשׁוּת:

פרופיל משתמש בכיתה ציבורית @Entity {@ Id פרטי פרופיל ארוך; משתמש משתמש פרטי @OneToOne @MapsId; // ...}

לאחר מכן, בואו נוודא שא פרופיל משתמש למזהה אותו מזהה לזה המשויך מִשׁתַמֵשׁ למשל:

@ מבחן ציבורי בטל כאשרSaveDerivedIdEntity_thenOk () {משתמש משתמש = משתמש חדש (); session.save (משתמש); פרופיל UserProfile = UserProfile חדש (); profile.setUser (משתמש); session.save (פרופיל); assertThat (profile.getProfileId ()). isEqualTo (user.getUserId ()); }

6. מסקנה

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

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