CyStack logo
  • Sản phẩm & Dịch vụ
  • Giải pháp
  • Bảng giá
  • Công ty
  • Tài liệu
Vi

vi

Mục lục

Trang chủBlogOne to One Mapping trong ...
Java

One to One Mapping trong Hibernate bằng Annotation

9 phút đọc27/10/2025
CyStack Author
Chris Pham

Technical Writer

0 lượt xem
Reading Time: 9 minutes

Hôm nay, chúng ta sẽ cùng tìm hiểu về One-to-One Mapping (ánh xạ một-một) trong Hibernate, bao gồm cả cách cấu hình bằng AnnotationXML.

Giới thiệu về One to One Mapping trong Hibernate

image.png

Trong cơ sở dữ liệu, các bảng thường có quan hệ với nhau. Những dạng quan hệ phổ biến gồm: One-to-One (một-một), One-to-Many (một-nhiều), Many-to-Many (nhiều-nhiều).

Các quan hệ này còn được phân thành hai loại: Unidirectional (một chiều)Bidirectional (hai chiều)

Trong bài này, chúng ta sẽ triển khai ánh xạ One-to-One trong Hibernate bằng cả hai cách cấu hình: bằng XMLAnnotation.

Cài đặt cơ sở dữ liệu cho ví dụ Hibernate One-to-One

Đầu tiên, chúng ta cần thiết lập cơ sở dữ liệu gồm hai bảng: TransactionCustomer, với mối quan hệ one-to-one giữa chúng.

  • Transaction sẽ là bảng chính.
  • Customer sẽ chứa Foreign Key (khóa ngoại) trỏ tới Transaction.

Tôi cung cấp đoạn script MySQL vì đây là hệ quản trị cơ sở dữ liệu tôi sử dụng trong hướng dẫn này. Nếu bạn đang sử dụng một hệ quản trị cơ sở dữ liệu khác, hãy đảm bảo điều chỉnh đoạn script sao cho phù hợp.

-- Create Transaction Table
CREATE TABLE `Transaction` (
  `txn_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `txn_date` date NOT NULL,
  `txn_total` decimal(10,0) NOT NULL,
  PRIMARY KEY (`txn_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;

-- Create Customer table
CREATE TABLE `Customer` (
  `txn_id` int(11) unsigned NOT NULL,
  `cust_name` varchar(20) NOT NULL DEFAULT '',
  `cust_email` varchar(20) DEFAULT NULL,
  `cust_address` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`txn_id`),
  CONSTRAINT `customer_ibfk_1` FOREIGN KEY (`txn_id`) REFERENCES `Transaction` (`txn_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Quan hệ giữa hai bảng trên sẽ được ánh xạ theo kiểu one-to-one với txn_id là khóa chính đồng thời là khóa ngoại trong bảng Customer.

image.png

Cấu hình cơ sở dữ liệu của chúng ta đã sẵn sàng, giờ đây hãy cùng chuyển sang ví dụ về dự án Hibernate One-to-One.

Cấu trúc dự án Hibernate One-to-One Mapping

Tạo một Maven project đơn giản trong IDE (ở đây dùng Eclipse). Cấu trúc thư mục dự án sau khi hoàn thiện sẽ như hình minh họa.

Cấu trúc hoàn chỉnh của dự án:

image.png

Ta sẽ bắt đầu với cấu hình bằng XML, sau đó là phiên bản tương đương sử dụng Annotation.

Maven Dependencies

Dưới đây là nội dung tệp pom.xml cần thiết cho dự án:


  4.0.0
  com.journaldev.hibernate
  HibernateOneToOneMapping
  0.0.1-SNAPSHOT
  
  	
  		org.hibernate
  		hibernate-core
  		4.3.5.Final
  	
  	
  		mysql
  		mysql-connector-java
  		5.0.5
  	
  

Các dependency này chỉ bao gồm Hibernate và trình điều khiển Java cho MySQL.

Lưu ý:

  • Phiên bản Hibernate được sử dụng là sử dụng Annotation và cấu hình XML
  • MySQL driver được chọn phù hợp với phiên bản MySQL đang dùng (ở đây là 5.0.5)
  • Hibernate 4 sử dụng hệ thống logging của JBoss, vì vậy không cần khai báo thêm thư viện ****slf4j nếu bạn dùng phiên bản mới.

Bạn có thể xác nhận được điều này trong phần Maven Dependencies ****của dự án. Nếu bạn sử dụng Hibernate cũ hơn, có thể bạn sẽ cần thêm các dependency cho thư viện slf4j.

Lớp Mô hình (Model Classes) cho ánh xạ One-to-One trong Hibernate

Các lớp mô hình dùng để ánh xạ quan hệ one-to-one giữa hai bảng TransactionCustomer trong cơ sở dữ liệu sẽ được định nghĩa như sau:

package com.journaldev.hibernate.model;

import java.util.Date;

public class Txn {

	private long id;
	private Date date;
	private double total;
	private Customer customer;
	
	@Override
	public String toString(){
		return id+", "+total+", "+customer.getName()+", "+customer.getEmail()+", "+customer.getAddress();
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public double getTotal() {
		return total;
	}
	public void setTotal(double total) {
		this.total = total;
	}
	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	
}
package com.journaldev.hibernate.model;

public class Customer {

	private long id;
	private String name;
	private String email;
	private String address;
	
	private Txn txn;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Txn getTxn() {
		return txn;
	}
	public void setTxn(Txn txn) {
		this.txn = txn;
	}
	
}

Vì ta đang sử dụng cấu hình ánh xạ bằng XML, nên hai lớp trên là những Java Bean đơn giản ****(POJO) chỉ bao gồm các thuộc tính và phương thức getter/setter. Tên lớp Txn được dùng thay cho Transaction để tránh xung đột với lớp Transaction có sẵn trong API của Hibernate.

Cấu hình Hibernate One-to-One Mapping (Dùng XML)

Dưới đây là phần cấu hình Hibernate để ánh xạ quan hệ one-to-one giữa hai bảng TransactionCustomer thông qua các file mapping XML  txn.hbm.xml.


">

	
		
			
			
		
		
			
		
		
			
		
		
	
	

Lưu ý quan trọng: Phần tử dùng để ánh xạ thuộc tính customer customer.hbm.xmlcho thấy đây là một quan hệ one-to-one.


" >


	
		
			
			
				txn
			
		
		

		
			
		
		
			
		
		
			
		
	


generator class="foreign" dùng để chỉ ra rằng khóa chính của bảng Customer là khóa ngoại đến bảng Transaction.

Tập tin cấu hình Hibernate

Dưới đây là tập tin cấu hình Hibernate cho ánh xạ one-to-one sử dụng cấu hình XML hibernate.cfg.xml.


">

    
        com.mysql.jdbc.Driver
        pankaj123
        jdbc:mysql://localhost/TestDB
        pankaj
        org.hibernate.dialect.MySQLDialect
        
        thread
        true
        
        
        
    

Tập tin cấu hình Hibernate thì đơn giản, bao gồm các thuộc tính kết nối cơ sở dữ liệu và các tập tin ánh xạ Hibernate**.**

Tiện ích Hibernate SessionFactory

Dưới đây là lớp tiện ích để tạo một thể hiện SessionFactory trong Hibernate:

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

Chỉ đơn giản như vậy thôi. Hãy cùng viết chương trình để test ánh xạ trong Hibernate một-một sử dụng cấu hình XML.

Chạy chương trình kiểm thử cấu hình Hibernate One-to-One (XML)

Trong chương trình kiểm thử này, đầu tiên chúng ta sẽ tạo một đối tượng Txn và lưu nó. Sau khi lưu vào cơ sở dữ liệu, chúng ta sẽ sử dụng ID được sinh ra để truy xuất đối tượng Txn và in thông tin ra.

package com.journaldev.hibernate.main;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Customer;
import com.journaldev.hibernate.model.Txn;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateOneToOneMain {

	public static void main(String[] args) {
		
		Txn txn = buildDemoTransaction();
		
		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 object
		session.save(txn);
		//Commit transaction
		tx.commit();
		System.out.println("Transaction ID="+txn.getId());
		
		//Get Saved Trasaction Data
		printTransactionData(txn.getId(), sessionFactory);
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

	private static void printTransactionData(long id, SessionFactory sessionFactory) {
		Session session = null;
		Transaction tx = null;
		try{
			//Get Session
			sessionFactory = HibernateUtil.getSessionFactory();
			session = sessionFactory.getCurrentSession();
			//start transaction
			tx = session.beginTransaction();
			//Save the Model object
			Txn txn = (Txn) session.get(Txn.class, id);
			//Commit transaction
			tx.commit();
			System.out.println("Transaction Details=\\n"+txn);
			
			}catch(Exception e){
				System.out.println("Exception occured. "+e.getMessage());
				e.printStackTrace();
			}
	}

	private static Txn buildDemoTransaction() {
		Txn txn = new Txn();
		txn.setDate(new Date());
		txn.setTotal(100);
		
		Customer cust = new Customer();
		cust.setAddress("Bangalore, India");
		cust.setEmail("pankaj@gmail.com");
		cust.setName("Pankaj Kumar");
		
		txn.setCustomer(cust);
		
		cust.setTxn(txn);
		return txn;
	}

}

Kết quả khi chúng ta chạy thử chương trình:

Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into TRANSACTION (txn_date, txn_total) values (?, ?)
Hibernate: insert into CUSTOMER (cust_name, cust_email, cust_address, txn_id) values (?, ?, ?, ?)
Transaction ID=19
Hibernate: select txn0_.txn_id as txn_id1_1_0_, txn0_.txn_date as txn_date2_1_0_, txn0_.txn_total as txn_tota3_1_0_, 
customer1_.txn_id as txn_id1_0_1_, customer1_.cust_name as cust_nam2_0_1_, customer1_.cust_email as cust_ema3_0_1_, 
customer1_.cust_address as cust_add4_0_1_ from TRANSACTION txn0_ left outer join CUSTOMER customer1_ on 
txn0_.txn_id=customer1_.txn_id where txn0_.txn_id=?
Transaction Details=
19, 100.0, Pankaj Kumar, pankaj@gmail.com, Bangalore, India
Closing SessionFactory

Như bạn có thể thấy, chương trình hoạt động tốt và chúng ta có thể truy xuất dữ liệu từ cả hai bảng bằng cách sử dụng transaction id (mã giao dịch). Hãy kiểm tra câu lệnh SQL mà Hibernate sử dụng bên trong để lấy dữ liệu – nó đã dùng join (phép nối) để truy vấn dữ liệu từ cả hai bảng.

Cấu hình Hibernate One-to-One Mapping (Dùng Annotation)

Ở phần trước, chúng ta đã xem cách sử dụng cấu hình XML để thiết lập quan hệ one-to-one trong Hibernate. Bây giờ, hãy cùng tìm hiểu cách sử dụng JPA và Annotation của Hibernate để thực hiện điều tương tự.

Tập tin Cấu hình Hibernate

hibernate-annotation.cfg.xml


">

    
        com.mysql.jdbc.Driver
        pankaj123
        jdbc:mysql://localhost/TestDB
        pankaj
        org.hibernate.dialect.MySQLDialect
        
        thread
        true
        
        
        
    

Cấu hình Hibernate ở đây khá đơn giản. Như bạn thấy, ta có hai lớp mô hình là Txn1Customer1 , đây là hai lớp sẽ được sử dụng với các annotation.

Lớp mô hình trong ví dụ Mapping One-to-One bằng Annotation

Đối với cấu hình sử dụng annotation, phần quan trọng nhất chính là các lớp mô hình (model classes). Hãy cùng xem mô phỏng các lớp mô hình:

package com.journaldev.hibernate.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;

@Entity
@Table(name="TRANSACTION")
public class Txn1 {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="txn_id")
	private long id;
	
	@Column(name="txn_date")
	private Date date;
	
	@Column(name="txn_total")
	private double total;
	
	@OneToOne(mappedBy="txn")
	@Cascade(value=org.hibernate.annotations.CascadeType.SAVE_UPDATE)
	private Customer1 customer;
	
	@Override
	public String toString(){
		return id+", "+total+", "+customer.getName()+", "+customer.getEmail()+", "+customer.getAddress();
	}

        //Getter-Setter methods, omitted for clarity 
}

Hầu hết các annotation ở đây đều đến từ Java Persistence API (JPA) vì Hibernate ****cung cấp phần hiện thực cho JPA. Tuy nhiên, đối với xếp tầng (cascading), chúng ta cần dùng annotation riêng của Hibernate: org.hibernate.annotations.Cascade và kiểu liệt kê (enum) org.hibernate.annotations.CascadeType.

package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name="CUSTOMER")
public class Customer1 {

	@Id
	@Column(name="txn_id", unique=true, nullable=false)
	@GeneratedValue(generator="gen")
	@GenericGenerator(name="gen", strategy="foreign", parameters={@Parameter(name="property", value="txn")})
	private long id;
	
	@Column(name="cust_name")
	private String name;
	
	@Column(name="cust_email")
	private String email;
	
	@Column(name="cust_address")
	private String address;
	
	@OneToOne
	@PrimaryKeyJoinColumn
	private Txn1 txn;

        //Getter-Setter methods
}

Lưu ý: Chúng ta cần sử dụng annotation @GenericGenerator để cho Hibernate biết rằng id của bảng Customer sẽ được lấy từ đối tượng txn, thay vì được tự động tạo ra như bình thường.

Lớp Tiện ích Hibernate SessionFactory

Việc tạo SessionFactory là độc lập với cách chúng ta cấu hình ánh xạ Hibernate (dù dùng XML hay Annotation). Dưới đây là lớp tiện ích (HibernateAnnotationUtil) dùng để tạo SessionFactory cho cấu hình sử dụng annotation:

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

Chương trình kiểm thử Hibernate One-to-One Mapping với Annotation

Dưới đây là một chương trình đơn giản để kiểm thử ánh xạ one-to-one bằng annotation trong Hibernate.

package com.journaldev.hibernate.main;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Customer1;
import com.journaldev.hibernate.model.Txn1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateOneToOneAnnotationMain {

	public static void main(String[] args) {
		
		Txn1 txn = buildDemoTransaction();
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Get Session
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created using annotations configuration");
		//start transaction
		tx = session.beginTransaction();
		//Save the Model object
		session.save(txn);
		//Commit transaction
		tx.commit();
		System.out.println("Annotation Example. Transaction ID="+txn.getId());
		
		//Get Saved Trasaction Data
		printTransactionData(txn.getId(), sessionFactory);
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

	private static void printTransactionData(long id, SessionFactory sessionFactory) {
		Session session = null;
		Transaction tx = null;
		try{
			//Get Session
			sessionFactory = HibernateAnnotationUtil.getSessionFactory();
			session = sessionFactory.getCurrentSession();
			//start transaction
			tx = session.beginTransaction();
			//Save the Model object
			Txn1 txn = (Txn1) session.get(Txn1.class, id);
			//Commit transaction
			tx.commit();
			System.out.println("Annotation Example. Transaction Details=\\n"+txn);
			
			}catch(Exception e){
				System.out.println("Exception occured. "+e.getMessage());
				e.printStackTrace();
			}
	}

	private static Txn1 buildDemoTransaction() {
		Txn1 txn = new Txn1();
		txn.setDate(new Date());
		txn.setTotal(100);
		
		Customer1 cust = new Customer1();
		cust.setAddress("San Jose, USA");
		cust.setEmail("pankaj@yahoo.com");
		cust.setName("Pankaj Kr");
		
		txn.setCustomer(cust);
		
		cust.setTxn(txn);
		return txn;
	}

}

Kết quả đầu ra

Khi bạn chạy chương trình trên, bạn sẽ thấy đầu ra tương tự như sau:

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created using annotations configuration
Hibernate: insert into TRANSACTION (txn_date, txn_total) values (?, ?)
Hibernate: insert into CUSTOMER (cust_address, cust_email, cust_name, txn_id) values (?, ?, ?, ?)
Annotation Example. Transaction ID=20
Hibernate: select txn1x0_.txn_id as txn_id1_1_0_, txn1x0_.txn_date as txn_date2_1_0_, txn1x0_.txn_total as txn_tota3_1_0_, 
customer1x1_.txn_id as txn_id1_0_1_, customer1x1_.cust_address as cust_add2_0_1_, customer1x1_.cust_email as cust_ema3_0_1_, 
customer1x1_.cust_name as cust_nam4_0_1_ from TRANSACTION txn1x0_ left outer join CUSTOMER customer1x1_ on 
txn1x0_.txn_id=customer1x1_.txn_id where txn1x0_.txn_id=?
Annotation Example. Transaction Details=
20, 100.0, Pankaj Kr, pankaj@yahoo.com, San Jose, USA
Closing SessionFactory

Nhận xét

Bạn có thể thấy kết quả đầu ra hoàn toàn giống với cấu hình Hibernate sử dụng XML. Việc dùng annotation giúp mã nguồn ngắn gọn và dễ bảo trì hơn.

Vậy là chúng ta đã hoàn thành ví dụ về Hibernate One-to-One Mapping sử dụng annotation. Bạn có thể tải toàn bộ project mẫu và thử nghiệm thêm để hiểu rõ hơn.

Download Hibernate OneToOne Mapping Project

Về tác giả

Chris Pham
Chris PhamTechnical Writer

I have over 5 years of experience writing technical documentation for tech products, making them accessible and user-friendly. My focus is always on providing clear and precise information. @#@ Tôi đã có hơn 5 năm kinh nghiệm viết tài liệu kỹ thuật cho các sản phẩm công nghệ, giúp người dùng dễ dàng tiếp cận và sử dụng. Tôi luôn tập trung vào việc cung cấp thông tin chính xác và dễ hiểu.

Cập nhật thông tin mới nhấtNhận các thông tin mới nhất về mối đe dọa, báo cáo an ninh mạng từ CyStack về hòm thư điện tử của bạn

Thảo luận (0)

Đăng nhập để thảo luận

Bài viết liên quan