Thực thể JPA && Mối quan hệ DB
Chúc một ngày tốt lành, đồng nghiệp!Thực thể là gì?
Thực thể là một đối tượng có trong đời thực (ví dụ: ô tô) có các thuộc tính (cửa, BÁNH XE , động cơ). Thực thể DB: Trong trường hợp này, thực thể của chúng tôi được lưu trữ trong DB, mọi thứ đều đơn giản. Tại sao và làm thế nào chúng ta đưa chiếc xe vào cơ sở dữ liệu - chúng ta sẽ xem xét nó sau.Mối quan hệ DB là gì?
Cách đây rất lâu, ở vương quốc xa xôi , một DB quan hệ đã được tạo ra . Trong DB này, dữ liệu được trình bày dưới dạng bảng. Nhưng ngay cả con lừa của Shrek cũng hiểu rằng cần phải tạo ra một cơ chế kết nối các bảng này với nhau. Kết quả xuất hiện 4 mối quan hệ DB : Nếu bạn nhìn thấy tất cả những điều này lần đầu tiên, tôi cảnh báo bạn một lần nữa - mọi chuyện sẽ trở nên tồi tệ hơn: hãy nghĩ đến việc đi dạo. Chúng tôi sẽ phân tích tất cả các mối quan hệ này bằng một ví dụ và hiểu sự khác biệt giữa chúng.Ví dụ kinh dị
Chúng ta sẽ có một dự án có 5 nhánh: master, nơi sẽ có mô tả về dự án và 1 nhánh cho mỗi mối quan hệ DB. Mỗi nhánh sẽ chứa các tập lệnh SQL để tạo một DB và điền dữ liệu thử nghiệm vào đó, cùng với một lớp Thực thể có ánh xạ chú thích. Cũng sẽ có một tệp cấu hình Hibernate cho mỗi nhánh. Tôi sẽ sử dụng DB nhúng H2 cho dự án để không bị phân tâm bởi các khía cạnh riêng lẻ của DB đám mây hoặc DB bên ngoài. Bằng cách nhấp vào liên kết, hãy cài đặt H2 DB trên máy hút bụi của bạn. Mình sẽ mô tả từng bước theo 1 nhánh, còn lại chỉ là những điểm chính thôi. Cuối cùng chúng tôi sẽ tóm tắt. Đi. Đây là liên kết đến nhánh chính của dự án của tôi.Mối quan hệ một-một
Liên kết đến chi nhánh ở đây .-
Chúng ta cần kết nối H2 DB với dự án của mình. Ở đây chúng tôi cần nhấn mạnh rằng chúng tôi cần Ultimate IDEA để làm việc thoải mái với DB và những thứ khác. Nếu bạn đã có nó rồi thì hãy truy cập trực tiếp vào kết nối DB. Chuyển đến tab Cơ sở dữ liệu và thực hiện như trong ảnh chụp màn hình:
Tiếp theo chúng ta chuyển sang cài đặt DB. Bạn có thể nhập dữ liệu và thậm chí cả DBMS của mình; Tôi nhắc lại, tôi sử dụng H2 DB để đơn giản.
Tiếp theo, hãy thiết lập mạch. Bước này là tùy chọn nhưng được khuyến nghị nếu bạn có nhiều lược đồ trong cơ sở dữ liệu.
Áp dụng các cài đặt và cuối cùng chúng ta sẽ nhận được kết quả như thế này:
-
Chúng tôi đã tạo cơ sở dữ liệu và định cấu hình quyền truy cập vào cơ sở dữ liệu đó từ IDEA. Bây giờ bạn cần tạo các bảng trong đó và điền vào đó một số dữ liệu. Ví dụ: tôi sẽ lấy hai thực thể: Tác giả và Sách. Một cuốn sách có thể có một tác giả, có thể có nhiều tác giả hoặc không có một tác giả nào. Trong ví dụ này, chúng tôi sẽ tạo tất cả các loại kết nối. Nhưng tại thời điểm này - mối quan hệ Một-Một. Hãy tạo tập lệnh tương ứng để tạo Bảng DB :
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) );
Và hãy thực hiện nó:
Kết quả thực thi trong bảng điều khiển:
Kết quả trong DB:
-
Hãy nhìn vào sơ đồ các bảng của chúng tôi. Để làm điều này, RMB trên DB của chúng tôi:
Kết quả:
Trên sơ đồ UML chúng ta có thể thấy tất cả các khóa chính và khóa ngoại, chúng ta cũng thấy được kết nối giữa các bảng của mình.
-
Hãy viết một tập lệnh điền dữ liệu thử nghiệm vào DB của chúng tôi:
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);
Ý tôi là, chuyện gì xảy ra vậy? Mối quan hệ một-một là cần thiết khi thực thể của một bảng có liên quan đến thực thể của một bảng khác (hoặc hoàn toàn không liên quan nếu NOT NULL bị xóa khỏi BOOK_ID). Trong ví dụ của chúng tôi, một cuốn sách PHẢI có một tác giả. Không con cach nao khac.
-
Bây giờ điều thú vị nhất là làm thế nào để kết nối một lớp Java với các thực thể DB? Rất đơn giản. Hãy tạo hai lớp Sách và Tác giả. Bằng một ví dụ, tôi sẽ phân tích lớp 1 và các trường giao tiếp chính. Hãy lấy lớp Author làm ví dụ :
@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; }
- Tất cả các trường trong lớp đều lặp lại các thuộc tính của thực thể DB.
- @Data (từ Lombok ) nói rằng đối với mỗi trường, một getter và setter sẽ được tạo, bằng, mã băm sẽ bị ghi đè và phương thức toString sẽ được tạo.
- @Entity nói rằng lớp đã cho là một thực thể và được liên kết với một thực thể DB.
- @DynamicInsert và @DynamicUpdate nói rằng việc chèn và cập nhật động sẽ được thực hiện trong DB. Đây là những cài đặt Hibernate sâu hơn sẽ hữu ích cho bạn để bạn có được việc phân nhóm ĐÚNG.
- @Table (name = "AUTHOR") liên kết lớp Sách với bảng DB AUTHOR.
- @Id nói rằng trường này là khóa chính.
- @GeneratedValue (strategy = GenerationType.IDENTITY) – chiến lược tạo khóa chính.
- @Column (name = "ID", nullable = false) liên kết một trường với thuộc tính DB và cũng cho biết rằng trường DB đã cho không thể rỗng. Điều này cũng hữu ích khi tạo bảng từ các thực thể. Quy trình ngược lại với cách chúng tôi tạo dự án hiện tại, điều này cần thiết trong DB thử nghiệm cho các bài kiểm tra Đơn vị.
- @OneToOne nói rằng trường đã cho là trường mối quan hệ Một-Một.
- @JoinColumn (name = "BOOK_ID", duy nhất = true, nullable = false) - một cột BOOK_ID sẽ được tạo, cột này là duy nhất và không rỗng.
-
Bây giờ hãy cấu hình Hibernate. Để thực hiện việc này, hãy tạo tệp 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 là phương ngữ của DBMS mà chúng tôi đã chọn.
- hibernate.connection.driver_class - Lớp trình điều khiển của DB của chúng tôi.
- hibernate.connection.url - utl của DB của chúng tôi. Bạn có thể bắt đầu từ điểm đầu tiên khi chúng tôi định cấu hình DB.
- hibernate.connection.username - Tên người dùng DB.
- hibernate.connection.password - Mật khẩu người dùng DB.
- hibernate.hbm2ddl.auto - thiết lập việc tạo bảng. Nếu cập nhật thì nó sẽ không tạo ra nếu nó đã được tạo mà chỉ cập nhật nó.
- hibernate.show_sql - có hiển thị truy vấn DB hay không.
- hibernate.format_sql - có định dạng truy vấn DB hay không. Nếu không thì tất cả chúng sẽ nằm trên một dòng. Tôi khuyên bạn nên bật nó lên.
- hibernate.use_sql_comments - nhận xét truy vấn DB. Nếu đây là một Phụ trang thì một nhận xét sẽ được viết phía trên yêu cầu rằng yêu cầu đó thuộc loại Phụ trang.
- hibernate.generate_statistics - tạo nhật ký. Tôi khuyên bạn nên thiết lập đăng nhập ở mức tối đa. Đọc nhật ký sẽ tăng cơ hội làm việc chính xác với ORM.
- hibernate.jdbc.batch_size - Kích thước lô tối đa.
- hibernate.jdbc.fetch_size - Kích thước tìm nạp tối đa.
- hibernate.order_inserts - cho phép chèn động.
- hibernate.order_updates - Cho phép cập nhật động.
- hibernate.jdbc.batch_versioned_data - cho phép phân nhóm. Nhìn vào DBMS của bạn: không phải ai cũng hỗ trợ điều này.
- lớp ánh xạ - các lớp là thực thể của chúng tôi. Tất cả mọi thứ cần phải được liệt kê.
-
Bây giờ bản chất của chúng ta phải được xác định. Chúng ta có thể kiểm tra điều này trong tab kiên trì:
Kết quả:
-
Chúng ta cũng cần định cấu hình dữ liệu gán:
Kết quả: Chúng tôi đã thực hiện lập bản đồ One-to-One. Tài liệu chỉ nhằm mục đích thông tin, chi tiết có trong tài liệu tham khảo.
Mối quan hệ một-nhiều
Liên kết đến chi nhánh ở đây . Mình sẽ không đăng code vào bài viết nữa vì đã quá dài rồi. Chúng tôi xem xét tất cả mã trên GitHub.-
Kết quả của việc thực thi tập lệnh khởi tạo, chúng tôi nhận được như sau:
Bạn có cảm thấy sự khác biệt so với bảng trước không?
-
Biểu đồ:
Mối quan hệ một-nhiều - một tác giả có thể có nhiều cuốn sách. Thực thể bên trái tương ứng với một hoặc nhiều thực thể bên phải.
-
Sự khác biệt trong ánh xạ sẽ nằm ở chú thích và trường:
Một trường xuất hiện trong lớp Tác giả :
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author") private Set<Book> books;
Nó đã là một bộ rồi vì chúng ta có thể có nhiều cuốn sách. @OneToMany đang nói về loại mối quan hệ. FetchType.Lazy nói rằng chúng tôi không cần tải toàn bộ danh sách sách nếu nó không được chỉ định trong yêu cầu. Cũng cần phải nói rằng KHÔNG THỂ thêm trường này vào toString, nếu không chúng ta sẽ bắt đầu tạo ra StackOverflowError. Lombok yêu quý của tôi sẽ lo việc này:
@ToString(exclude = "books")
Trong lớp Sách , chúng tôi thực hiện phản hồi Nhiều-Một:
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "AUTHOR_ID", nullable = false) private Author author;
Ở đây chúng tôi kết luận rằng Một-nhiều là hình ảnh phản chiếu của Nhiều-một và ngược lại. Cần nhấn mạnh rằng Hibernate không biết gì về truyền thông hai chiều. Đối với anh, đây là hai kết nối khác nhau: một hướng, một hướng ngược lại.
-
Không có gì thay đổi nhiều trong hibernate.cfg.xml .
-
Kiên trì:
Mối quan hệ nhiều-một
Vì Nhiều-một là hình ảnh phản chiếu của Một-nhiều nên sẽ có một vài điểm khác biệt. Liên kết đến chi nhánh ở đây .-
Kết quả thực thi tập lệnh khởi tạo, chúng ta nhận được kết quả như sau:
-
Biểu đồ:
-
Sự khác biệt trong ánh xạ sẽ nằm ở chú thích và trường:
Không còn một tập hợp nào trong lớp Tác giả nữa vì nó đã được chuyển sang lớp Sách .
-
Kiên trì:
Mối quan hệ nhiều-nhiều
Hãy chuyển sang mối quan hệ thú vị nhất. Mối quan hệ này, theo tất cả các quy tắc đứng đắn và không đứng đắn, được tạo ra thông qua một bảng bổ sung. Nhưng bảng này không phải là một thực thể. Thật thú vị phải không? Chúng ta hãy nhìn vào cái chết tiệt này. Liên kết đến chi nhánh ở đây .-
Nhìn vào script khởi tạo , một bảng HAS bổ sung xuất hiện ở đây. Chúng tôi nhận được một cái gì đó giống như tác giả có sách.
Kết quả thực thi tập lệnh, chúng ta sẽ nhận được các bảng sau:
-
Biểu đồ:
Trong ví dụ của chúng tôi, hóa ra một cuốn sách có thể có nhiều tác giả và một tác giả có thể có nhiều cuốn sách. Chúng có thể chồng lên nhau.
-
Các lớp ánh xạ sẽ có các tập hợp trong các lớp. Nhưng như tôi đã nói, bảng HAS không phải là một thực thể.
Lớp tác giả :
@ManyToMany @JoinTable(name = "HAS", joinColumns = @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"), inverseJoinColumns = @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID") ) private Set<Book> books;
@ManyToMany là một loại mối quan hệ.
@JoinTable - đây chính xác là thứ sẽ kết nối thuộc tính với bảng HAS bổ sung. Trong đó chúng tôi chỉ định hai thuộc tính sẽ trỏ đến khóa chính của hai thực thể.
Lớp sách :
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "books") private Set<Author> authors;
Ở đây chúng tôi chỉ ra FetchType và trường chúng tôi sẽ sử dụng để ánh xạ.
-
Một lần nữa, hibernate.cfg.xml của chúng tôi vẫn không thay đổi (tôi không tính đến thực tế là chúng tôi đã tạo một DB mới cho mỗi nhánh).
-
Kiên trì:
GO TO FULL VERSION