Hướng dẫn lập trình Java Hibernate cho người mới bắt đầu
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- Giới thiệu

Tài liệu này được viết dựa trên:
  • Hibernate 5.2.2.Final

  • Eclipse 4.6 (MARS)

History:
  • Last Update: 14-09-2016 (dd-MM-yyyy).
Trong tài liệu này tôi hướng dẫn bạn làm việc với Hibernate 5. Nó có rất nhiều thay đổi so với phiên bản 4. Một số thay đổi quan trọng được đề cập ở dưới đây:
  • Class org.hibernate.Query đã bị lỗi thời, và được thay thế bởi org.hibernate.query.Query
  • Class org.hibernate.Session đã được cập nhập thành để sử dụng kiểu Generics.
Chi tiết các thay đổi:
  1. Hibernate 5 là điểm bắt đầu cho Hibernate Spatial, nó là một phần của dự án Hibernate, như vậy chúng ta có thể xử lý các dữ liệu GIS (Dữ liệu bản đồ số).
  2. Hỗ trợ Java 8 Date và Time trong domain model mappings. Các ánh xạ giữ các kiểu Date tiêu chuẩn trong SQL và Java 8 Date Time như dưới đây:
    • DATE: java.time.LocalDate
    • TIME: java.time.LocalTime, java.time.OffsetTime
    • TIMESTAMP: java.time.Instant, java.time.LocalDateTime, java.time.OffsetDateTimejava.time.ZonedDateTime
  3. Các cơ chế cải tiến bytecode đã được thiết kế lại từ đầu, và Hibernate có cả hai tính năng plugin Maven và Gradle. Có ba khía cạnh chính được cải tiến:
    • Lazy initialization: Các trường (fields) có thể được khai báo là LAZY và chúng sẽ được lấy (fetched) chỉ khi chúng được truy cập lần đầu tiên.
    • Dirty checking: Các Entities được cải tiến để có thể nắm được tất cả các thuộc tính bị thay đổi sau khi được nạp (loaded) vào Persistence Context.
    • Bidirectional associations: Nó có thể đồng bộ hai phía của một liên hợp hai chiều (bidirectional association) một cách tự động, thậm trí trong trường hợp người lập trình chỉ cập nhập một phía.
  4. Hibernate's native APIs (Session, ...) cập nhập API để sử dụng các kiểu Generic. Không cần phải ép kiểu khi quét lấy (fetching) các entity.
  5. Hibernate 5.0 mở rộng hơn cho các kiểu (Chẳng hạn UUID).
  6. Bộ đệm thứ cấp cho các kiểu tham chiếu. Tính năng này cho phép trực tiếp lưu trữ các tham chiếu của các entity vào một bộ nhớ đệm (cache) thứ cấp đối với các entity không thay đổi.

2- Chuẩn bị Database

Hibernate vốn là một thư viện sinh ra để làm việc với mọi loại DB, nó không phụ thuộc vào bạn chọn loại DB nào. Nếu Java là "Viết 1 lần chạy mọi nơi" thì Hibernate "Viết 1 lần chạy trên mọi loại DB"
Trong tài liệu này, tôi sử dụng simplehr, nó là một database đơn giản, được sử dụng trong nhiều hướng dẫn trên o7planning. Bạn có thể tạo nó với một trong các loại cơ sở dữ liệu Oracle, MySQL hoặc SQL Server. Xem hướng dẫn dưới đây:

3- Tạo Maven Project & Khai báo thư viện

Ở đây tôi tạo một Maven project, và khai báo các thư viện Hibernate trong pom.xml.
  • File/New/Other...
Project đã được tạo ra.
Trong pom.xml tôi khai báo thư viện Hibernate 5, và các thư viện JDBC cho các loại Database khác nhau Oracle, MySQLSQL Server.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
               http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.o7planning</groupId>
    <artifactId>HibernateTutorial</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>HibernateTutorial</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <repositories>
        <!-- Repository for ORACLE JDBC Driver -->
        <repository>
            <id>codelds</id>
            <url>https://code.lds.org/nexus/content/groups/main-repo</url>
        </repository>
    </repositories>
   
   
    <dependencies>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <!-- Hibernate Core -->
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.2.Final</version>
        </dependency>


        <!-- MySQL JDBC driver -->
        <!-- http://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
        </dependency>

        <!-- Oracle JDBC driver -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>

        <!-- SQLServer JDBC driver (JTDS) -->
        <!-- http://mvnrepository.com/artifact/net.sourceforge.jtds/jtds -->
        <dependency>
            <groupId>net.sourceforge.jtds</groupId>
            <artifactId>jtds</artifactId>
            <version>1.3.1</version>
        </dependency>

    </dependencies>
   
</project>

 

4- Các Class Entity

Chúng ta tạo các class Entity. Mỗi Entity sẽ mô tả một bảng trong DB. Hãy khoan nói về những gì có trong các class đó.
  1. Department - Phòng ban
  2. Employee - Nhân viên
  3. SalaryGrade - Bậc lương
  4. Timekeeper - Máy chấm công, giờ ra vào của nhân viên.
Department.java
package org.o7planning.tutorial.hibernate.entities;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "DEPARTMENT",
  uniqueConstraints = { @UniqueConstraint(columnNames = { "DEPT_NO" }) })
public class Department {

private Integer deptId;
private String deptNo;

private String deptName;
private String location;
private Set<Employee> employees = new HashSet<Employee>(0);

public Department() {
}

public Department(Integer deptId, String deptName, String location) {
   this.deptId = deptId;
   this.deptNo = "D" + this.deptId;
   this.deptName = deptName;
   this.location = location;
}

@Id
@Column(name = "DEPT_ID")
public Integer getDeptId() {
   return deptId;
}

public void setDeptId(Integer deptId) {
   this.deptId = deptId;
}

@Column(name = "DEPT_NO", length = 20, nullable = false)
public String getDeptNo() {
   return deptNo;
}

public void setDeptNo(String deptNo) {
   this.deptNo = deptNo;
}

@Column(name = "DEPT_NAME", nullable = false)
public String getDeptName() {
   return deptName;
}

public void setDeptName(String deptName) {
   this.deptName = deptName;
}

@Column(name = "LOCATION")
public String getLocation() {
   return location;
}

public void setLocation(String location) {
   this.location = location;
}

@OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
public Set<Employee> getEmployees() {
   return employees;
}

public void setEmployees(Set<Employee> employees) {
   this.employees = employees;
}
}
Employee.java
package org.o7planning.tutorial.hibernate.entities;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "EMPLOYEE",
   uniqueConstraints = { @UniqueConstraint(columnNames = { "EMP_NO" }) })
public class Employee {
private Long empId;
private String empNo;

private String empName;
private String job;
private Employee manager;
private Date hideDate;
private Float salary;
private byte[] image;

private Department department;
private Set<Employee> employees = new HashSet<Employee>(0);

public Employee() {
}

public Employee(Long empId, String empName, String job, Employee manager,
        Date hideDate, Float salary, Float comm, Department department) {
    this.empId = empId;
    this.empNo = "E" + this.empId;
    this.empName = empName;
    this.job = job;
    this.manager = manager;
    this.hideDate = hideDate;
    this.salary = salary;
    this.department = department;
}

@Id
@Column(name = "EMP_ID")
public Long getEmpId() {
    return empId;
}

public void setEmpId(Long empId) {
    this.empId = empId;
}

@Column(name = "EMP_NO", length = 20, nullable = false)
public String getEmpNo() {
    return empNo;
}

public void setEmpNo(String empNo) {
    this.empNo = empNo;
}

@Column(name = "EMP_NAME", length = 50, nullable = false)
public String getEmpName() {
    return empName;
}

public void setEmpName(String empName) {
    this.empName = empName;
}

@Column(name = "JOB", length = 30, nullable = false)
public String getJob() {
    return job;
}

public void setJob(String job) {
    this.job = job;
}

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MNG_ID")
public Employee getManager() {
    return manager;
}

public void setManager(Employee manager) {
    this.manager = manager;
}

@Column(name = "HIRE_DATE", nullable = false)
@Temporal(TemporalType.DATE)
public Date getHideDate() {
    return hideDate;
}

public void setHideDate(Date hideDate) {
    this.hideDate = hideDate;
}

@Column(name = "SALARY", nullable = false)
public Float getSalary() {
    return salary;
}

public void setSalary(Float salary) {
    this.salary = salary;
}

@Column(name = "IMAGE", length = 1111111, nullable = true)
@Lob
public byte[] getImage() {
    return image;
}

public void setImage(byte[] image) {
    this.image = image;
}

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "DEPT_ID", nullable = false)
public Department getDepartment() {
    return department;
}

public void setDepartment(Department department) {
    this.department = department;
}

@OneToMany(fetch = FetchType.LAZY, mappedBy = "empId")
public Set<Employee> getEmployees() {
    return employees;
}

public void setEmployees(Set<Employee> employees) {
    this.employees = employees;
}

}
SalaryGrade.java
package org.o7planning.tutorial.hibernate.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "SALARY_GRADE")
public class SalaryGrade {
 private Integer grade;
 private Float lowSalary;
 private Float highSalary;

 public SalaryGrade() {
 }

 public SalaryGrade(Integer grade, Float lowSalary, Float highSalary) {
     this.grade = grade;
     this.lowSalary = lowSalary;
     this.highSalary = highSalary;
 }

 @Id
 @Column(name = "GRADE")
 public Integer getGrade() {
     return grade;
 }

 public void setGrade(Integer grade) {
     this.grade = grade;
 }

 @Column(name = "LOW_SALARY", nullable = false)
 public Float getLowSalary() {
     return lowSalary;
 }

 public void setLowSalary(Float lowSalary) {
     this.lowSalary = lowSalary;
 }

 @Column(name = "HIGH_SALARY", nullable = false)
 public Float getHighSalary() {
     return highSalary;
 }

 public void setHighSalary(Float highSalary) {
     this.highSalary = highSalary;
 }
}
Timekeeper.java
package org.o7planning.tutorial.hibernate.entities;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "TIMEKEEPER")
public class Timekeeper {
  public static final char IN = 'I';
  public static final char OUT = 'O';

  private String timekeeperId;

  private Date dateTime;

  private Employee employee;

  // 'I' or 'O'
  private char inOut;

  @Id
  @GeneratedValue(generator = "uuid")
  @GenericGenerator(name = "uuid", strategy = "uuid2")
  @Column(name = "Timekeeper_Id", length = 36)
  public String getTimekeeperId() {
      return timekeeperId;
  }

  public void setTimekeeperId(String timekeeperId) {
      this.timekeeperId = timekeeperId;
  }

  @Column(name = "Date_Time", nullable = false)
  @Temporal(TemporalType.TIMESTAMP)
  public Date getDateTime() {
      return dateTime;
  }

  public void setDateTime(Date dateTime) {
      this.dateTime = dateTime;
  }

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "EMP_ID", nullable = false)
  public Employee getEmployee() {
      return employee;
  }

  public void setEmployee(Employee employee) {
      this.employee = employee;
  }

  @Column(name = "In_Out", nullable = false, length = 1)
  public char getInOut() {
      return inOut;
  }

  public void setInOut(char inOut) {
      this.inOut = inOut;
  }

}

5- Cấu hình Hibernate

Mục đích là để Hibernate đọc được vào Database, cũng như khai báo danh sách các Entity, bạn đã tạo ở bước trên.
Tạo file cấu hình hibernate.cfg.xml đặt tại src/main/java
hibernate.cfg.xml (ORACLE)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                          "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
 <session-factory>

     <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
     <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
     <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:db11g</property>
     <property name="hibernate.connection.username">simplehr</property>
     <property name="hibernate.connection.password">12345</property>
     <property name="hibernate.show_sql">true</property>
     <property name="hibernate.connection.release_mode">auto</property>
     <property name="current_session_context_class">thread</property>
     <property name="hibernate.connection.autoReconnect">true</property>


     <mapping class="org.o7planning.tutorial.hibernate.entities.Department" />
     <mapping class="org.o7planning.tutorial.hibernate.entities.Employee" />
     <mapping class="org.o7planning.tutorial.hibernate.entities.SalaryGrade" />
     <mapping class="org.o7planning.tutorial.hibernate.entities.Timekeeper" />

 </session-factory>

</hibernate-configuration>
hibernate.cfg.xml (MySQL)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

  <session-factory>
      <!-- Database connection settings -->
      <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
      <property name="connection.url">jdbc:mysql://tran-vmware:3306/simplehr?serverTimezone=UTC</property>
      <property name="connection.username">root</property>
      <property name="connection.password">1234</property>

      <!-- JDBC connection pool (use the built-in) -->
      <property name="connection.pool_size">1</property>

      <!-- SQL dialect -->
      <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

      <!-- Enable Hibernate's automatic session context management -->
      <property name="current_session_context_class">thread</property>

      <!-- Disable the second-level cache -->
      <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

      <!-- Echo all executed SQL to stdout -->
      <property name="show_sql">true</property>

      <mapping class="org.o7planning.tutorial.hibernate.entities.Department" />
      <mapping class="org.o7planning.tutorial.hibernate.entities.Employee" />
      <mapping class="org.o7planning.tutorial.hibernate.entities.SalaryGrade" />
      <mapping class="org.o7planning.tutorial.hibernate.entities.Timekeeper" />

  </session-factory>

</hibernate-configuration>
hibernate.cfg.xml (SQL Server)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
      "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">net.sourceforge.jtds.jdbc.Driver</property>
        <property name="connection.url">jdbc:jtds:sqlserver://localhost:1433/simplehr;instance=SQLEXPRESS</property>
        <property name="connection.username">sa</property>
        <property name="connection.password">1234</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.SQLServerDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache -->
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <mapping class="org.o7planning.tutorial.hibernate.entities.Department" />
        <mapping class="org.o7planning.tutorial.hibernate.entities.Employee" />
        <mapping class="org.o7planning.tutorial.hibernate.entities.SalaryGrade" />
        <mapping class="org.o7planning.tutorial.hibernate.entities.Timekeeper" />

    </session-factory>
</hibernate-configuration>
Với mỗi loại Database khác nhau có một Dialect khác nhau.
Ví dụ:
Dialect cho Oracle:
  • org.hibernate.dialect.Oracle10gDialect (Dùng cho 10g &11g)
  • org.hibernate.dialect.Oracle12cDialect
Dialect cho SQL Server:
  • org.hibernate.dialect.SQLServerDialect
  • org.hibernate.dialect.SQLServer2012Dialect
  • org.hibernate.dialect.SQLServer2008Dialect
Dialect cho MySQL
  • org.hibernate.dialect.MySQLDialect
  • org.hibernate.dialect.MySQL5Dialect
Chú ý: org.hibernate.dialect.Oracle10gDialect được sử dụng cho cả Oracle 10g, 11g, 12c.

Dialect là gì?

Dialect là một class, nó nói với Hibernate cách chuyển các kiểu dữ liệu của Database thành các kiểu dữ liệu của Java, và ngược lại. Đồng thời nó cũng được sử dụng để định nghĩa cách chuyển đổi giữa các hàm của HSQL (Hibernate SQL) thành các hàm của Database tương ứng.
Java SQL Type Oracle My SQL SQL Server
Types.BIT number(1,0) bit bit
Types.BIGINT number(19,0) bigint bigint
Types.DATE date date date
 .......      
Types.CLOB clob longtext varchar(MAX)
Types.BLOB blob longblob varbinary(MAX)

6- SessionFactory

HibernateUtils.java
package org.o7planning.tutorial.hibernate;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtils {

  private static final SessionFactory sessionFactory = buildSessionFactory();

  // Hibernate 5:
  private static SessionFactory buildSessionFactory() {
      try {
          // Tạo đối tượng ServiceRegistry từ hibernate.cfg.xml
          ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()//
                  .configure("hibernate.cfg.xml").build();

 
          // Tạo nguồn siêu dữ liệu (metadata) từ ServiceRegistry
          Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder().build();

          return metadata.getSessionFactoryBuilder().build();
      } catch (Throwable ex) {
     
          System.err.println("Initial SessionFactory creation failed." + ex);
          throw new ExceptionInInitializerError(ex);
      }
  }

  public static SessionFactory getSessionFactory() {
      return sessionFactory;
  }

  public static void shutdown() {
      // Giải phóng cache và Connection Pools.
      getSessionFactory().close();
  }

}

7- Hibernate Query Language (HQL)

Hibernate sử dụng ngôn ngữ Hibernate Query Language ( HQL) để query dữ liệu. HQL có một chút khác biệt với SQL mà bạn đã biết.

SQL:
  • Query dữ liệu trên các bảng.
HQL:
  • Query dữ liệu trên các class Entity.
-- SQL
-- Đây là một câu lệnh SQL query dữ liệu trên bảng DEPARTMENT.
Select d.DEPT_NO, d.DEPT_NAME from DEPARTMENT d;

-- HQL
-- Đây là một câu lệnh HQL query dữ liệu trên Entity Department.
Select d.deptNo, d.deptName from Department d;

-- Query Object
Select d from Department d;

Nguyên tắc hoạt động của Hibernate:

Ứng dụng của bạn viết các câu lệnh HQL trong thời gian chạy Hibernate tự biết nó đang làm việc với loại Database nào, nó sẽ tự chuyển đổi các câu lệnh HQL sang SQL tương ứng với loại DB đó. Thực tế chúng ta biết rằng cú pháp SQL có sự khác nhau đôi chút giữa các loại Database khác nhau. Và kiểu dữ liệu cũng vậy.
Bạn có thể tham khảo thêm về cú pháp HQL tại:

 

8- Query dữ liệu với Hibernate

Có nhiều cách để lấy dữ liệu với Hibernate. Trong phần này tôi sẽ giới thiệu một vài cách lấy dữ liệu thông dụng.

8.1- Query Object sử dụng HQL

Ví dụ đầu tiên, sử dụng HQL query các đối tượng Entity:
QueryObjectDemo.java
package org.o7planning.tutorial.hibernate.query;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class QueryObjectDemo {

  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();

      try {
          // Tất cả các lệnh hành động với DB thông qua Hibernate
          // đều phải nằm trong 1 giao dịch (Transaction)
          // Bắt đầu giao dịch
          session.getTransaction().begin();

          // Tạo một câu lệnh HQL query object.
          // Tương đương với Native SQL:
          // Select e.* from EMPLOYEE e order by e.EMP_NAME, e.EMP_NO

          String sql = "Select e from " + Employee.class.getName() + " e "
                  + " order by e.empName, e.empNo ";

          // Tạo đối tượng Query.
          Query<Employee> query = session.createQuery(sql);

          // Thực hiện truy vấn.
          List<Employee> employees = query.getResultList();

          for (Employee emp : employees) {
              System.out.println("Emp: " + emp.getEmpNo() + " : "
                      + emp.getEmpName());
          }

          // Commit dữ liệu
          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          // Rollback trong trường hợp có lỗi xẩy ra.
          session.getTransaction().rollback();
      }
  }
 
}
Kết quả chạy ví dụ:

Demo2:

QueryObjectDemo2.java
package org.o7planning.tutorial.hibernate.query;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class QueryObjectDemo2 {

  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();

      try {
          // Tất cả các lệnh hành động với DB thông qua Hibernate
          // đều phải nằm trong 1 giao dịch (Transaction)
          // Bắt đầu giao dịch
          session.getTransaction().begin();

          // Tạo một câu lệnh HQL query object.
          // HQL Có tham số.
          // Tương đương với Native SQL:
          // Select e.* from EMPLOYEE e cross join DEPARTMENT d
          // where e.DEPT_ID = d.DEPT_ID and d.DEPT_NO = :deptNo;

          String sql = "Select e from " + Employee.class.getName() + " e "
                  + " where e.department.deptNo=:deptNo ";

          // Tạo đối tượng Query.
          Query<Employee> query = session.createQuery(sql);

          query.setParameter("deptNo", "D10");

          // Thực hiện truy vấn.
          List<Employee> employees = query.getResultList();

          for (Employee emp : employees) {
              System.out.println("Emp: " + emp.getEmpNo() + " : "
                      + emp.getEmpName());
          }

          // Commit dữ liệu
          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          // Rollback trong trường hợp có lỗi xẩy ra.
          session.getTransaction().rollback();
      }
  }
}

8.2- Query lấy dữ liệu một vài cột sử dụng HQL

QuerySomeColumnDemo.java
package org.o7planning.tutorial.hibernate.query;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class QuerySomeColumnDemo {

   public static void main(String[] args) {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session = factory.getCurrentSession();

       try {
           session.getTransaction().begin();

           // Query một vài cột.
           // Việc lấy dữ liệu trong trường hợp này sẽ phức tạp hơn.
           
    
           String sql = "Select e.empId, e.empNo, e.empName from "
                   + Employee.class.getName() + " e ";

           Query<Object[]> query = session.createQuery(sql);

           // Thực hiện truy vấn.
           // Lấy ra danh sách các đối tượng Object[]
 
           List<Object[]> datas = query.getResultList();

           for (Object[] emp : datas) {
               System.out.println("Emp Id: " + emp[0]);
               System.out.println(" Emp No: " + emp[1]);
               System.out.println(" Emp Name: " + emp[2]);
           }

           // Commit dữ liệu
           session.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           // Rollback trong trường hợp có lỗi xẩy ra.
           session.getTransaction().rollback();
       }

   }
}
Kết quả chạy ví dụ:

8.3- Query lấy dữ liệu một vài cột sử dụng HQL & JavaBean

Trong trường hợp bạn cần lấy dữ liệu một số cột trên một số bảng, cách tốt nhất là sử dụng Java beans. Class Java bean sẽ có một cấu tử để khởi tạo các giá trị các trường của nó. Cấu tử này tham gia vào câu truy vấn HQL.
ShortEmpInfo.java
package org.o7planning.tutorial.hibernate.beans;

public class ShortEmpInfo {

 private Long empId;
 private String empNo;
 private String empName;

 
 // Cấu tử có 3 tham số, sẽ được sử dụng trong Hibernate Query.
 
 public ShortEmpInfo(Long empId, String empNo, String empName) {
     this.empId = empId;
     this.empNo = empNo;
     this.empName = empName;
 }

 public Long getEmpId() {
     return empId;
 }

 public void setEmpId(Long empId) {
     this.empId = empId;
 }

 public String getEmpNo() {
     return empNo;
 }

 public void setEmpNo(String empNo) {
     this.empNo = empNo;
 }

 public String getEmpName() {
     return empName;
 }

 public void setEmpName(String empName) {
     this.empName = empName;
 }

}
ShortEmpInfoQueryDemo.java
package org.o7planning.tutorial.hibernate.query;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.beans.ShortEmpInfo;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class ShortEmpInfoQueryDemo {

   public static void main(String[] args) {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session = factory.getCurrentSession();

       try {
           session.getTransaction().begin();

           // Sử dụng cấu tử của Class ShortEmpInfo
           String sql = "Select new " + ShortEmpInfo.class.getName()
                   + "(e.empId, e.empNo, e.empName)" + " from "
                   + Employee.class.getName() + " e ";

           Query<ShortEmpInfo> query = session.createQuery(sql);

           // Thực hiện truy vấn.
           // Lấy ra danh sách các đối tượng ShortEmpInfo
           List<ShortEmpInfo> employees = query.getResultList();

           for (ShortEmpInfo emp : employees) {
               System.out.println("Emp: " + emp.getEmpNo() + " : "
                       + emp.getEmpName());
           }

 
           // Commit data.
           session.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           // Rollback trong trường hợp có lỗi xẩy ra.
           session.getTransaction().rollback();
       }
   }
   
}

8.4- Query Lấy dữ liệu duy nhất

UniqueResultDemo.java
package org.o7planning.tutorial.hibernate.query;

import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Department;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class UniqueResultDemo {

    public static Department getDepartment(Session session, String deptNo) {
        String sql = "Select d from " + Department.class.getName() + " d "//
                + " where d.deptNo= :deptNo ";
        Query<Department> query = session.createQuery(sql);
        query.setParameter("deptNo", deptNo);
        return (Department) query.getSingleResult();
    }

    public static Employee getEmployee(Session session, Long empId) {
        String sql = "Select e from " + Employee.class.getName() + " e "//
                + " where e.empId= :empId ";
        Query<Employee> query = session.createQuery(sql);
        query.setParameter("empId", empId);
        return (Employee) query.getSingleResult();
    }

    public static void main(String[] args) {
        SessionFactory factory = HibernateUtils.getSessionFactory();

        Session session = factory.getCurrentSession();

        try {
            session.getTransaction().begin();

            Department dept = getDepartment(session, "D10");
            Set<Employee> emps = dept.getEmployees();

            System.out.println("Dept Name: " + dept.getDeptName());
            for (Employee emp : emps) {
                System.out.println(" Emp name: " + emp.getEmpName());
            }

            Employee emp = getEmployee(session, 7839L);
            System.out.println("Emp Name: " + emp.getEmpName());

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }
    }
    
}

9- Đối tượng Transient, Persistent và Detached là gì?

DataUtils.java
package org.o7planning.tutorial.hibernate;

import org.hibernate.Session;
import org.hibernate.query.Query;
import org.o7planning.tutorial.hibernate.entities.Department;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class DataUtils {

   public static Department findDepartment(Session session, String deptNo) {
       String sql = "Select d from " + Department.class.getName() + " d "//
               + " Where d.deptNo = :deptNo";
       Query<Department> query = session.createQuery(sql);
       query.setParameter("deptNo", deptNo);
       return query.getSingleResult();
   }

   public static Long getMaxEmpId(Session session) {
       String sql = "Select max(e.empId) from " + Employee.class.getName() + " e ";
       Query<Number> query = session.createQuery(sql);
       Number value = query.getSingleResult();
       if (value == null) {
           return 0L;
       }
       return value.longValue();
   }

   public static Employee findEmployee(Session session, String empNo) {
       String sql = "Select e from " + Employee.class.getName() + " e "//
               + " Where e.empNo = :empNo";
       Query<Employee> query = session.createQuery(sql);
       query.setParameter("empNo", empNo);
       return query.getSingleResult();
   }

}
Đây là ví dụ đơn giản sử dụng Session.persist(Object) để insert một đối tượng Transient vào DB.
Khái niệm đối tượng Transitent, Persistent, Detached sẽ được giải thích trong ví dụ này.
PersistDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Department;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class PersistDemo {

   public static void main(String[] args) {
       
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session = factory.getCurrentSession();
       Department department = null;
       Employee emp = null;
       try {
           session.getTransaction().begin();

           Long maxEmpId = DataUtils.getMaxEmpId(session);
           Long empId = maxEmpId + 1;

           // Phòng ban với mã số D10.
           // Nó là đối tượng chịu sự quản lý của session
           // Và được gọi là đối tượng persistent.

           department = DataUtils.findDepartment(session, "D10");

           // Tạo mới đối tượng Employee
           // Đối tượng này chưa chịu sự quản lý của session.
           // Nó được coi là đối tượng Transient.
   
           emp = new Employee();
           emp.setEmpId(empId);
           emp.setEmpNo("E" + empId);
           emp.setEmpName("Name " + empId);
           emp.setJob("Coder");
           emp.setSalary(1000f);
           emp.setManager(null);
           emp.setHideDate(new Date());
           emp.setDepartment(department);

           // Sử dụng persist(..)        
           // Lúc này 'emp' đã chịu sự quản lý của session.
           // nó có trạng thái persistent.
           // Chưa có hành động gì với DB tại đây.
 
           session.persist(emp);

           // Tại bước này dữ liệu mới được đẩy xuống DB.
           // Câu lệnh Insert được tạo ra.
    
           session.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session.getTransaction().rollback();
       }

       // Sau khi session bị đóng lại (commit, rollback, close)
       // Đối tượng 'emp', 'dept' trở thành đối tượng Detached.
       // Nó không còn trong sự quản lý của session nữa.
     
       System.out.println("Emp No: " + emp.getEmpNo());
       
   }
   
}
Kết quả chạy ví dụ:

10- Vòng đời của Hibernate (Hibernate Lifecycle)

Class Session của Hibernate có một số các method quan trọng. Chúng được chia ra các nhóm như hình minh họa dưới đây.
Một đối tượng trong Hibernate có 1 trong 4 trạng thái:
  • Transient    (Tạm thời)
  • Persistent   (Bền vững)
  • Removed     (Đã bị xóa - dưới DB)
  • Detached    (Đã bị tách riêng ra - so với session hiện tại)
Chúng ta giải thích chúng bằng hình minh họa dưới đây:
  1. Trường hợp bạn tạo mới một đối tượng java từ một Entity, đối tượng đó có tình trạng là Transient. Hibernate không biết về sự tồn tại của nó. Nó nằm ngoài sự quản lý của Hibernate.
  2. Trường hợp bạn lấy ra đối tượng Entity bằng method get, load hoặc find, bạn có được một đối tượng nó tương ứng với 1 record dưới database. Đối tượng này có trạng thái Persistent. Nó được quản lý bởi Hibernate.
  3. Session gọi một trong các method save,saveOrUpdate, persist, merge sẽ đẩy đối tượng Transient vào sự quản lý của Hibernate và đối tượng này chuyển sang trạng thái Persistent. Tùy tình huống nó sẽ insert hoặc update dữ liệu vào DB.
  4. Session gọi evict(..) hoặc clear() để đuổi các đối tượng có trạng thái persistent (bền vững) ra khỏi sự quản lý của Hibernate, giờ các đối tượng này sẽ có trạng thái mới là Detected (Bị tách ra).  Nếu nó không được đính (Attached) trở lại, nó sẽ bị bộ gom rác của Java quét đi theo cơ chế thông thường.
  5. Sử dụng update(..), saveOrUpdate(..), merge(..) sẽ đính trở lại các đối tượng Detached vào lại. Tùy tình huống nó sẽ tạo ra dưới DB câu lệnh update hoặc insert. Các đối tượng sẽ trở về trạng thái Persistent (bền vững).
  6. Session gọi method remove(..), delete(..) để xóa một bản ghi, đối tượng persistent giờ chuyển sang trạng thái Removed (Đã bị xóa).

11- Thao tác Insert,Update, Delete với Hibernate

11.1- Persistent

PersistentDemo.java
package org.o7planning.tutorial.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Department;

public class PersistentDemo {

    public static void main(String[] args) {
        
        SessionFactory factory = HibernateUtils.getSessionFactory();

        Session session = factory.getCurrentSession();
        Department department = null;

        try {
            session.getTransaction().begin();

            System.out.println("- Finding Department deptNo = D10...");
            // Đây là một đối tượng có trạng thái Persistent.
            department = DataUtils.findDepartment(session, "D10");

            System.out.println("- First change Location");
            // Thay đổi gì đó trên đối tượng Persistent.
            department.setLocation("Chicago " + System.currentTimeMillis());
            
            System.out.println("- Location = " + department.getLocation());

            System.out.println("- Calling flush...");
            // Sử dụng session.flush() để chủ động đẩy các thay đổi xuống DB.
            // Có tác dụng trên tất cả các đối tượng Persistent có thay đổi.
   
            session.flush();

            System.out.println("- Flush OK");

            System.out.println("- Second change Location");
            // Thay đổi gì đó trên đối tượng Persistent.
            // Hình thành câu lệnh update, sẽ được thực thi
            // sau khi session đóng lại (commit).
 
            department.setLocation("Chicago " + System.currentTimeMillis());

            // In ra Location.
            System.out.println("- Location = " + department.getLocation());

            System.out.println("- Calling commit...");

            // Lệnh commit sẽ làm tất cả những sự thay đổi được đẩy xuống DB.
     
            session.getTransaction().commit();

            System.out.println("- Commit OK");
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }

        // Tạo lại session sau khi nó đã bị đóng trước đó
        // (Do commit hoặc rollback)
 
        session = factory.getCurrentSession();
        try {
            session.getTransaction().begin();

            System.out.println("- Finding Department deptNo = D10...");
            
            // Query lại Department D10.
            
            department = DataUtils.findDepartment(session, "D10");

            // In ra thông tin Location.
  
            System.out.println("- D10 Location = " + department.getLocation());

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }
    }
    
}
Kết quả chạy ví dụ:

11.2- Transient --> Persistent : Tổng quan

11.3- Transient --> Persistent : Sử dụng persist(Object)

PersistTransientDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;
import org.o7planning.tutorial.hibernate.entities.Timekeeper;

public class PersistTransientDemo {

    private static DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

    private static Timekeeper persist_Transient(Session session, Employee emp) {
        // Hãy chú ý:
        // timekeeperId cấu hình tự động được tạo ra bởi UUID.
        // @GeneratedValue(generator = "uuid")
        // @GenericGenerator(name = "uuid", strategy = "uuid2")
        // Tạo một đối tượng, nó đang có tình trạng Transient.
     
        Timekeeper tk1 = new Timekeeper();

        tk1.setEmployee(emp);
        tk1.setInOut(Timekeeper.IN);
        tk1.setDateTime(new Date());
 
        // Now, 'tk1' is transient object
        System.out.println("- tk1 Persistent? " + session.contains(tk1));

        System.out.println("====== CALL persist(tk).... ===========");
        
        // Hibernate gán Id vào 'tk1', sẽ chưa có insert gì cả
 
        session.persist(tk1);

        // 'tk1' đã được gắn ID
        System.out
                .println("- tk1.getTimekeeperId() = " + tk1.getTimekeeperId());

        // Lúc này 'tk1' đã có trạng thái Persistent
        // Nó đã được quản lý trong Session.
        // Nhưng chưa có hành động gì insert xuống DB.
        // ==> true
 
        System.out.println("- tk1 Persistent? " + session.contains(tk1));

        System.out.println("- Call flush..");
        
        // Chủ động đẩy dữ liệu xuống DB, gọi flush().
        // Nếu không gọi flush() dữ liệu sẽ được đẩy xuống tại lệnh commit().
        // Lúc này mới có insert.
  
        session.flush();

        String timekeeperId = tk1.getTimekeeperId();
        System.out.println("- timekeeperId = " + timekeeperId);
        System.out.println("- inOut = " + tk1.getInOut());
        System.out.println("- dateTime = " + df.format(tk1.getDateTime()));
        System.out.println();
        return tk1;
    }

    public static void main(String[] args) {
        SessionFactory factory = HibernateUtils.getSessionFactory();

        Session session = factory.getCurrentSession();
        Employee emp = null;
        try {
            session.getTransaction().begin();

            emp = DataUtils.findEmployee(session, "E7499");

            persist_Transient(session, emp);

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }
    }
    
}
Kết quả chạy ví dụ:

11.4- Transient --> Persistent : Sử dụng save(Object) *

SaveTransientDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;
import org.o7planning.tutorial.hibernate.entities.Timekeeper;

public class SaveTransientDemo {

  private static DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

  private static Timekeeper persist_Transient(Session session, Employee emp) {
      // Hãy chú ý:
      // timekeeperId cấu hình tự động được tạo ra bởi UUID.
      // @GeneratedValue(generator = "uuid")
      // @GenericGenerator(name = "uuid", strategy = "uuid2")
      // Tạo một đối tượng, nó đang có tình trạng Transient.
      Timekeeper tk2 = new Timekeeper();

      tk2.setEmployee(emp);
      tk2.setInOut(Timekeeper.IN);
      tk2.setDateTime(new Date());

      // Lúc này 'tk2' đang có tình trạng Transient.
      System.out.println("- tk2 Persistent? " + session.contains(tk2));

      System.out.println("====== CALL save(tk).... ===========");
      // save() rất giống với persist()
      // save() trả về ID còn persist() là void.
      // Hibernate gán Id vào 'tk2', sẽ chưa có insert gì cả
      // Nó trả về ID của 'tk2'.
      Serializable id = session.save(tk2);

      System.out.println("- id = " + id);

      // 'tk2' đã được gắn ID
      System.out
              .println("- tk2.getTimekeeperId() = " + tk2.getTimekeeperId());

      // Lúc này 'tk2' đã có trạng thái Persistent
      // Nó đã được quản lý trong Session.
      // Nhưng chưa có hành động gì insert xuống DB.
      // ==> true
      System.out.println("- tk2 Persistent? " + session.contains(tk2));

      System.out.println("- Call flush..");
      // Chủ động đẩy dữ liệu xuống DB, gọi flush().
      // Nếu không gọi flush() dữ liệu sẽ được đẩy xuống tại lệnh commit().
      // Lúc này mới có insert.
      session.flush();

      String timekeeperId = tk2.getTimekeeperId();
      System.out.println("- timekeeperId = " + timekeeperId);
      System.out.println("- inOut = " + tk2.getInOut());
      System.out.println("- dateTime = " + df.format(tk2.getDateTime()));
      System.out.println();
      return tk2;
  }

  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();
      Employee emp = null;
      try {
          session.getTransaction().begin();

          emp = DataUtils.findEmployee(session, "E7499");

          persist_Transient(session, emp);

          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          session.getTransaction().rollback();
      }
  }
}
Kết quả chạy ví dụ:

11.5- Transient --> Persistent : Sử dụng saveOrUpdate(Object)

SaveOrUpdateTransientDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;
import org.o7planning.tutorial.hibernate.entities.Timekeeper;

public class SaveOrUpdateTransientDemo {

  private static DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

  private static Timekeeper saveOrUpdate_Transient(Session session,
          Employee emp) {
      // Hãy chú ý:
      // timekeeperId cấu hình tự động được tạo ra bởi UUID.
      // @GeneratedValue(generator = "uuid")
      // @GenericGenerator(name = "uuid", strategy = "uuid2")
      // Tạo một đối tượng, nó đang có tình trạng Transient.
      Timekeeper tk3 = new Timekeeper();

      tk3.setEmployee(emp);
      tk3.setInOut(Timekeeper.IN);
      tk3.setDateTime(new Date());

      // Lúc này 'tk3' đang có tình trạng Transient.
      System.out.println("- tk3 Persistent? " + session.contains(tk3));

      System.out.println("====== CALL saveOrUpdate(tk).... ===========");

      // Tại đây Hibernate sẽ kiểm tra, tk3 có ID chưa (timekeeperId)
      // Nếu chưa có nó tự gán ID vào.
      session.saveOrUpdate(tk3);

      System.out
              .println("- tk3.getTimekeeperId() = " + tk3.getTimekeeperId());

      // Lúc này 'tk3' đã có trạng thái Persistent
      // Nó đã được quản lý trong Session.
      // Nhưng chưa có hành động gì insert, hoặc update xuống DB.
      // ==> true
      System.out.println("- tk3 Persistent? " + session.contains(tk3));

      System.out.println("- Call flush..");
      // Chủ động đẩy dữ liệu xuống DB, gọi flush().
      // Nếu không gọi flush() dữ liệu sẽ được đẩy xuống tại lệnh commit().
      // Lúc này có thể có Insert hoặc Update xuống DB. (!!!)
      // Tùy thuộc vào ID của 'tk3' có trên DB chưa.
      session.flush();

      String timekeeperId = tk3.getTimekeeperId();
      System.out.println("- timekeeperId = " + timekeeperId);
      System.out.println("- inOut = " + tk3.getInOut());
      System.out.println("- dateTime = " + df.format(tk3.getDateTime()));
      System.out.println();
      return tk3;
  }

  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();
      Employee emp = null;
      try {
          session.getTransaction().begin();

          emp = DataUtils.findEmployee(session, "E7499");

          saveOrUpdate_Transient(session, emp);

          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          session.getTransaction().rollback();
      }
  }
}
Kết quả chạy ví dụ:

11.6- Transient --> Persistent : Sử dụng merge(Object)

MergeTransientDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;
import org.o7planning.tutorial.hibernate.entities.Timekeeper;

public class MergeTransientDemo {

  private static DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

  private static Timekeeper saveOrUpdate_Transient(Session session,
          Employee emp) {
      // Hãy chú ý:
      // timekeeperId cấu hình tự động được tạo ra bởi UUID.
      // @GeneratedValue(generator = "uuid")
      // @GenericGenerator(name = "uuid", strategy = "uuid2")
      // Tạo một đối tượng, nó đang có tình trạng Transient.
      Timekeeper tk4 = new Timekeeper();

      tk4.setEmployee(emp);
      tk4.setInOut(Timekeeper.IN);
      tk4.setDateTime(new Date());

      // Lúc này 'tk4' đang có tình trạng Transient.
      System.out.println("- tk4 Persistent? " + session.contains(tk4));

      System.out.println("====== CALL merge(tk).... ===========");

      // Hibernate2 có method copyOrUpdateCopy
      // phiên bản 3 trở nó đổi tên thành merge.
      // (Vì vậy sẽ có sự tương đồng).
      // Tại đây Hibernate sẽ kiểm tra, tk4 có ID (timekeeperId) chưa
      // Nếu chưa có nó tự gán ID vào.
      // Sau đó tạo ra một bản copy và trả về.
      Timekeeper tk4Copy = (Timekeeper) session.merge(tk4);

      System.out
              .println("- tk4.getTimekeeperId() = " + tk4.getTimekeeperId());

      // Chú ý:
      // Lúc này 'tk4' vẫn có trạng thái Transient.
      // còn 'tk4Copy' thì có trạng thái Persistent.
      // Nhưng chưa có hành động gì insert, hoặc update xuống DB.
      // ==> false
      System.out.println("- tk4 Persistent? " + session.contains(tk4));

      // 'tk4Copy' có trạng thái Persistent.
      // ==> true
      System.out
              .println("- tk4Copy Persistent? " + session.contains(tk4Copy));

      System.out.println("- Call flush..");
      // Chủ động đẩy dữ liệu xuống DB, gọi flush().
      // Nếu không gọi flush() dữ liệu sẽ được đẩy xuống tại lệnh commit().
      // Lúc này có thể có Insert hoặc Update xuống DB. (!!!)
      // Tùy thuộc vào ID của 'tk4' có trên DB chưa.
      session.flush();

      // 'tk4' vẫn là Transient, sau khi flush().
      // Nhận xét: merge(..) an toàn hơn so với saveOrUpdate().
      System.out.println("- tk4 Persistent? " + session.contains(tk4));

      //
      String timekeeperId = tk4.getTimekeeperId();
      System.out.println("- timekeeperId = " + timekeeperId);
      System.out.println("- inOut = " + tk4.getInOut());
      System.out.println("- dateTime = " + df.format(tk4.getDateTime()));
      System.out.println();
      return tk4;
  }

  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();
      Employee emp = null;
      try {
          session.getTransaction().begin();

          emp = DataUtils.findEmployee(session, "E7499");

          saveOrUpdate_Transient(session, emp);

          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          session.getTransaction().rollback();
      }
  }
}
Kết quả chạy ví dụ:

11.7- Persistent --> Detached

Một đối tượng đang có tình trạng Persistent (Được quản lý bởi Hibernate) có thể chuyển sang trạng thái Detached (Tách ra, không được Hibernate quản lý)  thông qua method của Session:
  • evict(Object)
    • Đuổi cổ một đối tượng ra khỏi sự quản lý của Hibernate
  • clear()
    • Đuổi cổ tất các các đối tượng đang được quản lý bởi Hibernate ra.
Tất nhiên khi một session gọi commit(), close() hoặc rollback() lúc đó session đã kết thúc. Tất cả các đối tượng Persistence của session này sẽ là Detached đối với một session mới được mở ra.
 
EvictDemo.java
package org.o7planning.tutorial.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class EvictDemo {

  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();
      Employee emp = null;
      try {
          session.getTransaction().begin();

          // Đây là một đối tượng có tình trạng Persistent.
          emp = DataUtils.findEmployee(session, "E7499");

          // ==> true
          System.out.println("- emp Persistent? " + session.contains(emp));

          // Sử dụng evict(Object) để đuổi đối tượng Persistent
          // ra khỏi quản lý của Hibernate.
          session.evict(emp);

          // Lúc này 'emp' đang có trạng thái Detached.
          // ==> false
          System.out.println("- emp Persistent? " + session.contains(emp));

          // Tất cả các thay đổi trên 'emp' sẽ không được update
          // nếu không đưa 'emp' trở lại trạng thái Persistent.
          emp.setEmpNo("NEW");

          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          session.getTransaction().rollback();
      }
  }
}
Kết quả chạy ví dụ:
ClearDemo.java
package org.o7planning.tutorial.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Department;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class ClearDemo {
  public static void main(String[] args) {
      SessionFactory factory = HibernateUtils.getSessionFactory();

      Session session = factory.getCurrentSession();
      Employee emp = null;
      Department dept = null;
      try {
          session.getTransaction().begin();

          // Đây là một đối tượng có tình trạng Persistent.
          emp = DataUtils.findEmployee(session, "E7499");
          dept = DataUtils.findDepartment(session, "D10");

          // Sử dụng clear để đuổi hết tất cả các đối tượng có
          // trạng thái Persistent ra khỏi sự quản lý của Hibernate.
          session.clear();

          // Lúc này 'emp' & 'dept' đang có trạng thái Detached.
          // ==> false
          System.out.println("- emp Persistent? " + session.contains(emp));
          System.out.println("- dept Persistent? " + session.contains(dept));

          // Tất cả các thay đổi trên 'emp' sẽ không được update
          // nếu không đưa 'emp' trở lại trạng thái Persistent.
          emp.setEmpNo("NEW");

          dept = DataUtils.findDepartment(session, "D20");
          System.out.println("Dept Name = "+ dept.getDeptName());

          session.getTransaction().commit();
      } catch (Exception e) {
          e.printStackTrace();
          session.getTransaction().rollback();
      }
  }
}
Kết quả chạy ví dụ:

11.8- Detached --> Persistent : Tổng quan

Một đối tượng có trạng thái Detached (Bị tách ra khỏi sự quản lý của Hibernate) có thể được Attach trở lại thông qua một vài method của Session:
  • update(Object)
  • saveOrUpdate(Object)
  • merge(Object)
  • refresh(Object)
  • lock(Object)
Bạn có thể xem sự khác nhau của các method này theo các ví dụ dưới đây:

11.9- Detached --> Persistent : Sử dụng update(Object)

UpdateDetachedDemo.java
package org.o7planning.tutorial.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class UpdateDetachedDemo {

   public static void main(String[] args) {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session1 = factory.getCurrentSession();
       Employee emp = null;
       try {
           session1.getTransaction().begin();

           // Đây là đối tượng có trạng thái Persistent.
           emp = DataUtils.findEmployee(session1, "E7499");

           // session1 đã bị đóng lại sau commit được gọi.
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }

       // Mở một Session khác

       Session session2 = factory.getCurrentSession();

       try {
           session2.getTransaction().begin();

           // Kiểm tra trạng thái của emp:
           // ==> false
           System.out.println("- emp Persistent? " + session2.contains(emp));

           System.out.println("Emp salary: " + emp.getSalary());

           emp.setSalary(emp.getSalary() + 100);

           // update(..) chỉ áp dụng cho đối tượng Detached.
           // (Không dùng được với đối tượng Transient).
           // Sử dụng update(emp) để đưa emp trở lại trạng thái Persistent.
           session2.update(emp);

           // Chủ động đẩy dữ liệu xuống DB.
           // Câu lệnh update sẽ được gọi.
           session2.flush();

           System.out.println("Emp salary after update: " + emp.getSalary());

           // session2 đã bị đóng lại sau commit được gọi.
           session2.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session2.getTransaction().rollback();
       }

   }
}
Kết quả chạy ví dụ:

11.10- Detached --> Persistent : Sử dụng saveOrUpdate(Object)

SaveOrUpdateDetachedDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.util.Random;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class SaveOrUpdateDetachedDemo {

   public static void main(String[] args) {

       // Một đối tượng có trạng thái Detached.
       Employee emp = getEmployee_Detached();

       System.out.println(" - GET EMP " + emp.getEmpId());

       // Ngẫu nhiên xóa hoặc không xóa Employee ứng với ID.
       boolean delete = deleteOrNotDelete(emp.getEmpId());

       System.out.println(" - DELETE? " + delete);

       // saveOrUpdate đối tượng Detached.
       saveOrUpdate_test(emp);

       // Sau khi gọi saveOrUpdate().
       // Có thể ID của Entity sẽ khác đi trong trường hợp
       // entity có ID tự tăng và saveOrUpdate tạo ra câu Insert.
       System.out.println(" - EMP ID " + emp.getEmpId());
   }

   // Hàm trả về một đối tượng Employee đã
   // nằm ngoài sự quản lý của Hibernate (Detached).
   private static Employee getEmployee_Detached() {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session1 = factory.getCurrentSession();
       Employee emp = null;
       try {
           session1.getTransaction().begin();

           Long maxEmpId = DataUtils.getMaxEmpId(session1);
           System.out.println(" - Max Emp ID " + maxEmpId);

           Employee emp2 = DataUtils.findEmployee(session1, "E7839");

           Long empId = maxEmpId + 1;
           emp = new Employee();
           emp.setEmpId(empId);
           emp.setEmpNo("E" + empId);

           emp.setDepartment(emp2.getDepartment());
           emp.setEmpName(emp2.getEmpName());

           emp.setHideDate(emp2.getHideDate());
           emp.setJob("Test");
           emp.setSalary(1000F);

           // emp đã được quản lý bởi Hibernate
           session1.persist(emp);

           // session1 đã bị đóng lại sau commit được gọi.
           // Một bản ghi Employee đã được insert vào DB.
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }
       // session1 đã bị đóng 'emp' đã trở về trạng thái Detached.
       return emp;
   }

   // Xóa Employee theo ID cho bởi tham số.
   // Ngẫu nhiên xóa hoặc không xóa.
   private static boolean deleteOrNotDelete(Long empId) {
       // Một số ngẫu nhiên từ 0-9
       int random = new Random().nextInt(10);
       if (random < 5) {
           return false;
       }
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session2 = factory.getCurrentSession();
       try {
           session2.getTransaction().begin();
           String sql = "Delete " + Employee.class.getName() + " e "
                   + " where e.empId =:empId ";
           Query query = session2.createQuery(sql);
           query.setParameter("empId", empId);

           query.executeUpdate();

           session2.getTransaction().commit();
           return true;
       } catch (Exception e) {
           e.printStackTrace();
           session2.getTransaction().rollback();
           return false;
       }
   }

   private static void saveOrUpdate_test(Employee emp) {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       // Mở một Session khác

       Session session3 = factory.getCurrentSession();

       try {
           session3.getTransaction().begin();

           // Thực tế emp đang có trạng thái Detached
           // Nó không được quản lý bởi Hibernate.
           // Kiểm tra trạng thái của emp:
           // ==> false
           System.out.println(" - emp Persistent? " + session3.contains(emp));

           System.out.println(" - Emp salary before update: "
                   + emp.getSalary());

           // Set salary mới cho đối tượng Detached emp.
           // Bạn cũng có thể sét ID mới nếu muốn.
           emp.setSalary(emp.getSalary() + 100);

           // Sử dụng saveOrUpdate(emp) để đưa emp
           // trở lại trạng thái Persistent.
           // Chú ý: Nếu có một đối tượng có cùng ID
           // đang được quản lý bởi Hibernate mà gọi hàm này sẽ bị Exception.
           //
           // Lúc này vẫn chưa có sử lý gì liên quan DB.
           session3.saveOrUpdate(emp);

           // Chủ động đẩy dữ liệu xuống DB.
           // Tại đây có thể có thể tạo ra câu Insert hoặc Update vào DB.
           // Nếu bản ghi tương ứng đã bị xóa bởi ai đó, câu lệnh Insert sẽ
           // được tạo ra.
           // Ngược lại sẽ là một câu lệnh Update.
           session3.flush();

           System.out
                   .println(" - Emp salary after update: " + emp.getSalary());

           // session3 đã bị đóng lại sau commit được gọi.
           session3.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session3.getTransaction().rollback();
       }

   }

}
Chạy ví dụ trên một vài lần, bạn có thể thấy 2 tình huống saveOrUpdate tạo ra Insert hoặc Update vào DB.
INSERT:
UPDATE:

11.11- Detached --> Persistent : Sử dụng merge(Object)

Hibernate phiên bản 2 có method saveOrUpdateCopy(Object), từ phiên bản 3 trở lên nó đổi tên thành merge(Object). Chính vì vậy merge có một chút tương đồng và một chút khác biệt so với saveOrUpdate.

merge(Object) không đưa đối tượng vào sự quản lý của Hibernate nhưng nó tạo ra một bản copy của đối tượng đó và quản lý đối tượng copy.

Nếu bạn gọi saveOrUpdate(aObject) trong khi đã có một đối tượng bObject đang được quản lý bởi Hibernate và có cùng ID với aObject một ngoại lệ sẽ được ném ra. Trong khi sử dụng merge(aObject) sẽ không bị ngoại lệ này.
MergeDetachedDemo.java
package org.o7planning.tutorial.hibernate.demo;

import java.util.Random;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class MergeDetachedDemo {

   public static void main(String[] args) {

       // Một đối tượng có trạng thái Detached.
       Employee emp = getEmployee_Detached();

       System.out.println(" - GET EMP " + emp.getEmpId());

       // Ngẫu nhiên xóa hoặc không xóa Employee ứng với ID.
       boolean delete = deleteOrNotDelete(emp.getEmpId());

       System.out.println(" - DELETE? " + delete);

       // saveOrUpdate đối tượng Detached.
       saveOrUpdate_test(emp);

       // Sau khi gọi saveOrUpdate().
       // Có thể ID của Entity sẽ khác đi trong trường hợp
       // entity có ID tự tăng và saveOrUpdate tạo ra câu Insert.
       System.out.println(" - EMP ID " + emp.getEmpId());
   }

   // Hàm trả về một đối tượng Employee đã
   // nằm ngoài sự quản lý của Hibernate (Detached).
   private static Employee getEmployee_Detached() {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session1 = factory.getCurrentSession();
       Employee emp = null;
       try {
           session1.getTransaction().begin();

           Long maxEmpId = DataUtils.getMaxEmpId(session1);
           System.out.println(" - Max Emp ID " + maxEmpId);

           Employee emp2 = DataUtils.findEmployee(session1, "E7839");

           Long empId = maxEmpId + 1;
           emp = new Employee();
           emp.setEmpId(empId);
           emp.setEmpNo("E" + empId);

           emp.setDepartment(emp2.getDepartment());
           emp.setEmpName(emp2.getEmpName());

           emp.setHideDate(emp2.getHideDate());
           emp.setJob("Test");
           emp.setSalary(1000F);

           // emp đã được quản lý bởi Hibernate
           session1.persist(emp);

           // session1 đã bị đóng lại sau commit được gọi.
           // Một bản ghi Employee đã được insert vào DB.
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }
       // session1 đã bị đóng 'emp' đã trở về trạng thái Detached.
       return emp;
   }

   // Xóa Employee theo ID cho bởi tham số.
   // Ngẫu nhiên xóa hoặc không xóa.
   private static boolean deleteOrNotDelete(Long empId) {
       // Một số ngẫu nhiên từ 0-9
       int random = new Random().nextInt(10);
       if (random < 5) {
           return false;
       }
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session2 = factory.getCurrentSession();
       try {
           session2.getTransaction().begin();
           String sql = "Delete " + Employee.class.getName() + " e "
                   + " where e.empId =:empId ";
           Query query = session2.createQuery(sql);
           query.setParameter("empId", empId);

           query.executeUpdate();

           session2.getTransaction().commit();
           return true;
       } catch (Exception e) {
           e.printStackTrace();
           session2.getTransaction().rollback();
           return false;
       }
   }

   private static void saveOrUpdate_test(Employee emp) {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       // Mở một Session khác

       Session session3 = factory.getCurrentSession();

       try {
           session3.getTransaction().begin();

           // Thực tế emp đang có trạng thái Detached
           // Nó không được quản lý bởi Hibernate.
           // Kiểm tra trạng thái của emp:
           // ==> false
           System.out.println(" - emp Persistent? " + session3.contains(emp));

           System.out.println(" - Emp salary before update: "
                   + emp.getSalary());

           // Set salary mới cho đối tượng Detached emp.
           // Bạn cũng có thể sét ID mới nếu muốn.
           emp.setSalary(emp.getSalary() + 100);

           // merge(emp) trả về empMerge, một bản copy của emp,
           // empMerge được quản lý bởi Hibernate.
           // Còn emp vẫn trong tình trạng Detached
           //
           // Lúc này vẫn chưa có sử lý gì liên quan DB.
           Employee empMerge = (Employee) session3.merge(emp);

           // ==> false
           System.out.println(" - emp Persistent? " + session3.contains(emp));
           // ==> true
           System.out.println(" - empMerge Persistent? "
                   + session3.contains(empMerge));

           // Chủ động đẩy dữ liệu xuống DB.
           // Tại đây có thể có thể tạo ra câu Insert hoặc Update vào DB.
           // Nếu bản ghi tương ứng đã bị xóa bởi ai đó, câu lệnh Insert sẽ
           // được tạo ra.
           // Ngược lại sẽ là một câu lệnh Update.
           session3.flush();

           System.out
                   .println(" - Emp salary after update: " + emp.getSalary());

           // session3 đã bị đóng lại sau commit được gọi.
           session3.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session3.getTransaction().rollback();
       }

   }

}
Kết quả chạy ví dụ:
INSERT:
UPDATE:

11.12- Detached --> Persistent : Sử dụng refresh(Object)

RefreshDetachedDemo.java
package org.o7planning.tutorial.hibernate.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.tutorial.hibernate.DataUtils;
import org.o7planning.tutorial.hibernate.HibernateUtils;
import org.o7planning.tutorial.hibernate.entities.Employee;

public class RefreshDetachedDemo {

   public static void main(String[] args) {

       // Một đối tượng có trạng thái Detached.
       Employee emp = getEmployee_Detached();

       System.out.println(" - GET EMP " + emp.getEmpId());

       // refresh đối tượng Detached.
       refresh_test(emp);
   }

   // Hàm trả về một đối tượng Employee đã
   // nằm ngoài sự quản lý của Hibernate (Detached).
   private static Employee getEmployee_Detached() {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       Session session1 = factory.getCurrentSession();
       Employee emp = null;
       try {
           session1.getTransaction().begin();

           emp = DataUtils.findEmployee(session1, "E7839");

           // session1 đã bị đóng lại sau commit được gọi.
           // Một bản ghi Employee đã được insert vào DB.
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }
       // session1 đã bị đóng 'emp' đã trở về trạng thái Detached.
       return emp;
   }

   private static void refresh_test(Employee emp) {
       SessionFactory factory = HibernateUtils.getSessionFactory();

       // Mở một Session khác

       Session session2 = factory.getCurrentSession();

       try {
           session2.getTransaction().begin();

           // Thực tế emp đang có trạng thái Detached
           // Nó không được quản lý bởi Hibernate.
           // Kiểm tra trạng thái của emp:
           // ==> false
           System.out.println(" - emp Persistent? " + session2.contains(emp));

           System.out.println(" - Emp salary before update: "
                   + emp.getSalary());

           // Set salary mới cho đối tượng Detached emp.
           emp.setSalary(emp.getSalary() + 100);

           // refresh tạo ra câu query.
           // Và đưa đối tượng Detached về Persistent.
           // Các thay đổi của emp không được lưu lại.O
           session2.refresh(emp);

           // ==> true
           System.out.println(" - emp Persistent? " + session2.contains(emp));

           System.out.println(" - Emp salary after refresh: "
                   + emp.getSalary());
           
           session2.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session2.getTransaction().rollback();
       }

   }

}
Kết quả chạy ví dụ:

11.13- Detached --> Persistent : Sử dụng lock(Object)

  • TODO...

12- Tạo ra database từ các Entity

Hibernate cho phép bạn tạo ra các bảng từ các Entity, với đầy đủ các giàng buộc giữa các bảng.

13- Tra cứu Hibernate