JavaRush /בלוג Java /Random-HE /ישויות JPA וקשרי DB
Nikita Koliadin
רָמָה
Днепр

ישויות JPA וקשרי DB

פורסם בקבוצה

ישויות JPA && קשרי DB

יום טוב, עמיתים!
ישויות JPA ויחסי DB - 1
החומר הזה מיועד למי שכבר יש לו מושג על ארגון מסדי הנתונים (להלן פשוט DB - "מסד נתונים"), ידע מינימלי כיצד פועל מיפוי אובייקטים-יחסי (להלן פשוט ORM ), והטמעות שלו, כגון Hibernate / JPA . אם אתה לא מכיר את זה, אני ממליץ לך להתחיל עם JDBC , ורק אז לעבור למודל ORM. הזהרתי אותך, ואני לא נושא באחריות לנפשך לאחר קריאת מאמר זה ללא הכנה מתאימה! :) בואו נתחיל להתמודד עם הכל לפי הסדר. ראשית, נעמיק קצת בתיאוריה, רק קצת. שנית, נבין איך לעשות את החרא הזה בג'אווה האהובה על כולם. כמו כן, נכתוב איתכם גיליון תרמית לפרויקטים, אשר יגבש את ההבנה שלנו בנושא וישמש כתבנית כיצד יש לבצע מיפוי . אז, בואו נעשה את זה!

מהי ישות?

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

מה זה DB Relations?

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

דוגמה לאימה

יהיה לנו פרויקט אחד שיהיו לו 5 סניפים: מאסטר, שבו יהיה תיאור הפרויקט, וענף 1 לכל קשר DB. כל ענף יכיל סקריפטים של SQL ליצירת DB ומילויו בנתוני בדיקה, בתוספת מחלקת Entity עם מיפוי הערות. יהיה גם קובץ תצורה של Hibernate עבור כל סניף. אני אשתמש ב-H2 משובץ DB עבור הפרויקט כדי לא להיות מוסחת על ידי היבטים בודדים של ענן DB או DB חיצוני. על ידי לחיצה על הקישור, התקן את H2 DB על שואב האבק שלך. אתאר כל שלב בענף אחד, השאר הם רק נקודות המפתח. בסוף נסכם. ללכת. זהו קישור לענף המאסטר של הפרויקט שלי.

מערכת יחסים אחד לאחד

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

    ישויות JPA וקשרי DB - 2

    לאחר מכן נעבור להגדרות DB. אתה יכול להזין את הנתונים שלך, ואפילו את ה-DBMS שלך; אני חוזר, אני משתמש ב-H2 DB בשביל הפשטות.

    ישויות JPA ויחסי DB - 3

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

    ישויות JPA וקשרי DB - 4

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

    ישויות JPA ויחסי DB - 5
  2. יצרנו את מסד הנתונים והגדרנו את הגישה אליו מ-IDEA. כעת עליך ליצור בו טבלאות ולמלא אותו בכמה נתונים. לדוגמה, אקח שתי ישויות: מחבר וספר. לספר יכול להיות מחבר, יכול להיות מספר מחברים, או שלא יהיה אחד. בדוגמה זו ניצור את כל סוגי החיבורים. אבל בשלב הזה - מערכת יחסים של אחד לאחד. בואו ניצור את הסקריפט המתאים שיוצר DB Tables :

    DROP TABLE IF EXISTS PUBLIC.BOOK;
    
    CREATE TABLE PUBLIC.BOOK (
      ID         INTEGER      NOT NULL AUTO_INCREMENT,
      NAME       VARCHAR(255) NOT NULL,
      PRINT_YEAR INTEGER(4)   NOT NULL,
      CONSTRAINT BOOK_PRIMARY_KEY PRIMARY KEY (ID)
    );
    
    DROP TABLE IF EXISTS PUBLIC.AUTHOR;
    
    CREATE TABLE PUBLIC.AUTHOR (
      ID          INTEGER      NOT NULL AUTO_INCREMENT,
      FIRST_NAME  VARCHAR(255) NOT NULL,
      SECOND_NAME VARCHAR(255) NOT NULL,
      BOOK_ID     INTEGER      NOT NULL UNIQUE,
      CONSTRAINT AUTHOR_PRIMARY_KEY PRIMARY KEY (ID),
      CONSTRAINT BOOK_FOREIGN_KEY FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID)
    );

    ובואו נבצע את זה:

    ישויות JPA וקשרי DB - 6

    תוצאת ביצוע במסוף:

    ישויות JPA וקשרי DB - 7

    תוצאה ב-DB:

    ישויות JPA וקשרי DB - 8
  3. הבה נתבונן בתרשים של הטבלאות שלנו. כדי לעשות זאת, RMB ב-DB שלנו:

    ישויות JPA ויחסי DB - 9

    תוֹצָאָה:

    ישויות JPA וקשרי DB - 10

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

  4. בואו נכתוב סקריפט שממלא את ה-DB שלנו בנתוני בדיקה:

    INSERT INTO PUBLIC.BOOK (NAME, PRINT_YEAR)
    VALUES ('First book', 2010),
           ('Second book', 2011),
           ('Third book', 2012);
    
    INSERT INTO PUBLIC.AUTHOR (FIRST_NAME, SECOND_NAME, BOOK_ID)
    VALUES ('Pablo', 'Lambado', 1),
           ('Pazo', 'Zopa', 2),
           ('Lika', 'Vika', 3);

    כלומר, מה קורה? יש צורך בקשר אחד לאחד כאשר הישות של טבלה אחת קשורה לישות אחת לאחרת (או אינה קשורה כלל אם NOT NULL הוסר מ-BOOK_ID). בדוגמה שלנו, ספר אחד חייב להיות בעל מחבר אחד. אין דרך אחרת.

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

    @Data
    @Entity
    @DynamicInsert
    @DynamicUpdate
    @Table(name = "AUTHOR")
    public class Author {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "ID", nullable = false)
        private Long id;
    
        @Column(name = "FIRST_NAME", nullable = false)
        private String firstName;
    
        @Column(name = "SECOND_NAME", nullable = false)
        private String secondName;
    
        @OneToOne
        @JoinColumn(name = "BOOK_ID", unique = true, nullable = false)
        private Book book;
    }
בואו נבין את זה לפי הסדר:
  1. כל השדות במחלקה חוזרים על התכונות של הישות DB.
  2. @Data (מ- Lombok ) אומר שלכל שדה ייווצר getter ו-seter, equals, hashcode ידרוס, ותיווצר שיטת toString.
  3. @Entity אומר שהמחלקה הנתונה היא ישות ומשויכת לישות DB.
  4. @DynamicInsert ו- @DynamicUpdate אומרים שהוספות ועדכונים דינמיים יבוצעו ב-DB. אלו הן הגדרות Hibernate עמוקות יותר שיהיו שימושיות עבורך כך שתהיה לך אצווה נכונה.
  5. @Table (שם = "AUTHOR") קושר את מחלקת הספר לטבלת DB AUTHOR.
  6. @Id אומר שהשדה הזה הוא המפתח העיקרי.
  7. @GeneratedValue (אסטרטגיה = GenerationType.IDENTITY) – אסטרטגיית יצירת מפתח ראשי.
  8. @Column (שם = "ID", nullable = false) משייך שדה לתכונה DB, וגם אומר ששדה ה-DB הנתון אינו יכול להיות null. זה שימושי גם בעת יצירת טבלאות מיישויות. התהליך ההפוך לאופן שבו אנו יוצרים כעת את הפרויקט שלנו, זה נחוץ ב-DBs הבדיקה לבדיקות יחידה.
  9. @OneToOne אומר שהשדה הנתון הוא שדה קשר של אחד לאחד.
  10. @JoinColumn (שם = "BOOK_ID", ייחודי = true, nullable = false) - תיווצר עמודת BOOK_ID שהיא ייחודית ולא null.
בצד ההפוך (במחלקת ספר ) אנחנו צריכים גם ליצור חיבור One-to-One ולציין את השדה עליו מתרחש המיפוי. @OneToOne(mappedBy = "book") - בדוגמה זו, זהו שדה הספר של המחלקה Author. JPA יקשר אותם בעצמו. במבט ראשון אולי נראה שיש בלאגן של הערות, אבל למעשה זה מאוד נוח ועם ניסיון תוסיף אותן אוטומטית בלי לחשוב אפילו.
  1. עכשיו בואו נגדיר את מצב Hibernate. כדי לעשות זאת, צור קובץ hibernate.cfg.xml :

    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
        <session-factory>
            <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
            <property name="hibernate.connection.driver_class">org.h2.Driver</property>
    
            <property name="hibernate.connection.url">jdbc:h2:~/db/onetoone</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.connection.password"/>
    
            <property name="hibernate.hbm2ddl.auto">update</property>
    
            <property name="hibernate.show_sql">true</property>
            <property name="hibernate.format_sql">true</property>
            <property name="hibernate.use_sql_comments">true</property>
    
            <property name="hibernate.generate_statistics">true</property>
    
            <property name="hibernate.jdbc.batch_size">50</property>
            <property name="hibernate.jdbc.fetch_size">50</property>
    
            <property name="hibernate.order_inserts">true</property>
            <property name="hibernate.order_updates">true</property>
            <property name="hibernate.jdbc.batch_versioned_data">true</property>
    
            <mapping class="com.qthegamep.forjavarushpublication2.entity.Book"/>
            <mapping class="com.qthegamep.forjavarushpublication2.entity.Author"/>
        </session-factory>
    </hibernate-configuration>
תיאור הנכסים :
  1. hibernate.dialect הוא הניב של ה-DBMS שבחרנו.
  2. hibernate.connection.driver_class - כיתת נהגים של DB שלנו.
  3. hibernate.connection.url - utl של ה-DB שלנו. אתה יכול לקחת את זה מהנקודה הראשונה שבה הגדרנו את DB.
  4. hibernate.connection.username - שם משתמש DB.
  5. hibernate.connection.password - סיסמת משתמש DB.
  6. hibernate.hbm2ddl.auto - הגדרת יצירת טבלאות. אם עדכון, אז הוא לא יוצר אם הוא כבר נוצר, אלא רק מעדכן אותו.
  7. hibernate.show_sql - האם להציג שאילתות DB.
  8. hibernate.format_sql - האם לעצב שאילתות DB. אם לא, אז כולם יהיו על קו אחד. אני ממליץ להפעיל אותו.
  9. hibernate.use_sql_comments - הערות שאילתות DB. אם מדובר ב-Insert, אז נכתבת הערה מעל הבקשה שהבקשה היא מסוג Insert.
  10. hibernate.generate_statistics - יוצר יומנים. אני ממליץ וממליץ להגדיר רישום למקסימום. קריאת היומנים תגדיל את הסיכויים שלך לעבוד נכון עם ה-ORM.
  11. hibernate.jdbc.batch_size — גודל אצווה מקסימלי.
  12. hibernate.jdbc.fetch_size — גודל אחזור מקסימלי.
  13. hibernate.order_inserts - מאפשר הוספות דינמיות.
  14. hibernate.order_updates - מאפשר עדכונים דינמיים.
  15. hibernate.jdbc.batch_versioned_data - מאפשר אצווה. תסתכל על ה-DBMS שלך: לא כולם תומכים בזה.
  16. מחלקת מיפוי - מחלקות שהן הישויות שלנו. הכל צריך להיות רשום.
  1. כעת יש לקבוע את המהות שלנו. נוכל לבדוק זאת בלשונית ההתמדה:

    ישויות JPA וקשרי DB - 11

    תוֹצָאָה:

    ישויות JPA וקשרי DB - 12
  2. אנחנו צריכים גם להגדיר הקצאת נתונים:

    ישויות JPA וקשרי DB - 13 ישויות JPA ויחסי DB - 14

    תוצאות: ביצענו מיפוי אחד לאחד. החומר מיועד למטרות מידע בלבד, הפרטים נמצאים בהפניות.

מערכת יחסים של אחד לרבים

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

    ישויות JPA וקשרי DB - 15

    האם אתה מרגיש את ההבדל עם הטבלה הקודמת?

  2. תרשים:

    ישויות JPA וקשרי DB - 16

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

  3. ההבדל במיפוי יהיה בהערות ובשדות:

    מופיע שדה במחלקה Author :

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
    private Set<Book> books;

    זה כבר סט, מכיוון שיכולים להיות לנו כמה ספרים. @OneToMany מדבר על סוג גישה. FetchType.Lazy אומר שאיננו צריכים לטעון את כל רשימת הספרים אם היא לא צוינה בבקשה. צריך גם לומר שאי אפשר להוסיף את השדה הזה ל-toString, אחרת נתחיל לעשן StackOverflowError. לומבוק האהוב שלי דואג לזה:

    @ToString(exclude = "books")

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

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "AUTHOR_ID", nullable = false)
    private Author author;

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

  4. שום דבר לא השתנה ב- hibernate.cfg.xml .

  5. הַתמָדָה:

    ישויות JPA וקשרי DB - 17

מערכת יחסים של רבים לאחד

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

    ישויות JPA וקשרי DB - 18
  2. תרשים:

    ישויות JPA וקשרי DB - 19
  3. ההבדל במיפוי יהיה בהערות ובשדות:

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

  4. hibernate.cfg.xml

  5. הַתמָדָה:

    ישויות JPA וקשרי DB - 20

מערכת יחסים של רבים לרבים

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

    כתוצאה מביצוע הסקריפט, נקבל את הטבלאות הבאות:

    ישויות JPA וקשרי DB - 21
  2. תרשים:

    ישויות JPA וקשרי DB - 22

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

  3. לשיעורי מיפוי יהיו סטים בתוך השיעורים. אבל כפי שאמרתי, טבלת HAS אינה ישות.

    כיתת מחבר :

    @ManyToMany
    @JoinTable(name = "HAS",
            joinColumns = @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"),
            inverseJoinColumns = @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID")
    )
    private Set<Book> books;

    @ManyToMany הוא סוג של מערכת יחסים.

    @JoinTable - זה בדיוק מה שיחבר את התכונה עם טבלת HAS נוספת. בו אנו מציינים שתי תכונות שיצביעו על המפתחות הראשיים של שתי ישויות.

    שיעור ספרים :

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "books")
    private Set<Author> authors;

    כאן אנו מציינים את FetchType ואת השדה בו נשתמש למיפוי.

  4. Hibernate.cfg.xml שלנו שוב נשאר ללא שינוי (אני לא לוקח בחשבון את העובדה שיצרנו DB חדש לכל סניף).

  5. הַתמָדָה:

    ישויות JPA וקשרי DB - 23

תַחקִיר

אז, בדקנו באופן שטחי את סוגי קשרי ה-DB והבנו כיצד ליישם אותם במודל ORM. כתבנו פרויקט בדיקה שמדגים את כל החיבורים, והבנו איך להגדיר את Hibernate / jpa. פיו.

קישורים שימושיים

מאמרים קודמים שלי: נ.ב. ייתכנו שגיאות וחסרונות בטקסט. PPS המחבר עישן משהו מוזר בזמן כתיבת מאמר זה. תודה לך על תשומת הלב!
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION