JavaRush /Java Blog /Random-TW /JPA 實體與資料庫關係
Nikita Koliadin
等級 40
Днепр

JPA 實體與資料庫關係

在 Random-TW 群組發布

JPA 實體與資料庫關係

美好的一天,同事們!
JPA 實體和資料庫關係 - 1
本材料是針對那些已經了解資料庫組織(以下簡稱DB - 「資料庫」)、對物件關係映射如何運作(以下簡稱ORM)及其實作(例如Hibernate / JPA)有最少了解的人。如果您對此不熟悉,我建議您從JDBC開始,然後再轉向 ORM 模型。我警告過你,我不對你在沒有充分準備的情況下閱讀本文後的心理負責!:) 讓我們開始按順序處理所有事情。首先,我們將深入研究理論,只是一點點。其次,我們將弄清楚如何在每個人最喜歡的 Java 中做這些事情。我們還將與您一起編寫一份項目備忘單,它將鞏固我們對該主題的理解,並作為應該如何完成映射的模板。那麼,就讓我們開始吧!

什麼是實體?

實體現實生活中的物件(例如汽車),具有屬性(門、車輪、引擎)。DB實體:在這種情況下,我們的實體儲存在資料庫中,一切都很簡單。我們為什麼以及如何將汽車放入資料庫 - 我們稍後會看到。

什麼是資料庫關係?

很久以前,在遙遠的王國,創建了一個關係型資料庫。在該資料庫中,資料以表格的形式呈現。但即使是史瑞克的驢子也明白,有必要創造一種機制來互連這些桌子。結果出現了4個DB關係
  1. 一對一
  2. 一對多
  3. 多對一
  4. 多對多
如果您是第一次看到這一切,我再次警告您 - 情況會變得更糟:考慮出去散步。我們將透過一個例子來分析所有這些關係,並理解它們之間的差異。

恐怖的例子

我們將有一個項目,該項目將有 5 個分支:master,其中將有項目的描述,以及每個資料庫關係的 1 個分支。每個分支將包含用於建立資料庫並用測試資料填充它的 SQL 腳本,以及帶有註釋映射的實體類別。每個分支還將有一個 Hibernate 設定檔。我將在專案中使用H2嵌入式資料庫,以免被雲端資料庫或外部資料庫的各個方面分散注意力。透過點擊鏈接,在吸塵器上安裝 H2 DB。我將在1個分支中描述每個步驟,其餘的只是要點。最後我們來總結一下。去。 是我專案的主分支的連結。

一對一的關係

連結到此處的分支。
  1. 我們需要將 H2 DB 連接到我們的專案。這裡我們需要強調的是,我們需要Ultimate IDEA才能輕鬆地與DB和其他東西一起工作。如果已經有了,那麼就直接進入資料庫連線。轉到資料庫選項卡並按照螢幕截圖中的操作進行操作:

    JPA Entities and DB Relationships - 2

    接下來我們繼續進行資料庫設定。您可以輸入您的數據,甚至您的 DBMS;我重複一遍,為了簡單起見,我使用 H2 DB。

    JPA Entities and DB Relationships - 3

    接下來,讓我們設定電路。此步驟是可選的,但如果資料庫中有多個架構,則建議執行此步驟。

    JPA Entities and DB Relationships - 4

    應用設置,最後我們應該得到這樣的結果:

    JPA Entities and DB Relationships - 5
  2. 我們創建了資料庫並配置了從 IDEA 對其的存取。現在您需要在其中建立表格並填充一些資料。例如,我將採用兩個實體:作者和書籍。一本書可能有一個作者,可能有多個作者,也可能沒有一個作者。在此範例中,我們將建立所有類型的連接。但此時——一對一的關係。讓我們建立對應的腳本來建立資料庫表

    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 Entities and DB Relationships - 6

    控制台執行結果:

    JPA Entities and DB Relationships - 7

    資料庫中的結果:

    JPA Entities and DB Relationships - 8
  3. 讓我們來看看表格的圖表。為此,我們的資料庫上的人民幣:

    JPA Entities and DB Relationships - 9

    結果:

    JPA Entities and DB Relationships - 10

    UML圖上我們可以看到所有的主鍵和外鍵,我們也可以看到表之間的連結。

  4. 讓我們編寫一個腳本,用測試資料填充我們的資料庫:

    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);

    我的意思是,會發生什麼事?當一個表的實體與另一個表的一個實體相關(或如果從 BOOK_ID 中刪除 NOT NULL 則根本不相關)時,需要一對一關係。在我們的範例中,一本書必須有一位作者。別無退路。

  5. 現在最有趣的是如何將Java類別與DB實體連結?很簡單。讓我們建立兩個類別 Book 和 Author。我將透過一個例子來分析第一類和關鍵的通訊領域。我們以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. 類別中的所有欄位都重複資料庫實體的屬性。
  2. @Data(來自Lombok)表示,對於每個字段,將創建一個 getter 和 setter,equals、hashcode 將被覆蓋,並產生一個 toString 方法。
  3. @Entity表示給定的類別是一個實體並與資料庫實體關聯。
  4. @DynamicInsert@DynamicUpdate表示動態插入和更新將在資料庫中執行。這些是更深層的 Hibernate 設置,對您有用,以便您擁有正確的批次。
  5. @Table (name = "AUTHOR") 將 Book 類別綁定到 DB AUTHOR 表。
  6. @Id表示該欄位是主鍵。
  7. @GenerateValue (strategy = GenerationType.IDENTITY) – 主鍵產生策略。
  8. @Column (name = "ID", nullable = false) 將一個欄位與一個 DB 屬性關聯起來,同時也表示給定的 DB 欄位不能為 null。從實體產生表時這也很有用。與我們現在創建專案的方式相反的過程,這在單元測試的測試資料庫中是必需的。
  9. @OneToOne表示給定欄位是一對一關係欄位。
  10. @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - 將建立一個 BOOK_ID 列,該列是唯一的且不為空。
在反面(在Book類別中),我們還需要建立一對一連線並指示發生對應的欄位。@OneToOne(mappedBy = "book") - 在此範例中,這是 Author 類別的 book 欄位。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 - 我們的資料庫的驅動程式類別。
  3. hibernate.connection.url - 我們資料庫的 utl。你可以從我們配置資料庫的第一點開始。
  4. hibernate.connection.username - 資料庫使用者名稱。
  5. hibernate.connection.password — 資料庫使用者密碼。
  6. hibernate.hbm2ddl.auto - 設定表產生。如果是update,那麼如果已經創建了就不會生成,而只是更新它。
  7. hibernate.show_sql - 是否顯示資料庫查詢。
  8. hibernate.format_sql - 是否格式化資料庫查詢。如果沒有,那麼他們都會在一條線上。我建議打開它。
  9. hibernate.use_sql_comments - 註解資料庫查詢。如果這是一個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 Entities and DB Relationships - 11

    結果:

    JPA Entities and DB Relationships - 12
  2. 我們還需要配置分配資料:

    JPA Entities and DB Relationships - 13 JPA Entities and DB Relationships - 14

    結果:我們完成了一對一映射。資料僅供參考,詳細資訊請參閱參考資料。

一對多關係

連結到此處的分支。文章中的程式碼我就不再貼出來了,因為程式碼已經太長了。我們查看 GitHub 上的所有程式碼。
  1. 執行初始化腳本的結果是:

    JPA Entities and DB Relationships - 15

    你感覺和上一張錶有什麼不同嗎?

  2. 圖表:

    JPA Entities and DB Relationships - 16

    一對多關係 - 一位作者可以擁有多本書。左側實體對應於一個或多個右側實體。

  3. 映射的區別在於註解和字段:

    Author類別中出現一個欄位:

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

    這已經是一套了,因為我們可以有幾本書。@OneToMany正在討論關係類型。FetchType.Lazy 表示如果請求中未指定,我們不需要載入整個圖書清單。還應該說的是,這個欄位不能加入到 toString 中,否則我們將開始產生 StackOverflowError。我心愛的 Lombok 負責處理這個問題:

    @ToString(exclude = "books")

    Book類別中,我們進行多對一回饋:

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

    在這裡我們得出結論,一對多是多對一的鏡像,反之亦然。應該強調的是,Hibernate 對雙向通訊一無所知。對他來說,這是兩種不同的連結:一種是同向的,另一種是相反的。

  4. hibernate.cfg.xml中沒有發生太大變化。

  5. 持久性:

    JPA Entities and DB Relationships - 17

多對一關係

由於多對一是一對多的鏡像,因此幾乎沒有差異。連結到此處的分支。
  1. 執行初始化腳本的結果是:

    JPA Entities and DB Relationships - 18
  2. 圖表:

    JPA Entities and DB Relationships - 19
  3. 映射的區別在於註解和字段:

    Author類別中不再有集合,因為它已移至Book類別。

  4. 休眠設定檔

  5. 持久性:

    JPA Entities and DB Relationships - 20

多對多關係

讓我們繼續討論最有趣的關係。根據所有正派和不正派規則,這種關係是透過一個附加表創建的。但這個表不是一個實體。有趣,對吧?我們來看看這個狗屎。連結到此處的分支。
  1. 查看初始化腳本,這裡出現了一個額外的HAS表。我們得到類似作者有書的東西。

    執行腳本的結果是,我們將得到下表:

    JPA Entities and DB Relationships - 21
  2. 圖表:

    JPA Entities and DB Relationships - 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再次保持不變(我沒有考慮我們為每個分支建立新資料庫的事實)。

  5. 持久性:

    JPA Entities and DB Relationships - 23

報告

因此,我們已經粗略地研究了資料庫關係的類型,並弄清楚瞭如何在 ORM 模型中實現它們。我們編寫了一個測試項目來演示所有連接,並弄清楚如何配置 hibernate / jpa。唷。

有用的連結

我之前的文章: PS 文中可能存在錯誤和不足之處。PPS 作者在寫這篇文章時抽的是奇怪的東西。感謝您的關注!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION