openplanning

Hướng dẫn lập trình Java Hibernate cho người mới bắt đầu

  1. Chuẩn bị Database
  2. Tạo Maven Project & Khai báo thư viện
  3. Các lớp Entity
  4. Cấu hình Hibernate
  5. SessionFactory
  6. Hibernate Query Language (HQL)
  7. Query dữ liệu với Hibernate
  8. Transient, Persistent và Detached là gì?
  9. Vòng đời của Hibernate (Hibernate Lifecycle)
  10. Thao tác Insert, Update, Delete với Hibernate
  11. Tạo ra các bảng từ các lớp Entity

1. 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"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:

2. 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>

3. Các lớp Entity

Chúng ta tạo các lớp Entity. Mỗi Entity sẽ đại diện cho một bảng trong DB. Hãy khoan nói về những gì có trong các lớp đó.
  • Department - Phòng ban
  • Employee - Nhân viên
  • SalaryGrade - Bậc lương
  • 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;
  }

}

4. 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 mà bạn đã tạo ở bước trên.
Tập tin hibernate.cfg.xml được đặ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 (Phương ngữ) khác nhau.
Ví dụ:
Dialect dùng cho Oracle:
  • org.hibernate.dialect.Oracle10gDialect (Oracle 10g &11g)
  • org.hibernate.dialect.Oracle12cDialect
Dialect dùng cho SQL Server:
  • org.hibernate.dialect.SQLServerDialect
  • org.hibernate.dialect.SQLServer2012Dialect
  • org.hibernate.dialect.SQLServer2008Dialect
Dialect dùng 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 lớp, nó nói với Hibernate cách chuyển đổi 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)

5. SessionFactory

HibernateUtils là một lớp tiện ích, nó có phương thức trả về đối tượng SessionFactory. Lớp này sẽ được sử dụng thường xuyên trong các ví dụ.
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();
  }

}

6. 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 (Truy vấn) dữ liệu trên các bảng.
HQL:
  • Query (Truy vấn) dữ liệu trên các lớp 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:

7. 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.
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();
      }
  }
}
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ụ:
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. Lớp Java bean sẽ có một constructor để khởi tạo các giá trị các trường (field) của nó. Constructor này tham gia vào câu lệnh 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();
       }
   }
   
}
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();
        }
    }
    
}

8. 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ụ:

9. Vòng đời của Hibernate (Hibernate Lifecycle)

Lớp Session của Hibernate có một số các phương thức 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 các trạng thái này bằng hình minh họa dưới đây:
Transient:
Trường hợp bạn tạo mới một đối tượng java từ một Entity, đối tượng đó sẽ có trạng thái 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.
Persistent
Trường hợp bạn lấy ra đối tượng Entity thông qua các phương thức get, load, find, getSingleResult,.. bạn có được một đối tượng nó tương ứng với 1 bản ghi dưới database. Đối tượng này có trạng thái Persistent. Nó được quản lý bởi Hibernate.
Transient -> Persistent
Session gọi một trong các phương thức save, saveOrUpdate, persist, merge sẽ đẩy đối tượng Transientvào sự quản lý của Hibernate và đối tượng này chuyển sang trạng thái Persistent. Nó tương đương với hành động insert hoặc update trong Database.
Persistent -> Detached
Đối tượng Session gọi phương thức evict(..) hoặc clear() để đuổi các đối tượng có trạng thái Persistent 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à Detached (Bị tách ra). Nếu nó không được đính lại (Re-Attached), nó sẽ bị bộ gom rác của Java (Java Garbage Collector) loại bỏ theo cơ chế thông thường.
Detached -> Persistent
Sử dụng một trong các phương thức: update(..), saveOrUpdate(..), merge(..) sẽ đưa một đối tượng có trạng thái Detached vào sự quản lý của Hibernate. Nó tương đương với hành động update hoặc insert dưới Database. Đối tượng sẽ chuyển sang trạng thái Persistent.
Persistent -> Removed
Đối tượng Session gọi phương thức remove(..) hoặc delete(..) để xóa một đối tượng (Bản ghi), đối tượng Persistent sẽ chuyển sang trạng thái Removed (Đã bị loại bỏ).

10. Thao tác Insert, Update, Delete với Hibernate

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ụ:
Transient --> Persistent : Tổng quan
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ụ:
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ụ:
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ụ:
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ụ:
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 (Bị tách ra, không được Hibernate quản lý) thông qua phương thức dưới đây của Session:
  • evict(Object)
Đuổi một đối tượng ra khỏi sự quản lý của Hibernate
  • clear()
Đuổi tất các các đối tượng ra khỏi sự quản lý của Hibernate.
Chú ý: Khi đối tượng Session gọi một trong các phương thức: commit(), close(), rollback() lúc đó session (phiên làm việc) này sẽ kết thúc. Tất cả các đối tượng Persistence của session này sẽ được coi là Detached đối với một session mới.
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ụ:
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 đính lại (Re-attach) thông qua một vài phương thức 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 phương thức này theo các ví dụ dưới đây:
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ụ:
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 hai tình huống saveOrUpdatetạo ra các hành động: Insert hoặc Update vào DB.
INSERT:
UPDATE:
Detached --> Persistent : Sử dụng merge(Object)
Hibernate phiên bản 2 có phương thức saveOrUpdateCopy(Object), từ phiên bản 3 trở lên nó đổi tên thành merge(Object). Chính vì vậy mergecó 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:
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ụ:
Detached --> Persistent : Sử dụng lock(Object)
  • TODO...

11. Tạo ra các bảng từ các lớp Entity

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