Hibernate快速入門

Hibernate是一個開源,輕量級的ORM(對象關係映射)工具。Hibernate框架簡化了java應用程序與數據庫交互的開發。

ORM工具簡化了數據創建,數據處理和數據訪問。它是將對象映射到數據庫中存儲的數據(表)的編程技術。

注:爲什麼會有這篇教程文章?答:只是想寫一篇最NB的Hibernate教程入門文章。NB代表人見人愛,花見花開,車見爆胎,飛鳥落地…,最後,需要注意的是:這篇文章包教不包會!除非你從頭到尾認真看完並運行所有示例代碼。

1. Hibernate快速入門簡介

本教程文章基於以下工具(軟件):

  • Hibernate 5.2.2.Final
  • Eclipse 4.6 (MARS)

Hibernate.x ~ Hibernate.5 更新功能:

  1. Hibernate 5.0開始Hibernate Spatial是Hibernate項目的一部分,因此我們也可以處理GIS數據了。

  2. 域模型映射支持Java 8日期和時間類型。 標準SQL日期/時間類型以及支持的Java 8 Date / Time類類型之間的映射,如下所示:

    • DATE: java.time.LocalDate
    • TIME: java.time.LocalTime, java.time.OffsetTime
    • TIMESTAMP: java.time.Instant, java.time.LocalDateTime,java.time.OffsetDateTimejava.time.ZonedDateTime
  3. 字節碼增強機制從頭重新設計,Hibernate可支持Maven和Gradle插件。可以通過字節碼儀器來增強三個主要方面:

    • 懶初始化:字段可以聲明爲LAZY,只有在第一次被訪問時,它們才被提取。
    • 髒檢查:實體被增強,使得它們可以跟蹤在持久化上下文中加載之後變化的所有屬性。
    • 雙向關聯:即使開發人員僅更新單側,但也可以自動同步雙向關聯的雙方。
  4. Hibernate的原生API(Session等)已更新爲使用泛型類型化。無需在獲取實體時轉換。

  5. Hibernate 5.0將其擴展到更廣泛的類型(例如UUID)。

  6. 引用二級緩存,使實體引用能夠直接存儲到第二級緩存中(用於不可變實體)。

2. 準備數據庫

Hibernate是一個庫,爲了處理所有類型的數據庫,它不依賴於應用程序選擇的任何類型的數據庫,如果Java是「一次寫入到處運行」的語言,Hibernate則是「寫一次就可運行在所有類型的數據庫「中的框架。

在這篇文章中,使用的是MySQL數據(你可使用其它的數據庫,如:Oracle,MySQL或SQL Server),並創建一個簡單的數據庫:mydb,完整的數據庫創建語句如下所示:

創建數據:

CREATE DATABASE IF NOT EXISTS mydb default charset utf8 COLLATE utf8_general_ci;

需要創建以下幾張表,它們的關係圖如下所示 -

Hibernate快速入門

創建表語句:

create table DEPARTMENT (
   DEPT_ID integer not null,
   DEPT_NAME varchar(255) not null,
   DEPT_NO varchar(20) not null,
   LOCATION varchar(255),
   primary key (DEPT_ID),
   unique (DEPT_NO)
);

create table EMPLOYEE (
   EMP_ID bigint not null,
   EMP_NAME varchar(50) not null,
   EMP_NO varchar(20) not null,
   HIRE_DATE date not null,
   IMAGE longblob,
   JOB varchar(30) not null,
   SALARY float not null,
   DEPT_ID integer not null,
   MNG_ID bigint,
   primary key (EMP_ID),
   unique (EMP_NO)
);

create table SALARY_GRADE (
   GRADE integer not null,
   HIGH_SALARY float not null,
   LOW_SALARY float not null,
   primary key (GRADE)
);

create table TIMEKEEPER (
   Timekeeper_Id varchar(36) not null,
   Date_Time datetime not null,
   In_Out char(1) not null,
   EMP_ID bigint not null,
   primary key (Timekeeper_Id)
);

alter table EMPLOYEE
   add index FK75C8D6AE269A3C9 (DEPT_ID),
   add constraint FK75C8D6AE269A3C9
   foreign key (DEPT_ID)
   references DEPARTMENT (DEPT_ID);

alter table EMPLOYEE
   add index FK75C8D6AE6106A42 (EMP_ID),
   add constraint FK75C8D6AE6106A42
   foreign key (EMP_ID)
   references EMPLOYEE (EMP_ID);

alter table EMPLOYEE
   add index FK75C8D6AE13C12F64 (MNG_ID),
   add constraint FK75C8D6AE13C12F64
   foreign key (MNG_ID)
   references EMPLOYEE (EMP_ID);

alter table TIMEKEEPER
   add index FK744D9BFF6106A42 (EMP_ID),
   add constraint FK744D9BFF6106A42
   foreign key (EMP_ID)
   references EMPLOYEE (EMP_ID);

向上面創建的表中,分別插入一些測試數據,如下所示 -

insert into Department (DEPT_ID, DEPT_NAME, DEPT_NO, LOCATION)
values (10, 'ACCOUNTING', 'D10', 'NEW YORK');

insert into Department (DEPT_ID, DEPT_NAME, DEPT_NO, LOCATION)
values (20, 'RESEARCH', 'D20', 'DALLAS');

insert into Department (DEPT_ID, DEPT_NAME, DEPT_NO, LOCATION)
values (30, 'SALES', 'D30', 'CHICAGO');

insert into Department (DEPT_ID, DEPT_NAME, DEPT_NO, LOCATION)
values (40, 'OPERATIONS', 'D40', 'BOSTON');

-------------------------------------------------------------------------------------------------


insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7839, 'KING', 'E7839', Str_To_Date('17-11-1981', '%d-%m-%Y'), 'PRESIDENT', 5000, 10, null);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7566, 'JONES', 'E7566', Str_To_Date('02-04-1981', '%d-%m-%Y'), 'MANAGER', 2975, 20, 7839);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7902, 'FORD', 'E7902', Str_To_Date('03-12-1981', '%d-%m-%Y'), 'ANALYST', 3000, 20, 7566);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7369, 'SMITH', 'E7369', Str_To_Date('17-12-1980', '%d-%m-%Y'), 'CLERK', 800, 20, 7902);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7698, 'BLAKE', 'E7698', Str_To_Date('01-05-1981', '%d-%m-%Y'), 'MANAGER', 2850, 30, 7839);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7499, 'ALLEN', 'E7499', Str_To_Date('20-02-1981', '%d-%m-%Y'), 'SALESMAN', 1600, 30, 7698);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7521, 'WARD', 'E7521', Str_To_Date('22-02-1981', '%d-%m-%Y'), 'SALESMAN', 1250, 30, 7698);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7654, 'MARTIN', 'E7654', Str_To_Date('28-09-1981', '%d-%m-%Y'), 'SALESMAN', 1250, 30, 7698);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7782, 'CLARK', 'E7782', Str_To_Date('09-06-1981', '%d-%m-%Y'), 'MANAGER', 2450, 30, 7839);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7788, 'SCOTT', 'E7788', Str_To_Date('19-04-1987', '%d-%m-%Y'), 'ANALYST', 3000, 20, 7566);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7844, 'TURNER', 'E7844', Str_To_Date('08-09-1981', '%d-%m-%Y'), 'SALESMAN', 1500, 30, 7698);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7876, 'ADAMS', 'E7876', Str_To_Date('23-05-1987', '%d-%m-%Y'), 'CLERK', 1100, 20, 7698);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7900, 'ADAMS', 'E7900', Str_To_Date('03-12-1981', '%d-%m-%Y'), 'CLERK', 950, 30, 7698);

insert into Employee (EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, JOB, SALARY, DEPT_ID, MNG_ID)
values (7934, 'MILLER', 'E7934', Str_To_Date('23-01-1982', '%d-%m-%Y'), 'CLERK', 1300, 10, 7698);

-------------------------------------------------------------------------------------------------

insert into Salary_Grade (GRADE, HIGH_SALARY, LOW_SALARY)
values (1, 9999, 3001);

3. 創建Maven項目和聲明庫

在這裏,創建一個Maven項目並在pom.xml中聲明使用的Hibernate庫。打開 Eclipse,選擇菜單 File -> New -> Other…,在彈出框中選擇 Maven,如下所示 -

Hibernate快速入門

下一步選擇工作目錄,如下圖所示 -

Hibernate快速入門

下一步,選擇模板類型,如下圖所示 -

Hibernate快速入門

下一步,寫入工程名稱:HibernateQuickStart,以及輸入包信息:com.yiibai 等信息。

Hibernate快速入門

項目(HibernateQuickStart)創建完成後,如下圖所示 -

Hibernate快速入門

pom.xml中,需要聲明使用Hibernate 5庫,以及用於各種數據庫類型(如Oracle,MySQL和SQL Server)的JDBC庫,這裏使用 MySQL JDBC。

完整的 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>com.yiibai</groupId>
    <artifactId>HibernateQuickStart</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

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

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

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

        <!-- Hibernate Core -->
        <!-- http://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>

        <!-- 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. 實體類

在這一步中,我們來創建實體類。每個實體描述一個數據庫中的表。這裏先不說明每個類是做什麼用的。一共要創建如下幾個實體類(對應上面創建的四張表),如下 -

  • Department.java
  • Employee.java
  • SalaryGrade.java
  • Timekeeper.java

所有創建的類如下圖所示 -

Hibernate快速入門

這幾個類的代碼,分別如下所示 -

Department.java 類代碼 -

package com.yiibai.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 com.yiibai.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 com.yiibai.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 com.yiibai.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. 配置hibernate

配置hibernate目的是讓Hibernate可以連接數據庫並與數據庫交互,並聲明在前面的步驟中創建的實體列表。

src/main/java中創建一個名稱爲:hibernate.cfg.xml 的配置文件,當前項目結構如下圖所示 -

hibernate.cfg.xml 配置文件的內容如下所示 -

<?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://localhost:3306/mydb?serverTimezone=UTC</property>
      <property name="connection.username">root</property>
      <property name="connection.password">123456</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="com.yiibai.entities.Department" />
      <mapping class="com.yiibai.entities.Employee" />
      <mapping class="com.yiibai.entities.SalaryGrade" />
      <mapping class="com.yiibai.entities.Timekeeper" />

  </session-factory>

</hibernate-configuration>

每種數據庫都有一個單獨的方言, 例如:

Oracle方言:

  • org.hibernate.dialect.Oracle10gDialect(Dùngcho 10g&11g)
  • org.hibernate.dialect.Oracle12cDialect

SQL Server方言:

  • org.hibernate.dialect.SQLServerDialect並
  • org.hibernate.dialect.SQLServer2012Dialect
  • org.hibernate.dialect.SQLServer2008Dialect

MySQL方言

  • org.hibernate.dialect.MySQLDialect
  • org.hibernate.dialect.MySQL5Dialect

什麼是方言?

Dialect是一個使用Hibernate的方式將數據庫的數據類型轉換爲Java的數據類型,反之亦然。此外,它用於定義將HSQL(Hibernate SQL)的函數轉換爲數據中的函數的方式,如下列出的一部分 -

Java SQL類型

Oracle

MySQL

SQL Server

Types.BIT

number(1,0)

bit

bit

Types.BIGINT

number(19,0)

bigin

bigint

Types.DATE

date

date

date

…….

Types.CLOB

clob

longtext

varchar(MAX)

Types.BLOB

blob

longblob

varbinary(MAX)

6. SessionFactory

創建一個文件: HibernateUtils.java , 其代碼如下 -

7. Hibernate查詢語言(HQL)

Hibernate使用Hibernate查詢語言(HQL)查詢數據。 HQL與我們所瞭解的數據庫SQL語句有點不同。

SQL:

  • 在表中查詢數據

HQL:

  • 在實體類中查詢對象數據

參考比較以下用法 -

-- SQL
-- This is a SQL query in table DEPARTMENT.
Select d.DEPT_NO, d.DEPT_NAME from DEPARTMENT d;

-- HQL
-- This is a HQL query in Entity Department.
Select d.deptNo, d.deptName from Department d;

-- Query Object
Select d from Department d;

Hibernate的操作規則:

應用程序編寫的HQL在操作過程中,Hibernate本身就意識到它使用的數據庫類型(如:MySQL),它會自動將HQL轉換爲等價的數據庫類型的SQL形式。 事實上,各種類型的數據庫之間的SQL語法有一些差異,比如:返回記錄的行數的限制就不同(MySQL中使用 limit 子句)。

可以參考HQL語法: http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/queryhql.html

8. 使用Hibernate查詢數據

在Hibernate中有很多方法可以用來查詢數據。在這部分中,將介紹一些查詢數據的典型方法。

8.1 - 使用HQL的查詢對象

查詢對象示例-1
第一個例子,使用HQL查詢對象(Entity),創建一個Java類文件:QueryObjectDemo.java,其代碼如下 -

package com.yiibai;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class QueryObjectDemo {

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

       Session session = factory.getCurrentSession();

       try {

           // All the action with DB via Hibernate
           // must be located in one transaction.
           // Start Transaction.            
           session.getTransaction().begin();

           // Create an HQL statement, query the object.
           // Equivalent to the SQL statement:
           // 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 ";


           // Create Query object.
           Query<Employee> query = session.createQuery(sql);


           // Execute query.
           List<Employee> employees = query.getResultList();

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

           // Commit data.
           session.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           // Rollback in case of an error occurred.
           session.getTransaction().rollback();
       }
   }

}

運行上面代碼,得到以下結果 -

Hibernate快速入門

查詢對象示例-2

創建一個Java類文件:QueryObjectDemo2.java,其代碼如下 -

package com.yiibai;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class QueryObjectDemo2 {

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

        Session session = factory.getCurrentSession();

        try {

            // All the action with DB via Hibernate
            // must be located in one transaction
            // Start Transaction.
            session.getTransaction().begin();

            // Create an HQL statement, query the object.
            // HQL with parameters.
            // Equivalent to the SQL statement:
            // 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 ";

            // Create query object.
            Query<Employee> query = session.createQuery(sql);

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

            // Execute query.
            List<Employee> employees = query.getResultList();

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

            // Commit data
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            // Rollback in case of an error occurred.
            session.getTransaction().rollback();
        }
    }
}

運行上面代碼,得到以下結果 -

......
org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select employee0_.EMP_ID ..._NO=?
Emp: E7839 : KING
Emp: E7934 : MILLER
`

8.2 - 使用HQL查詢讀取多列數據

創建一個Java類文件:QuerySomeColumnDemo.java,其代碼如下 -

package com.yiibai;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class QuerySomeColumnDemo {

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

        Session session = factory.getCurrentSession();

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

            // Query some columns.
            String sql = "Select e.empId, e.empNo, e.empName from "
                    + Employee.class.getName() + " e ";

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

            // Execute Query.
            // Get the array of 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 data.
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            // Rollback in case of an error occurred.
            session.getTransaction().rollback();
        }

    }
}

運行上面代碼,得到以下結果 -

Hibernate: select employee0_.EMP_ID..._0_ from EMPLOYEE employee0_
Emp Id: 7369
    Emp No: E7369
    Emp Name: SMITH
Emp Id: 7499
    Emp No: E7499
    Emp Name: ALLEN
Emp Id: 7521
    Emp No: E7521
    Emp Name: WARD
Emp Id: 7566
    Emp No: E7566
    Emp Name: JONES
Emp Id: 7654
    Emp No: E7654
    Emp Name: MARTIN
Emp Id: 7698
    Emp No: E7698
    Emp Name: BLAKE
Emp Id: 7782
    Emp No: E7782
    Emp Name: CLARK
Emp Id: 7788
    Emp No: E7788
    Emp Name: SCOTT
Emp Id: 7839
    Emp No: E7839
    Emp Name: KING
Emp Id: 7844
    Emp No: E7844
    Emp Name: TURNER
Emp Id: 7876
    Emp No: E7876
    Emp Name: ADAMS
Emp Id: 7900
    Emp No: E7900
    Emp Name: ADAMS
Emp Id: 7902
    Emp No: E7902
    Emp Name: FORD
Emp Id: 7934
    Emp No: E7934
    Emp Name: MILLER
`

8.3 - 使用HQL和JavaBean查詢多列數據

在這種情況下,如果需要在某些表中提取某些列的數據,最好的方法是使用Java bean。使用Java bean的構造函數來爲不同的字段設置值。在此構造函數加入HQL查詢。

創建一個Java類文件:ShortEmpInfo.java,其代碼如下 -

package com.yiibai;

public class ShortEmpInfo {

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

    //
    // Constructor have 3 parameters, will be used in the 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;
    }

}

創建一個Java類文件:ShortEmpInfoQueryDemo.java,其代碼如下 -

package com.yiibai;

import java.util.List;

import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class ShortEmpInfoQueryDemo {

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

        Session session = factory.getCurrentSession();

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

            // Using constructor of 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);


            // Execute query.
            // Get a List of 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 in case of an error occurred.
            session.getTransaction().rollback();
        }
    }

}

運行上面代碼,得到以下結果 -

Hibernate快速入門

8.4 - 查詢檢索唯一結果

創建一個Java類文件:UniqueResultDemo.java,其代碼如下 -

package com.yiibai;

import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;

import com.yiibai.entities.Department;
import com.yiibai.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();
        }
    }

}

運行上面代碼,得到以下結果 -

Hibernate快速入門

9. Hibernate瞬態,持久和分離

在這部分中,使用Session.persist(Object)將瞬態對象插入數據庫的簡單示例。本例中將介紹對象瞬態(Transitent),持久(Persistent)和分離(Detached)的概念。

創建一個Java類文件:DataUtils.java,其代碼如下 -

package com.yiibai;

import org.hibernate.Session;
import org.hibernate.query.Query;
import com.yiibai.entities.Department;
import com.yiibai.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();
   }

}

創建一個Java類文件:PersistDemo.java,其代碼如下 -

package com.yiibai;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.entities.Department;
import com.yiibai.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;

           // Get Persistent object.
           department = DataUtils.findDepartment(session, "D10");

           // Create transient object
           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);

           // Using persist(..)
           // Now 'emp' is managed by Hibernate.
           // it has Persistent status.
           // No action at this time with DB.
           session.persist(emp);


           // At this step the data is pushed to the DB.
           // Execute Insert statement.
           session.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session.getTransaction().rollback();
       }

       // After the session is closed (commit, rollback, close)
       // Objects 'emp', 'dept' became the Detached objects.
       // It is no longer in the control of the session.        
       System.out.println("Emp No: " + emp.getEmpNo());

   }

}

運行上面代碼,得到以下結果 -

Hibernate快速入門

10. Hibernate生命週期

Hibernate的Session類的有一些/幾組的重要方法,如下圖所示:

Hibernate快速入門

Hibernate中的一個對象存在於以下四個狀態之中的一種:

  • 短暫(Transient)
  • 持久(Persistent)
  • Removed
  • Detached

以上幾個狀態在下面圖中解釋:

Hibernate快速入門

下面來看這幾個狀態的流轉說明 -

  1. 當從一個實體創建一個新的Java對象時,該對象處於「短暫」狀態。 Hibernate不知道它的存在,因爲它獨立於Hibernate的管理。

  2. 如果使用方法:getloadfind獲取實體對象,則將獲得一個等同於數據庫中的1條記錄的對象。 此對象處於Persistent狀態。 它由Hibernate管理。

  3. 會話調用方法:savesaveOrUpdatepersist。 合併將短暫(Transient)對象置於Hibernate的管理之下,此對象轉爲持久化(Persistent)狀態。 在使用的具體情況下,它向數據庫插入或更新數據。

  4. Session調用evict(..)clear(),以便從處於Hibernate管理狀態的對象處於關閉狀態,並且這些對象處於分離(Detached)的狀態。

  5. 使用update(..)saveOrUpdate(..)merge(..)將有助於重新連接分離對象。 在具體情況下,它會向數據庫中創建更新或插入數據。 對象轉回持久化(Persistent)狀態。

  6. Session調用方法:remove(..)delete(..)刪除除記錄並持久化對象。

11. 用Hibernate插入,更新,刪除

11.1 - 持久化(Persistent)

當一個對像使用 Sessionget(),load(),find()方法獲取關聯數據時,它處於持久化(Persistent)狀態。

Hibernate快速入門

創建一個JAVA類文件:PersistentDemo.java,用於演示對象的持久化(Persistent)狀態。

package com.yiibai;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.entities.Department;
import com.yiibai.entities.Employee;

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...");

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

           System.out.println("- First change Location");


           // Changing something on Persistent object.
           department.setLocation("Chicago " + System.currentTimeMillis());

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

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

           // Use session.flush () to actively push the changes to the DB.
           // It works for all changed Persistent objects.
           session.flush();

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

           System.out.println("- Second change Location");

           // Change something on Persistent object
           department.setLocation("Chicago " + System.currentTimeMillis());

           // Print out location
           System.out.println("- Location = " + department.getLocation());

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

           // Commit
           session.getTransaction().commit();

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

       // Create the session after it had been closed earlier
       // (Cause by commit or update)
       session = factory.getCurrentSession();
       try {
           session.getTransaction().begin();

           System.out.println("- Finding Department deptNo = D10...");

           // Query lại Department D10.

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

           // Print out location
           System.out.println("- D10 Location = " + department.getLocation());

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

}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.2 - 瞬態轉爲持久化狀態

Hibernate快速入門

11.3 - 瞬態轉爲持久化狀態:使用persist(Object)

創建一個JAVA類文件:PersistTransientDemo.java,用於演示對象的持久化(Persistent)狀態。

package com.yiibai;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;
import com.yiibai.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) {

       // Note:
       // Configuring of timekeeperId
       // @GeneratedValue(generator = "uuid")
       // @GenericGenerator(name = "uuid", strategy = "uuid2")            
       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 assign value to Id of 'tk1'
       // No action to DB.
       session.persist(tk1);

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


       // Now 'tk1' is Persistent object.
       // But no action with DB.
       // ==> true
       System.out.println("- tk1 Persistent? " + session.contains(tk1));

       System.out.println("- Call flush..");


       // Flush data to DB.
       // Hibernate execute insert statement.
       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();
       }
   }

}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.4 - 瞬態轉爲持久化狀態:使用save(Object)

創建一個JAVA類文件:SaveTransientDemo.java,用於演示對象的持久化(Persistent)狀態。

package com.yiibai;

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 com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;
import com.yiibai.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) {

       // See configuration of timekeeperId:
       // @GeneratedValue(generator = "uuid")
       // @GenericGenerator(name = "uuid", strategy = "uuid2")
       // Create an Object, Transitent state.        
       Timekeeper tk2 = new Timekeeper();

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

       // Now 'tk3' are state Transient.        
       System.out.println("- tk2 Persistent? " + session.contains(tk2));

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

       // save() very similar to persist()
       // save() return ID, persist() return void.
       // Hibernate assign ID value to 'tk2', no action with DB
       // And return ID of 'tk2'.        
       Serializable id = session.save(tk2);

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

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


       // Now, 'tk2' has Persistent state
       // It has been managed in Session.
       // ==> true
       System.out.println("- tk2 Persistent? " + session.contains(tk2));

       System.out.println("- Call flush..");

       // To push data into the DB, call flush().
       // If not call flush() data will be pushed to the DB when calling commit().
       // Will execute insert statement.
       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();
       }
   }
}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.5 - 瞬態轉爲持久化狀態:使用saveOrUpdate(Object)

創建一個JAVA類文件:SaveOrUpdateTransientDemo.java,用於演示對象的持久化(Persistent)狀態。

package com.yiibai;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;
import com.yiibai.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) {

       // See configuration of timekeeperId:
       // @GeneratedValue(generator = "uuid")
       // @GenericGenerator(name = "uuid", strategy = "uuid2")
       // Create an Object, Transitent state.
       Timekeeper tk3 = new Timekeeper();

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

       // Now 'tk3' are state Transient.
       System.out.println("- tk3 Persistent? " + session.contains(tk3));

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

       // Here Hibernate checks, 'tk3' have ID or not (timekeeperId)
       // If no, it will be assigned automatically
       session.saveOrUpdate(tk3);

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

       // Now 'tk3' has Persistent state
       // It has been managed in Session.
       // But no action insert, or update to DB.
       // ==> true
       System.out.println("- tk3 Persistent? " + session.contains(tk3));

       System.out.println("- Call flush..");

       // To push data into the DB, call flush().
       // If not call flush() data will be pushed to the DB when calling commit().
       // Now possible to Insert or Update DB. (!!!)
       // Depending on the ID of 'tk3' exists in the DB or not
       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();
       }
   }
}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.6 - 瞬態轉爲持久化狀態:使用merge(Object)

創建一個JAVA類文件:MergeTransientDemo.java,用於演示對象的持久化(Persistent)狀態。

package com.yiibai;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;
import com.yiibai.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) {

       // Note:
       // Configuring of timekeeperId
       // @GeneratedValue(generator = "uuid")
       // @GenericGenerator(name = "uuid", strategy = "uuid2")        
       Timekeeper tk4 = new Timekeeper();

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

       // Now 'tk4' Transient status.
       System.out.println("- tk4 Persistent? " + session.contains(tk4));

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


       // Hibernate2 has method saveOrUpdateCopy
       // Hibernate3 change saveOrUpdateCopy to merge
       // So there will be similarities between the two methods merge and copyOrUpdate
       // Here Hibernate check tk4 has ID or not
       // If not, Hibernate assign value to ID of tk4
       // Return copy of tk4.
       Timekeeper tk4Copy = (Timekeeper) session.merge(tk4);

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


       // Now 'tk4' still Transient state.
       // and 'tk4Copy' has Persistent status
       // No action with DB (insert or update).
       System.out.println("- tk4 Persistent? " + session.contains(tk4));

       // 'tk4Copy' has Persistent status
       // ==> true
       System.out
               .println("- tk4Copy Persistent? " + session.contains(tk4Copy));

       System.out.println("- Call flush..");


       // This time have Insert or Update to DB. (!!!)
       session.flush();

       // 'tk4' still Transitent, after flush().
       // merge(..) safer than 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();
       }
   }
}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.7 - 持久化轉變爲分離狀態

由Hibernate管理的持久化(Persistent)條件中的一個對象可以通過以下兩個Session的方法轉換爲Detached(獨立於Hibernate的管理)狀態:

  • evict (Object) - 從Hibernate管理中刪除一個對象

  • clear() - 從Hibernate管理的對象中刪除所有對象。

當然,當Session調用順序爲:commit()close()rollback()時,當前會話已經完成。 此會話的所有Persistence對象將從新打開的會話中分離。

Hibernate快速入門

創建一個JAVA類文件:EvictDemo.java,用於演示對象持久化轉變爲分離狀態。

package com.yiibai;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.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();

           // This is object has Persistent status
           emp = DataUtils.findEmployee(session, "E7499");

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


           // using evict() to evicts a single object from the session
           session.evict(emp);

           // Now 'emp' has Detached status
           // ==> false
           System.out.println("- emp Persistent? " + session.contains(emp));


           // All change on the 'emp' will not update
           // if not reatach 'emp' to session
           emp.setEmpNo("NEW");

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

執行上面代碼,得到以下結果 -

Hibernate快速入門

創建一個JAVA類文件:ClearDemo.java,用於演示將所有對象持久化轉變爲分離狀態。

package com.yiibai;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Department;
import com.yiibai.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();

           // It is an object has Persistent status.
           emp = DataUtils.findEmployee(session, "E7499");
           dept = DataUtils.findDepartment(session, "D10");


           // clear() evicts all the objects in the session.
           session.clear();


           // Now 'emp' & 'dept' has Detached status
           // ==> false
           System.out.println("- emp Persistent? " + session.contains(emp));
           System.out.println("- dept Persistent? " + session.contains(dept));

           // All change on the 'emp' will not update
           // if not reatach 'emp' to session
           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();
       }
   }
}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.8 - 分離狀態轉變爲持久化狀態

Hibernate管理分離的對象可以通過以下Session的一些方法重新附加:

  • update(Object)
  • saveOrUpdate(Object)
  • merge(Object)
  • refresh(Object)
  • lock(Object)

可以在以下示例中看到這些方法的區別:

Hibernate快速入門

11.9-分離轉變爲持久性狀態:使用update(Object)

創建一個JAVA類文件:UpdateDetachedDemo.java,用於演示將對象分離轉變爲持久性狀態。

package com.yiibai;

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

import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Department;
import com.yiibai.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();

            // This is a Persistent object.
            emp = DataUtils.findEmployee(session1, "E7499");

            // session1 was closed after a commit is called.
            session1.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session1.getTransaction().rollback();
        }

        // Open other session
        Session session2 = factory.getCurrentSession();

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

            // Check state of 'emp'
            // ==> false
            System.out.println("- emp Persistent? " + session2.contains(emp));

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

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

            // update (..) is only used for Detached object.
            // (Not for Transient object).
            // Use the update (emp) to bring back emp Persistent state.
            session2.update(emp);

            // Call flush
            // Update statement will be called.
            session2.flush();

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

            // session2 was closed after a commit is called.
            session2.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session2.getTransaction().rollback();
        }

    }
}

執行上面代碼,得到以下結果 -

Hibernate快速入門

11.10 - 分離轉變爲持久性狀態:使用saveOrUpdate(Object)

創建一個JAVA類文件:SaveOrUpdateDetachedDemo.java,用於演示將對象分離轉變爲持久性狀態。

package com.yiibai;

import java.util.Random;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class SaveOrUpdateDetachedDemo {

   public static void main(String[] args) {

       // An object Detached state.
       Employee emp = getEmployee_Detached();

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

       // Random delete or not delete Employee
       boolean delete = deleteOrNotDelete(emp.getEmpId());

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

       // Call saveOrUpdate for detached object.
       saveOrUpdate_test(emp);

       // After call saveOrUpdate()
       System.out.println(" - EMP ID " + emp.getEmpId());
   }


   // Return Employee object has Detached state
   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 has been managed by Hibernate
           session1.persist(emp);

           // session1 was closed after a commit is called.
           // An Employee record are insert into DB.            
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }
       // Session1 closed 'emp' switch to Detached state.
       return emp;
   }

   // Random: delete or not delete.
   private static boolean deleteOrNotDelete(Long empId) {
       // A random number 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();

       // Open other session
       Session session3 = factory.getCurrentSession();

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

           // Check state of emp
           // ==> false
           System.out.println(" - emp Persistent? " + session3.contains(emp));

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

           // Set new salary for Detached emp object.
           emp.setSalary(emp.getSalary() + 100);


           // Using saveOrUpdate(emp) to switch emp to Persistent state
           // Note: If exists object same ID in session, this method raise Exception
           //
           // Now, no action with DB.            
           session3.saveOrUpdate(emp);

           // By pushing data into the DB.
           // It will call a Insert or update statement.
           // If the record is deleted before ==> insert
           // Else ==> update.    
           session3.flush();

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

           // session3 was closed after a commit is called.
           session3.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session3.getTransaction().rollback();
       }

   }

}

執行上面代碼,多幾次運行示例錯碼,可以看到兩種情況,saveOrUpdate()方法調用在數據上插入或更新。得到以下結果 -

Hibernate快速入門

11.11-分離轉變爲持久性狀態:使用merge(Object)

Hibernate 2版本有saveOrUpdateCopy(Object)方法。從Hibernate 3起,它被重命名爲merge(Object)。 因此與saveOrUpdate()相比,merge()方法有一些相似性和差異。

merge(Object)不會將對象置於Hibernate的管理下,而是創建一個對象的副本,而不是管理該對象。

如果調用saveOrUpdate(aObject)aObject由Hibernate管理,並且與aObject具有相同的ID將會拋出異常,但是使用merge(aObject)時不會得到此異常。

創建一個JAVA類文件:MergeDetachedDemo.java,用於演示將對象分離轉變爲持久性狀態。

package com.yiibai;

import java.util.Random;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class MergeDetachedDemo {

   public static void main(String[] args) {

       // An object has Detached status
       Employee emp = getEmployee_Detached();

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

       // Random: delete or not delete the Employee by ID.
       boolean delete = deleteOrNotDelete(emp.getEmpId());

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

       // Call saveOrUpdate Detached object
       saveOrUpdate_test(emp);

       // After call saveOrUpdate
       // ...
       System.out.println(" - EMP ID " + emp.getEmpId());
   }


   // Method return Employee object
   // and has Detached status.
   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' has Persistant state
           session1.persist(emp);


           // session1 was closed after a commit is called.
           // An Employee record are insert into DB.
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }
       // session1 closed, 'emp' switched Detached state.
       return emp;
   }


   // Delete Employee by ID
   // Random: delete or not delete
   private static boolean deleteOrNotDelete(Long empId) {
       // A random number 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();

       // Open other session
       Session session3 = factory.getCurrentSession();

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


           // The fact, 'emp' has Detached state
           // It is not managed by Hibernate.
           // Check the status of emp:
           // ==> false
           System.out.println(" - emp Persistent? " + session3.contains(emp));

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

           // Set new salary for Detached object 'emp'
           emp.setSalary(emp.getSalary() + 100);


           // merge(emp) return empMerge, a copy of 'emp',
           // empMerge managed by Hibernate
           // 'emp' still in Detached state
           //
           // At this time there is no action regarding 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));


           // Push data into the DB.
           // Here it is possible to create the Insert or Update on DB.
           // If the corresponding record has been deleted by someone, it insert
           // else it update
           session3.flush();

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

           // session3 closed after a commit is called.
           session3.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session3.getTransaction().rollback();
       }

   }

}

執行上面代碼,多幾次運行示例錯碼,可以看到兩種情況,得到以下結果 -

Hibernate快速入門

11.12 - 分離轉變爲持久性狀態:使用refresh(Object)

創建一個JAVA類文件:RefreshDetachedDemo.java,用於演示將對象分離轉變爲持久性狀態。

package com.yiibai;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yiibai.DataUtils;
import com.yiibai.HibernateUtils;
import com.yiibai.entities.Employee;

public class RefreshDetachedDemo {

   public static void main(String[] args) {

       // an Object with Detached status
       Employee emp = getEmployee_Detached();

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

       // Refresh Object  
       refresh_test(emp);
   }


   // Return Employee object has Detached state
   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 was closed after a commit is called.
           // An Employee record are insert into DB.
           session1.getTransaction().commit();
       } catch (Exception e) {
           e.printStackTrace();
           session1.getTransaction().rollback();
       }
       // Session1 closed 'emp' switch to Detached state.
       return emp;
   }

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

       // Open other session
       Session session2 = factory.getCurrentSession();

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


           // Check the status of 'emp' (Detached)
           // ==> false
           System.out.println(" - emp Persistent? " + session2.contains(emp));

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

            // Set new salary for 'emp'.
           emp.setSalary(emp.getSalary() + 100);


           // refresh: make a query statement
           // and switch 'emp' to Persistent state
           // The changes are ignored
           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();
       }

   }

}

執行上面代碼,多幾次運行示例錯碼,可以看到兩種情況,得到以下結果 -

Hibernate快速入門