Trong JPA, để thể hiện mối quan hệ một cái này tương ứng với một cái kia, ví dụ như một khách du lịch chỉ ở một phòng duy nhất trong một khách sạn lớn, chúng ta sẽ sử dụng annotation @OneToOne. Cụ thể nó như thế nào? Trong bài viết này, chúng ta hãy cùng tìm hiểu về annotation @OneToOne trong JPA các bạn nhé!
Đầu tiên, mình sẽ tạo một Maven project để làm ví dụ:
Mình sẽ sử dụng Hibernate làm implementation của JPA nên sẽ thêm Hibernate dependency như sau:
1 2 3 4 5 |
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.11.Final</version> </dependency> |
MySQL Connector:
1 2 3 4 5 |
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.6</version> </dependency> |
Giả sử bây giờ mình có hai bảng bao gồm tourist và room với các thông tin như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
CREATE TABLE `room` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, `hotel` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `tourist` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, `room_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`room_id`) REFERENCES `room` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
Như các bạn thấy, bảng room có 3 cột là id, name và hotel với primary key là cột id. Bảng tourist thì có các cột id, name và room_id với primary key là cột id. Trong bảng tourist, mình đã định nghĩa một foreign key từ bảng tourist sang bảng room với mục đích một tourist sẽ ở một phòng và một phòng chỉ có thể có một tourist ở mà thôi.
Entity cho những bảng trên có sử dụng annotation @OneToOne sẽ như sau:
Entity Tourist:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.huongdanjava.jpaonetoone; import javax.persistence.*; @Entity @Table public class Tourist { @Id @GeneratedValue private Integer id; @Column private String name; @OneToOne @JoinColumn(name = "room_id") private Room room; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Room getRoom() { return room; } public void setRoom(Room room) { this.room = room; } } |
Entity Room:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package com.huongdanjava.jpaonetoone; import javax.persistence.*; @Table @Entity public class Room { @Id @GeneratedValue private Integer id; @Column private String name; @Column private String hotel; @OneToOne(mappedBy = "room") private Tourist tourist; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHotel() { return hotel; } public void setHotel(String hotel) { this.hotel = hotel; } public Tourist getTourist() { return tourist; } public void setTourist(Tourist tourist) { this.tourist = tourist; } } |
Bởi vì bảng tourist có column room_id là foreign key tới bảng room nên trong entity Tourist chúng ta sẽ khai báo annotation @OneToOne cùng với một annotation khác là @JoinColumn để thể hiện mối quan hệ này.
Còn để thể hiện mối quan hệ ngược lại, một room chỉ có một tourist, mình cũng đã sử dụng annotation @OneToOne trong entity Room với thuộc tính mappedBy là room. Thuộc tính room ở đây chính là tên biến trong entity Tourist được khai báo với annotation @OneToOne.
OK, vậy là chúng ta đã hoàn thành việc khai báo annotation @OneToOne để thể hiện mối quan hệ giữa một tourist và một room. Bây giờ hãy thử làm ví dụ để xem nó làm việc như thế nào nhé các bạn!
Mình sẽ thêm tập tin cấu hình cho JPA, persistence.xml, nằm trong thư mục /src/main/resources/META-INF với nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence_2_1.xsd"> <persistence-unit name="jpaexample" transaction-type="RESOURCE_LOCAL"> <class>com.huongdanjava.jpaonetoone.Room</class> <class>com.huongdanjava.jpaonetoone.Tourist</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpaexample" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="123456" /> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.use_sql_comments" value="true" /> </properties> </persistence-unit> </persistence> |
Giả sử trong database mình đang có những dữ liệu sau:
thì khi chạy ví dụ sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.huongdanjava.jpaonetoone; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; public class Application { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaexample"); EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); Tourist tourist = em.find(Tourist.class, 1); System.out.println("Tourist name: " + tourist.getName() + ", room: " + tourist.getRoom().getName()); em.close(); emf.close(); } } |
Kết quả sẽ là:
Davitluit
Trường hợp thiết kế db như này cũng có thể là OneToMany (1 phòng có nhiều Tourist ). Cho mình hỏi tại sao không thiết lập quan hệ OneTo One dựa trên khóa chính 2 bảng?