Hôm nay, chúng ta sẽ tìm hiểu về ánh xạ One To Many trong Hibernate. Chúng ta sẽ xem xét ví dụ về ánh xạ này bằng cách sử dụng cả Annotation và cấu hình XML.

Ánh xạ ‘One To Many’ trong Hibernate là gì?
Hiểu một cách đơn giản, ánh xạ ‘one to many’ là một hàng trong bảng này có thể được ánh xạ tới nhiều hàng trong một bảng khác. Ví dụ, hãy tưởng tượng một hệ thống Cart (giỏ hàng), trong đó chúng ta có một bảng khác dành cho các Item (mặt hàng). Một cart có thể có nhiều item, vì vậy đây chính là một ánh xạ ‘one to many’. Chúng ta sẽ sử dụng kịch bản Cart-Items cho ví dụ về ánh xạ này trong Hibernate.
Ánh xạ ‘One To Many’ trong Hibernate – Thiết lập cơ sở dữ liệu
Chúng ta có thể sử dụng ràng buộc khóa ngoại cho ánh xạ ‘one to many’. Dưới đây là đoạn script cơ sở dữ liệu của chúng ta cho bảng Cart và Items. Tôi đang sử dụng cơ sở dữ liệu MySQL cho ví dụ này. setup.sql
CREATE TABLE `Cart` (
`cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`total` decimal(10,0) NOT NULL,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
CREATE TABLE `Items` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`cart_id` int(11) unsigned NOT NULL,
`item_id` varchar(10) NOT NULL,
`item_total` decimal(10,0) NOT NULL,
`quantity` int(3) NOT NULL,
PRIMARY KEY (`id`),
KEY `cart_id` (`cart_id`),
CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
Dưới đây là sơ đồ ER của bảng Cart và Items.

Cơ sở dữ liệu của chúng ta đã sẵn sàng. Bây giờ, chúng ta sẽ bắt đầu tạo dự án ví dụ về ánh xạ ‘One to Many’ trong Hibernate. Trước hết, chúng ta sẽ sử dụng cấu hình dựa trên XML, sau đó sẽ triển khai ánh xạ ‘one to many’ bằng cách sử dụng Annotation của Hibernate và JPA.
Cấu trúc dự án Ánh xạ ‘One To Many’ trong Hibernate
Hãy tạo một dự án Maven đơn giản trong Eclipse hoặc IDE yêu thích của bạn. Cấu trúc dự án cuối cùng sẽ trông giống như hình dưới đây.

Phụ thuộc Maven cho Hibernate
File pom.xml cuối cùng của chúng ta sẽ chứa các dependency (phụ thuộc) cho Hibernate và MySQL driver. Hibernate sử dụng JBoss logging, và nó sẽ tự động được thêm vào như là transitive dependencies (phụ thuộc chuyển tiếp).
<project xmlns="<https://maven.apache.org/POM/4.0.0>" xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>" xsi:schemaLocation="<https://maven.apache.org/POM/4.0.0> <https://maven.apache.org/xsd/maven-4.0.0.xsd>">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.hibernate</groupId>
<artifactId>HibernateOneToManyMapping</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.5.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
</dependencies>
</project>
Lưu ý rằng tôi đang sử dụng phiên bản Hibernate mới nhất là 4.3.5.Final và phiên bản MySQL driver phù hợp với bản cài đặt cơ sở dữ liệu của tôi.
Các model class trong ánh xạ One To Many
Đối với các bảng Cart và Items, chúng ta sẽ có các model class để ánh xạ tương ứng.
Dưới đây là lớp Cart.java:
package com.journaldev.hibernate.model;
import java.util.Set;
public class Cart {
private long id;
private double total;
private String name;
private Set<Items> items;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Items> getItems() {
return items;
}
public void setItems(Set<Items> items) {
this.items = items;
}
}
Tôi đang sử dụng Set của Items để đảm bảo rằng mỗi bản ghi là duy nhất.
Chúng ta cũng có thể sử dụng List hoặc Array cho One To Many Mapping trong Hibernate.
package com.journaldev.hibernate.model;
public class Items {
private long id;
private String itemId;
private double itemTotal;
private int quantity;
private Cart cart;
//Hibernate requires no-args constructor
public Items(){}
public Items(String itemId, double total, int qty, Cart c){
this.itemId=itemId;
this.itemTotal=total;
this.quantity=qty;
this.cart=c;
}
public String getItemId() {
return itemId;
}
public void setItemId(String itemId) {
this.itemId = itemId;
}
public double getItemTotal() {
return itemTotal;
}
public void setItemTotal(double itemTotal) {
this.itemTotal = itemTotal;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
Items có quan hệ Many To One với Cart, vì vậy chúng ta không cần khai báo Collection cho đối tượng Cart.
Lớp tiện ích Hibernate SessionFactory
Chúng ta sẽ có một utility class (lớp tiện ích) dùng để tạo đối tượng Hibernate SessionFactory.
Dưới đây là lớp HibernateUtil.java:
package com.journaldev.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
System.out.println("Hibernate Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
Tệp cấu hình Hibernate XML
Tệp cấu hình Hibernate XML của chúng ta sẽ chứa thông tin cơ sở dữ liệu và các tài nguyên ánh xạ.
Dưới đây là tệp hibernate.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"<https://hibernate.org/dtd/hibernate-configuration-3.0.dtd>">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">pankaj123</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
<property name="hibernate.connection.username">pankaj</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="cart.hbm.xml"/>
<mapping resource="items.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Ví dụ ánh xạ One To Many trong Hibernate – Cấu hình bằng XML
Đây là phần quan trọng nhất của hướng dẫn. Hãy cùng xem cách chúng ta ánh xạ hai lớp Cart và Items để thực hiện ánh xạ One To Many trong Hibernate.
Dưới đây là tệp ánh xạ cart.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"<https://hibernate.org/dtd/hibernate-mapping-3.0.dtd>">
<hibernate-mapping package="com.journaldev.hibernate.model">
<class name="Cart" table="CART" >
<id name="id" type="long">
<column name="cart_id" />
<generator class="identity" />
</id>
<property name="total" type="double">
<column name="total" />
</property>
<property name="name" type="string">
<column name="name" />
</property>
<set name="items" table="ITEMS" fetch="select">
<key>
<column name="cart_id" not-null="true"></column>
</key>
<one-to-many class="Items"/>
</set>
</class>
</hibernate-mapping>
Phần quan trọng ở đây là thẻ set và phần tử one-to-many bên trong nó.
Lưu ý rằng chúng ta đang cung cấp key (khóa) sẽ được sử dụng cho ánh xạ one-to-many, cụ thể là cart_id. Tiếp theo là tệp ánh xạ items.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"<https://hibernate.org/dtd/hibernate-mapping-3.0.dtd>" >
<hibernate-mapping package="com.journaldev.hibernate.model">
<class name="Items" table="ITEMS">
<id name="id" type="long">
<column name="id" />
<generator class="identity" />
</id>
<property name="itemId" type="string">
<column name="item_id"></column>
</property>
<property name="itemTotal" type="double">
<column name="item_total"></column>
</property>
<property name="quantity" type="integer">
<column name="quantity"></column>
</property>
<many-to-one name="cart" class="Cart">
<column name="cart_id" not-null="true"></column>
</many-to-one>
</class>
</hibernate-mapping>
Lưu ý rằng từ Items đến Cart là mối quan hệ many-to-one. Vì vậy, chúng ta cần sử dụng phần tử <many-to-one> cho Cart và cung cấp tên cột sẽ được ánh xạ với khóa chính. Dựa trên cấu hình ánh xạ Hibernate của Cart, khóa cart_id sẽ được sử dụng để ánh xạ. Dự án ví dụ Hibernate One To Many Mapping sử dụng XML mapping của chúng ta đã sẵn sàng. Hãy viết một chương trình kiểm thử để kiểm tra xem nó có hoạt động đúng không.
Ví dụ kiểm thử ánh xạ One To Many – Test Program
HibernateOneToManyMain.java
package com.journaldev.hibernate.main;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Items;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateOneToManyMain {
public static void main(String[] args) {
Cart cart = new Cart();
cart.setName("MyCart");
Items item1 = new Items("I1", 10, 1, cart);
Items item2 = new Items("I2", 20, 2, cart);
Set<Items> itemsSet = new HashSet<Items>();
itemsSet.add(item1); itemsSet.add(item2);
cart.setItems(itemsSet);
cart.setTotal(10*1 + 20*2);
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//Get Session
sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");
//start transaction
tx = session.beginTransaction();
//Save the Model objects
session.save(cart);
session.save(item1);
session.save(item2);
//Commit transaction
tx.commit();
System.out.println("Cart ID="+cart.getId());
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
e.printStackTrace();
}finally{
if(!sessionFactory.isClosed()){
System.out.println("Closing SessionFactory");
sessionFactory.close();
}
}
}
}
Lưu ý rằng chúng ta cần lưu cả hai đối tượng Cart và Items lần lượt từng cái một. Hibernate sẽ tự động xử lý việc cập nhật khóa ngoại (foreign keys) trong bảng Items. Khi chúng ta chạy chương trình trên, sẽ nhận được kết quả như sau.
Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into CART (total, name) values (?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: update ITEMS set cart_id=? where id=?
Hibernate: update ITEMS set cart_id=? where id=?
Cart ID=6
Closing SessionFactory
Lưu ý rằng Hibernate đang sử dụng câu lệnh UPDATE để thiết lập giá trị cart_id trong bảng ITEMS.
Ánh xạ One To Many sử dụng Annotation
Sau khi chúng ta đã xem cách triển khai One To Many Mapping trong Hibernate bằng cấu hình XML, giờ hãy xem cách thực hiện tương tự bằng cách sử dụng JPA annotations.
Ví dụ ánh xạ One To Many sử dụng Annotation
Tệp cấu hình Hibernate gần như giống với trước, ngoại trừ phần tử ánh xạ sẽ thay đổi, vì giờ đây chúng ta sử dụng lớp Java để ánh xạ One To Many Mapping bằng annotation.
hibernate-annotation.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"<https://hibernate.org/dtd/hibernate-configuration-3.0.dtd>">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">pankaj123</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
<property name="hibernate.connection.username">pankaj</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<mapping class="com.journaldev.hibernate.model.Cart1"/>
<mapping class="com.journaldev.hibernate.model.Items1"/>
</session-factory>
</hibernate-configuration>
Lớp tiện ích Hibernate SessionFactory
Lớp tiện ích SessionFactory gần như giống với trước đó, chúng ta chỉ cần sử dụng tệp cấu hình Hibernate mới.
HibernateAnnotationUtil.java
package com.journaldev.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateAnnotationUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate-annotation.cfg.xml
Configuration configuration = new Configuration();
configuration.configure("hibernate-annotation.cfg.xml");
System.out.println("Hibernate Annotation Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate Annotation serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
Các lớp mô hình Hibernate One To Many sử dụng Annotation
Vì chúng ta không sử dụng các tệp ánh xạ XML, nên toàn bộ cấu hình liên quan đến ánh xạ sẽ được thực hiện bằng JPA annotations trong các lớp mô hình.
Nếu bạn đã hiểu cách ánh xạ bằng XML, thì việc sử dụng annotation sẽ rất đơn giản và tương tự. Dưới đây là lớp Cart1.java:
package com.journaldev.hibernate.model;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="CART")
public class Cart1 {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="cart_id")
private long id;
@Column(name="total")
private double total;
@Column(name="name")
private String name;
@OneToMany(mappedBy="cart1")
private Set<Items1> items1;
// Getter Setter methods for properties
}
Điểm quan trọng cần lưu ý là annotation @OneToMany, trong đó biến mappedBy được sử dụng để chỉ định tên thuộc tính trong lớp Items1 sẽ được dùng để thực hiện ánh xạ. Vì vậy, trong lớp Items1, chúng ta cần có một thuộc tính tên là cart1. Đừng quên bao gồm đầy đủ các phương thức getter và setter.
package com.journaldev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="ITEMS")
public class Items1 {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private long id;
@Column(name="item_id")
private String itemId;
@Column(name="item_total")
private double itemTotal;
@Column(name="quantity")
private int quantity;
@ManyToOne
@JoinColumn(name="cart_id", nullable=false)
private Cart1 cart1;
//Hibernate requires no-args constructor
public Items1(){}
public Items1(String itemId, double total, int qty, Cart1 c){
this.itemId=itemId;
this.itemTotal=total;
this.quantity=qty;
this.cart1=c;
}
//Getter Setter methods
}
Điểm quan trọng nhất trong lớp trên là annotation @ManyToOne được đặt trên biến lớp Cart1, cùng với annotation @JoinColumn để chỉ định tên cột dùng cho ánh xạ. Vậy là chúng ta đã hoàn tất việc thiết lập One To Many Mapping trong Hibernate bằng annotation trong các lớp mô hình. Hãy so sánh với cấu hình dùng XML, bạn sẽ thấy chúng tương tự nhau. Hãy cùng viết một chương trình kiểm thử và thực thi nó
Chương trình kiểm thử Hibernate One To Many Mapping sử dụng Annotation
Chương trình kiểm thử của chúng ta cũng tương tự như khi dùng cấu hình XML, chỉ khác là sử dụng các lớp mới để lấy Hibernate Session và lưu các đối tượng mô hình vào cơ sở dữ liệu. HibernateOneToManyAnnotationMain.java
package com.journaldev.hibernate.main;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Cart1;
import com.journaldev.hibernate.model.Items1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;
public class HibernateOneToManyAnnotationMain {
public static void main(String[] args) {
Cart1 cart = new Cart1();
cart.setName("MyCart1");
Items1 item1 = new Items1("I10", 10, 1, cart);
Items1 item2 = new Items1("I20", 20, 2, cart);
Set<Items1> itemsSet = new HashSet<Items1>();
itemsSet.add(item1); itemsSet.add(item2);
cart.setItems1(itemsSet);
cart.setTotal(10*1 + 20*2);
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//Get Session
sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");
//start transaction
tx = session.beginTransaction();
//Save the Model object
session.save(cart);
session.save(item1);
session.save(item2);
//Commit transaction
tx.commit();
System.out.println("Cart1 ID="+cart.getId());
System.out.println("item1 ID="+item1.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
System.out.println("item2 ID="+item2.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
e.printStackTrace();
}finally{
if(!sessionFactory.isClosed()){
System.out.println("Closing SessionFactory");
sessionFactory.close();
}
}
}
}
Khi chúng ta thực thi chương trình kiểm thử ví dụ Hibernate One To Many sử dụng annotation ở trên, chúng ta sẽ nhận được kết quả như sau.
Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created
Hibernate: insert into CART (name, total) values (?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Cart1 ID=7
item1 ID=9, Foreign Key Cart ID=7
item2 ID=10, Foreign Key Cart ID=7
Closing SessionFactory
Vậy là chúng ta đã hoàn tất phần hướng dẫn về Hibernate One To Many Mapping. Bạn có thể tải xuống dự án mẫu từ liên kết bên dưới và thử nghiệm thêm các tình huống khác.