Quan hệ <many-to-many> có thể được thực hiện bằng cách sử dụng Set collection trong java mà không chứa bất kỳ phần tử nào bị trùng lặp. Trong bài quan hệ one-to-many trong Hibernate chúng ta đã biết cách ánh xạ Set collection trong Hibernate. Trong bài này chúng ta tiếp tục sử dụng Set collection để ánh xạ quan hệ many-to-many trong Hibernate
Nội dung chính
Các công nghệ được sử dụng trong ví dụ này:
- Eclipse MARS 2
- MySQL 5.0.11
- JDK 1.8
- Hibernate 3.6.3
Cấu trúc project
Các bước thực hiện
Giả sử ta có mối quan hệ một nhân viên(employee) có một hoặc nhiều chứng chỉ(certificate). Và ngược lại một chứng chỉ(certificate) có một hoặc nhiều nhân viên(employee).
Định nghĩa các table trong MySQL
Để lưu trữ nhân viên, ta tạo bảng EMPLOYEE có cấu trúc như sau:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
Để lưu trữ chứng chỉ, ta tạo bảng CERTIFICATE có cấu trúc như sau:
create table CERTIFICATE ( id INT NOT NULL auto_increment, certificate_name VARCHAR(30) default NULL, PRIMARY KEY (id) );
Để cài đặt mối quan hệ many-to-many giữa các đối tượng EMPLOYEE và CERTIFICATE, chúng ta sẽ phải tạo một bảng trung gian chứa ID của Employee và và ID của Certificate như sau:
create table EMP_CERT ( employee_id INT NOT NULL, certificate_id INT NOT NULL, PRIMARY KEY (employee_id,certificate_id) );
Định nghĩa các lớp POJO
Tạo lớp POJO Certificate tương ứng với bảng CERTIFICATE. Lớp này phải impliemnts cả hai phương thức equals() và hashCode() để Java có thể xác định có hai phần tử/đối tượng nào đó giống nhau không.
public class Certificate { private int id; private String name; public Certificate() {} public Certificate(String name) { this.name = name; } public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getName() { return name; } public void setName( String name ) { this.name = name; } public boolean equals(Object obj) { if (obj == null) return false; if (!this.getClass().equals(obj.getClass())) return false; Certificate obj2 = (Certificate)obj; if((this.id == obj2.getId()) && (this.name.equals(obj2.getName()))) { return true; } return false; } public int hashCode() { int tmp = 0; tmp = ( id + name ).hashCode(); return tmp; } }
Tạo lớp POJO Employee được sử dụng để lưu trữ các đối tượng vào bảng EMPLOYEE và có một tập các chứng chỉ được lưu bằng tập Set có tên biến là certificates
import java.util.*; public class Employee { private int id; private String firstName; private String lastName; private int salary; private Set certificates; public Employee() {} public Employee(String fname, String lname, int salary) { this.firstName = fname; this.lastName = lname; this.salary = salary; } public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName( String first_name ) { this.firstName = first_name; } public String getLastName() { return lastName; } public void setLastName( String last_name ) { this.lastName = last_name; } public int getSalary() { return salary; } public void setSalary( int salary ) { this.salary = salary; } public Set getCertificates() { return certificates; } public void setCertificates( Set certificates ) { this.certificates = certificates; } }
Định nghĩa các Hibernate Mapping File
Chúng ta phải tạo các file mapping để hướng dẫn Hibernate làm thế nào để ánh xạ các lớp vào các bảng cơ sở dữ liệu. Phần tử <set> sẽ được sử dụng để thiết lập mối quan hệ many-to-many giữa các EMPLOYEE và CERTIFICATE.
Employee.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="vn.kienthuclaptrinh.hibernate.entity.Employee" table="EMPLOYEE"> <id name="id" type="int" column="id"> <generator class="native" /> </id> <set name="certificates" cascade="save-update" table="EMP_CERT"> <key column="employee_id" /> <many-to-many column="certificate_id" class="vn.kienthuclaptrinh.hibernate.entity.Certificate" /> </set> <property name="firstName" column="first_name" type="string" /> <property name="lastName" column="last_name" type="string" /> <property name="salary" column="salary" type="int" /> </class> <class name="vn.kienthuclaptrinh.hibernate.entity.Certificate" table="CERTIFICATE"> <id name="id" type="int" column="id"> <generator class="native" /> </id> <property name="name" column="certificate_name" type="string" /> </class> </hibernate-mapping>
Chúng ta hãy xem chi tiết hơn về các phần tử của file mapping trong Hibernate như sau:
- Tài liệu mapping là một tài liệu XML có <hibernate-mapping> là phần tử gốc chứa tất cả các phần tử <class>.
- Các phần tử <class> được sử dụng để định nghĩa ánh xạ cụ thể từ các lớp Java sang các bảng cơ sở dữ liệu. Tên lớp Java được chỉ định sử dụng thuộc tính name của phần tử lớp và tên bảng cơ sở dữ liệu được chỉ định sử dụng thuộc tính table.
- Phần tử <meta> là thành phần tùy chọn và có thể được sử dụng để tạo ra mô tả lớp.
- Phần tử <id> ánh xạ thuộc tính ID duy nhất trong lớp tới khóa chính của bảng cơ sở dữ liệu. Thuộc tính name của id id đề cập đến thuộc tính trong lớp và thuộc tính column đề cập đến cột trong bảng cơ sở dữ liệu. Thuộc tính type giữ kiểu ánh xạ hibernate, các kiểu mapping này sẽ chuyển đổi từ kiểu dữ liệu Java sang SQL.
- Phần tử <generator> bên trong phần tử id được sử dụng để tự động tạo giá trị cho khóa chính. Thiết lập thuộc tính class của phần tử generator được đặt là native để cho phép hibernate chọn identity, sequence hoặc hilo để tạo khoá chính tùy thuộc vào khả năng của cơ sở dữ liệu.
- Phần tử <property> được sử dụng để ánh xạ một thuộc tính của lớp Java vào một cột trong bảng cơ sở dữ liệu. Thuộc tính name của phần tử đề cập đến thuộc tính trong lớp và thuộc tính column đề cập đến cột trong bảng cơ sở dữ liệu. Thuộc tính type giữ kiểu ánh xạ hibernate, các kiểu mapping này sẽ chuyển đổi từ kiểu dữ liệu Java sang SQL.
- Phần tử <set> thiết lập mối quan hệ giữa các lớp Certificate và Employee. Chúng ta thiết lập thuộc tính cascade là save-update để nói cho Hibernate để persist các đối tượng Certificate đối với các thao tác lưu tức CREATE và UPDATE tại cùng thời điểm với các đối tượng Employee. Thuộc tính name có giá trị là tên biến được định nghĩa trong lớp cha, trong trường hợp của chúng ta đó là certificates. Đối với mỗi biến Set, chúng ta cần phải xác định một phần tử set riêng biệt trong file mapping. Ở đây chúng ta sử dụng thuộc tính table để thiết lập tên bảng trung gian EMP_CERT.
- Phần tử <key> là các cột trong bảng EMP_CERT là khóa ngoại của đối tượng cha tức là EMPLOYEE và liên kết đến certification_id trong bảng CERTIFICATE.
- Phần tử <many-to-many> chỉ ra rằng một đối tượng Employee liên quan đến nhiều đối tượng Certificate và các thuộc tính column được sử dụng để liên kết đến bảng trung gian EMP_CERT .
Tạo file cấu hình Hibernate
File cấu hình Hibernate được sử dụng để định nghĩa các thông tin kết nối database (trong ví dụ này là MySQL) và liên kết đến các file mapping.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- Thông tin kết nối db --> <property name="hibernate.connection.url"> jdbc:mysql://localhost/testdb </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> 1234567890 </property> <!-- List các file mapping XML --> <mapping resource="Employee.hbm.xml" /> <mapping resource="Certificate.hbm.xml" /> </session-factory> </hibernate-configuration>
Tạo lớp ứng ụng
Cuối cùng, chúng ta sẽ tạo lớp chứa phương thức main () để chạy ứng dụng. Chúng ta sẽ sử dụng ứng dụng này để lưu các employee cùng với các certificate của họ. Sau đó áp dụng các thao tác CRUD bản ghi đã tạo.
import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import vn.kienthuclaptrinh.hibernate.entity.Certificate; import vn.kienthuclaptrinh.hibernate.entity.Employee; public class ManageEmployee { private static SessionFactory factory; public static void main(String[] args) { try { factory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { System.err.println("Failed to create sessionFactory object." + ex); throw new ExceptionInInitializerError(ex); } ManageEmployee ME = new ManageEmployee(); // Let us have a set of certificates for the first employee HashSet certificates = new HashSet(); certificates.add(new Certificate("MCA")); certificates.add(new Certificate("MBA")); certificates.add(new Certificate("PMP")); // Add employee records in the database Integer empID1 = ME.addEmployee("Ho", "Van A", 4000, certificates); // Add another employee record in the database Integer empID2 = ME.addEmployee("Vo", "Van B", 3000, certificates); // List down all the employees ME.listEmployees(); // Update employee's salary records ME.updateEmployee(empID1, 5000); // Delete an employee from the database ME.deleteEmployee(empID2); // List down all the employees ME.listEmployees(); } // Method to add an employee record in the database public Integer addEmployee(String fname, String lname, int salary, Set cert) { Session session = factory.openSession(); Transaction tx = null; Integer employeeID = null; try { tx = session.beginTransaction(); Employee employee = new Employee(fname, lname, salary); employee.setCertificates(cert); employeeID = (Integer) session.save(employee); tx.commit(); } catch (HibernateException e) { if (tx != null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } return employeeID; } // Method to list all the employees detail public void listEmployees() { Session session = factory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); List employees = session.createQuery("FROM Employee").list(); for (Iterator iterator1 = employees.iterator(); iterator1.hasNext();) { Employee employee = (Employee) iterator1.next(); System.out.print("First Name: " + employee.getFirstName()); System.out.print(" Last Name: " + employee.getLastName()); System.out.println(" Salary: " + employee.getSalary()); Set certificates = employee.getCertificates(); for (Iterator iterator2 = certificates.iterator(); iterator2.hasNext();) { Certificate certName = (Certificate) iterator2.next(); System.out.println("Certificate: " + certName.getName()); } } tx.commit(); } catch (HibernateException e) { if (tx != null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } } // Method to update salary for an employee public void updateEmployee(Integer EmployeeID, int salary) { Session session = factory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Employee employee = (Employee) session.get(Employee.class, EmployeeID); employee.setSalary(salary); session.update(employee); tx.commit(); } catch (HibernateException e) { if (tx != null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } } // Method to delete an employee from the records public void deleteEmployee(Integer EmployeeID) { Session session = factory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Employee employee = (Employee) session.get(Employee.class, EmployeeID); session.delete(employee); tx.commit(); } catch (HibernateException e) { if (tx != null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } } }
Kết quả chạy trên Eclipse
First Name: Ho Last Name: Van A Salary: 4000 Certificate: MCA Certificate: PMP Certificate: MBA First Name: Vo Last Name: Van B Salary: 3000 Certificate: MCA Certificate: PMP Certificate: MBA First Name: Ho Last Name: Van A Salary: 5000 Certificate: MCA Certificate: PMP Certificate: MBA
Download Source Code Eclipse
Hibernate example có sử dụng maven Download Now!
Hibernate example không sử dụng maven Download Now!