JPA 實體與資料庫關係
美好的一天,同事們!什麼是實體?
實體是現實生活中的物件(例如汽車),具有屬性(門、車輪、引擎)。DB實體:在這種情況下,我們的實體儲存在資料庫中,一切都很簡單。我們為什麼以及如何將汽車放入資料庫 - 我們稍後會看到。什麼是資料庫關係?
很久以前,在遙遠的王國,創建了一個關係型資料庫。在該資料庫中,資料以表格的形式呈現。但即使是史瑞克的驢子也明白,有必要創造一種機制來互連這些桌子。結果出現了4個DB關係: 如果您是第一次看到這一切,我再次警告您 - 情況會變得更糟:考慮出去散步。我們將透過一個例子來分析所有這些關係,並理解它們之間的差異。恐怖的例子
我們將有一個項目,該項目將有 5 個分支:master,其中將有項目的描述,以及每個資料庫關係的 1 個分支。每個分支將包含用於建立資料庫並用測試資料填充它的 SQL 腳本,以及帶有註釋映射的實體類別。每個分支還將有一個 Hibernate 設定檔。我將在專案中使用H2嵌入式資料庫,以免被雲端資料庫或外部資料庫的各個方面分散注意力。透過點擊鏈接,在吸塵器上安裝 H2 DB。我將在1個分支中描述每個步驟,其餘的只是要點。最後我們來總結一下。去。 這是我專案的主分支的連結。一對一的關係
連結到此處的分支。-
我們需要將 H2 DB 連接到我們的專案。這裡我們需要強調的是,我們需要Ultimate IDEA才能輕鬆地與DB和其他東西一起工作。如果已經有了,那麼就直接進入資料庫連線。轉到資料庫選項卡並按照螢幕截圖中的操作進行操作:
接下來我們繼續進行資料庫設定。您可以輸入您的數據,甚至您的 DBMS;我重複一遍,為了簡單起見,我使用 H2 DB。
接下來,讓我們設定電路。此步驟是可選的,但如果資料庫中有多個架構,則建議執行此步驟。
應用設置,最後我們應該得到這樣的結果:
-
我們創建了資料庫並配置了從 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) );
讓我們執行它:
控制台執行結果:
資料庫中的結果:
-
讓我們來看看表格的圖表。為此,我們的資料庫上的人民幣:
結果:
在UML圖上我們可以看到所有的主鍵和外鍵,我們也可以看到表之間的連結。
-
讓我們編寫一個腳本,用測試資料填充我們的資料庫:
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 則根本不相關)時,需要一對一關係。在我們的範例中,一本書必須有一位作者。別無退路。
-
現在最有趣的是如何將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; }
- 類別中的所有欄位都重複資料庫實體的屬性。
- @Data(來自Lombok)表示,對於每個字段,將創建一個 getter 和 setter,equals、hashcode 將被覆蓋,並產生一個 toString 方法。
- @Entity表示給定的類別是一個實體並與資料庫實體關聯。
- @DynamicInsert和@DynamicUpdate表示動態插入和更新將在資料庫中執行。這些是更深層的 Hibernate 設置,對您有用,以便您擁有正確的批次。
- @Table (name = "AUTHOR") 將 Book 類別綁定到 DB AUTHOR 表。
- @Id表示該欄位是主鍵。
- @GenerateValue (strategy = GenerationType.IDENTITY) – 主鍵產生策略。
- @Column (name = "ID", nullable = false) 將一個欄位與一個 DB 屬性關聯起來,同時也表示給定的 DB 欄位不能為 null。從實體產生表時這也很有用。與我們現在創建專案的方式相反的過程,這在單元測試的測試資料庫中是必需的。
- @OneToOne表示給定欄位是一對一關係欄位。
- @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - 將建立一個 BOOK_ID 列,該列是唯一的且不為空。
-
現在讓我們來設定 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>
- hibernate.dialect 是我們選擇的 DBMS 的方言。
- hibernate.connection.driver_class - 我們的資料庫的驅動程式類別。
- hibernate.connection.url - 我們資料庫的 utl。你可以從我們配置資料庫的第一點開始。
- hibernate.connection.username - 資料庫使用者名稱。
- hibernate.connection.password — 資料庫使用者密碼。
- hibernate.hbm2ddl.auto - 設定表產生。如果是update,那麼如果已經創建了就不會生成,而只是更新它。
- hibernate.show_sql - 是否顯示資料庫查詢。
- hibernate.format_sql - 是否格式化資料庫查詢。如果沒有,那麼他們都會在一條線上。我建議打開它。
- hibernate.use_sql_comments - 註解資料庫查詢。如果這是一個Insert,則在請求上方寫一條註釋,表示該請求是Insert類型的。
- hibernate.generate_statistics - 產生日誌。我推薦並建議將日誌記錄設為最大。閱讀日誌將增加您正確使用 ORM 的機會。
- hibernate.jdbc.batch_size — 最大批次大小。
- hibernate.jdbc.fetch_size — 最大取得大小。
- hibernate.order_inserts - 允許動態插入。
- hibernate.order_updates - 允許動態更新。
- hibernate.jdbc.batch_versioned_data - 允許批次處理。看看你的 DBMS:不是每個人都支持這一點。
- 映射類別 - 作為我們實體的類別。所有的事情都需要列出來。
-
現在我們必須確定我們的本質。我們可以在持久性選項卡中檢查這一點:
結果:
-
我們還需要配置分配資料:
結果:我們完成了一對一映射。資料僅供參考,詳細資訊請參閱參考資料。
一對多關係
連結到此處的分支。文章中的程式碼我就不再貼出來了,因為程式碼已經太長了。我們查看 GitHub 上的所有程式碼。-
執行初始化腳本的結果是:
你感覺和上一張錶有什麼不同嗎?
-
圖表:
一對多關係 - 一位作者可以擁有多本書。左側實體對應於一個或多個右側實體。
-
映射的區別在於註解和字段:
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 對雙向通訊一無所知。對他來說,這是兩種不同的連結:一種是同向的,另一種是相反的。
-
hibernate.cfg.xml中沒有發生太大變化。
-
持久性:
多對一關係
由於多對一是一對多的鏡像,因此幾乎沒有差異。連結到此處的分支。多對多關係
讓我們繼續討論最有趣的關係。根據所有正派和不正派規則,這種關係是透過一個附加表創建的。但這個表不是一個實體。有趣,對吧?我們來看看這個狗屎。連結到此處的分支。-
查看初始化腳本,這裡出現了一個額外的HAS表。我們得到類似作者有書的東西。
執行腳本的結果是,我們將得到下表:
-
圖表:
在我們的例子中,事實證明,一本書可以有很多作者,而一個作者可以有很多書。它們可能會重疊。
-
映射類別將在類別內有集合。但正如我所說,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 和我們將用於映射的欄位。
-
我們的hibernate.cfg.xml再次保持不變(我沒有考慮我們為每個分支建立新資料庫的事實)。
-
持久性:
GO TO FULL VERSION