Hibernate 简明教程
Hibernate - ORM Overview
What is JDBC?
JDBC 代表 Java Database Connectivity 。它提供了一组 Java API,用于从 Java 程序访问关系数据库。这些 Java API 使 Java 程序能够执行 SQL 语句和与任何 SQL 兼容数据库进行交互。
JDBC 提供了一个灵活的架构来编写一个数据库独立的应用程序,该应用程序可以在不同的平台上运行并与不同的 DBMS 交互,而无需进行任何修改。
Pros and Cons of JDBC
Pros of JDBC |
Cons of JDBC |
干净而简单的 SQL 处理使用大型数据时性能良好非常适合小型应用程序语法简单,易于学习 |
如果用于大型项目,则很复杂编程开销大没有封装难以实现 MVC 概念查询特定于 DBMS |
Why Object Relational Mapping (ORM)?
当我们使用面向对象系统时,对象模型和关系数据库之间存在不匹配。RDBMS 以表格形式表示数据,而面向对象语言(例如 Java 或 C#)则以相互连接的对象图表的形式表示数据。
考虑以下具有适当构造函数和关联公共函数的 Java 类:
public class Employee {
private int id;
private String first_name;
private String last_name;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.first_name = fname;
this.last_name = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public String getFirstName() {
return first_name;
}
public String getLastName() {
return last_name;
}
public int getSalary() {
return salary;
}
}
考虑将上述对象存储和检索到以下 RDBMS 表中:
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
第一个问题是,如果在开发了一些页面或应用程序后需要修改数据库的设计,该怎么办?其次,在关系数据库中加载和存储对象会使我们面临以下五个不匹配问题:
Sr.No. |
Mismatch & Description |
1 |
Granularity 有时你会遇到一个对象模型,它比数据库中对应表的数量有更多的类。 |
2 |
Inheritance RDBMS 并没有定义类似于继承的概念,这是面向对象编程语言中的一种自然范式。 |
3 |
Identity 一个 RDBMS 定义了一个完全相同的概念:“主键”。然而,Java 定义了对象标识 (a==b) 和对象相等 (a.equals(b))。 |
4 |
Associations 面向对象语言使用对象引用表示关联,而 RDBMS 将关联表示为外键列。 |
5 |
Navigation 在 Java 和 RDBMS 中访问对象的方式有根本性的不同。 |
*O*对象-*R*关系映射 (ORM) 是解决上述所有阻抗失配问题的办法。
What is ORM?
ORM 代表 *O*对象-*R*关系映射 (ORM) 是一种用于转换关系数据库和诸如 Java、C# 等面向对象编程语言之间数据的编程技术。
与纯 JDBC 相比,一个 ORM 系统有以下优点:
Sr.No. |
Advantages |
1 |
让业务代码访问对象而不是数据库表。 |
2 |
将 SQL 查询的详细信息隐藏在面向对象逻辑中。 |
3 |
基于“底层”的 JDBC。 |
4 |
无需处理数据库实现。 |
5 |
实体基于业务概念而不是数据库结构。 |
6 |
事务管理和自动键生成。 |
7 |
Fast development of application. |
一个 ORM 解决方案包含以下四个实体:
Sr.No. |
Solutions |
1 |
对持久类对象执行基本 CRUD 操作的 API。 |
2 |
一个语言或 API 用于指定引用类和类属性的查询。 |
3 |
一种用于指定映射元数据的可配置工具。 |
4 |
与事务性对象交互以执行脏数据检查、惰性关联提取和其他优化函数的技术。 |
Hibernate - Overview
Hibernate 是 Java 的 *O*bject-*R*elational *M*apping (ORM) 解决方案。它是一个由 Gavin King 于 2001 年创建的开源持久性框架。它是面向任何 Java 应用程序的强大、高性能对象关系持久性和查询服务。
Hibernate 将 Java 类映射到数据库表和从 Java 数据类型到 SQL 数据类型,免除了 95% 的开发人员与常见的与数据持久性相关编程任务。
Hibernate 介于传统 Java 对象和数据库服务器之间,可以基于适当的 O/R 机制和模式处理持久化这些对象的所有工作。
Hibernate Advantages
-
Hibernate 使用 XML 文件对 Java 类进行到数据库表的映射,并且不必编写任何代码。
-
提供将 Java 对象直接存取数据库和从数据库检索的简单 API。
-
如果数据库或任何表中发生更改,则只需要更改 XML 文件属性。
-
抽象了不熟悉的 SQL 类型,并提供了一种处理熟悉的 Java 对象的方法。
-
Hibernate 无需应用程序服务器即可运行。
-
处理数据库中对象的复杂关联。
-
使用智能提取策略,将数据库访问最小化。
-
提供简单的 data 查询。
Hibernate - Architecture
Hibernate 具有分层架构,它帮助用户在不知道底层 API 的情况下进行操作。Hibernate 利用数据库和配置数据向应用程序提供持久性服务(和持久性对象)。
以下是 Hibernate 应用程序架构的高级视图。
以下是对 Hibernate 应用程序架构及其重要核心类的详细视图。
Hibernate 使用各种现有的 Java API,如 JDBC、Java 事务 API(JTA)、Java 命名和目录接口 (JNDI)。JDBC 提供对关系数据库通用功能的初级抽象,使几乎任何带有 JDBC 驱动的数据库都可以受 Hibernate 支持。JNDI 和 JTA 允许 Hibernate 与 J2EE 应用程序服务器集成。
以下部分简要描述了 Hibernate 应用程序架构中涉及的每个类对象。
Configuration Object
Configuration 对象是您在任何 Hibernate 应用程序中创建的第一个 Hibernate 对象。它通常仅在应用程序初始化期间创建一次。它表示 Hibernate 所需的配置或属性文件。
Configuration 对象提供两个键组件:
-
Database Connection - 由 Hibernate 支持的一个或多个配置文件处理。这些文件为 hibernate.properties 和 hibernate.cfg.xml 。
-
Class Mapping Setup - 此组件创建 Java 类和数据库表之间的连接。
SessionFactory Object
配置对象用于创建 SessionFactory 对象,后者使用提供的配置文件为应用程序配置 Hibernate,并允许实例化 Session 对象。SessionFactory 是一个线程安全对象,可用于应用程序的所有线程。
SessionFactory 是一个重量级对象;它通常在应用程序启动期间创建并保留以便以后使用。您需要为使用独立配置文件的每个数据库创建一个 SessionFactory 对象。因此,如果您要使用多个数据库,则需要创建多个 SessionFactory 对象。
Session Object
使用会话来与数据库获得物理连接。会话对象是轻量的,并且设计为在需要与数据库进行交互时进行实例化。持久性对象通过会话对象保存并检索。
会话对象不应长期保持打开状态,因为它们通常不是线程安全的,并且应根据需要创建并销毁它们。
Transaction Object
事务代表与数据库的工作单元,并且大多数 RDBMS 都支持事务功能。Hibernate 中的事务由底层事务管理器和事务(来自 JDBC 或 JTA)处理。
这是一个可选对象,Hibernate 应用程序可以选择不使用此接口,而是在其自己的应用程序代码中管理事务。
Hibernate - Environment
本章解释如何安装 Hibernate 和其他相关软件包,为 Hibernate 应用程序准备环境。我们将使用 MySQL 数据库试验 Hibernate 示例,所以请确保已经为 MySQL 数据库设置了安装程序。有关 MySQL 的更多详细信息,您可以查看我们的 MySQL Tutorial 。
Downloading Hibernate
假设您已经在系统上安装了最新版本的 Java。以下是下载和安装 Hibernate 到系统中的简单步骤:
-
选择是要在 Windows 还是 Unix 上安装 Hibernate,然后继续进行下一步,下载适用于 Windows 的 .zip 文件,适用于 Unix 的 .tz 文件。
-
从 http://www.hibernate.org/downloads 下载最新版本的 Hibernate。
-
在编写本教程时,我下载了 hibernate-distribution3.6.4.Final ,当您解压缩下载的文件时,它将为您提供如下所示的目录结构:
Installing Hibernate
下载并解压缩最新版本的 Hibernate 安装文件后,您需要执行以下两个简单步骤。确保正确设置 CLASSPATH 变量,否则在编译应用程序时将会遇到问题。
-
现在,将 /lib 中的所有库文件复制到 CLASSPATH 中,并更改 classpath 变量以包含所有 JAR:
-
最后,将 hibernate3.jar 文件复制到 CLASSPATH 中。此文件位于安装根目录中,并且是 Hibernate 执行其工作所需的主要 JAR。
Hibernate Prerequisites
以下是 Hibernate 需要的包/库列表,您应在开始使用 Hibernate 之前安装它们。要安装这些包,您必须从 /lib 将库文件复制到您的类路径 CLASSPATH 中,并相应地更改您的 CLASSPATH 变量。
Sr.No. |
Packages/Libraries |
1 |
dom4j XML parsing www.dom4j.org/ |
2 |
Xalan XSLT Processor https://xml.apache.org/xalan-j/ |
3 |
Xerces Xerces Java 解析器 https://xml.apache.org/xerces-j/ |
4 |
cglib 对 Java 类在运行时的适当更改 http://cglib.sourceforge.net/ |
5 |
log4j Logging Faremwork https://logging.apache.org/log4j |
6 |
Commons Logging, Email etc. https://jakarta.apache.org/commons |
7 |
SLF4J Java 的日志记录外观 https://www.slf4j.org |
Hibernate - Configuration
Hibernate 需要提前知道——在哪里找到映射信息,这些映射信息定义了 Java 类和数据库表之间的相互关系。Hibernate 还需要一组与数据库和其他相关参数相关的配置设置。所有此类信息通常按标准 Java 属性文件 hibernate.properties 提供,或按名为 hibernate.cfg.xml 的 XML 文件提供。
我在示例中将考虑使用 XML 格式的文件 hibernate.cfg.xml 来指定必需的 Hibernate 属性。大多数属性采用其默认值,并且不必在属性文件中指定它们(除非确实需要)。此文件保存在应用程序类路径的根目录中。
Hibernate Properties
以下是一些重要属性,在独立情况下,您需要对其进行数据库配置:
Sr.No. |
Properties & Description |
1 |
hibernate.dialect 此属性使 Hibernate 为所选数据库生成适当的 SQL。 |
2 |
hibernate.connection.driver_class The JDBC driver class. |
3 |
hibernate.connection.url 连接到数据库实例的 JDBC URL。 |
4 |
hibernate.connection.username The database username. |
5 |
hibernate.connection.password The database password. |
6 |
hibernate.connection.pool_size 限制了在 Hibernate 数据库连接池中等待的连接数量。 |
7 |
hibernate.connection.autocommit 允许对 JDBC 连接使用自动提交模式。 |
如果您使用数据库与应用程序服务器和 JNDI 一起使用,则需要配置以下属性:
Sr.No. |
Properties & Description |
1 |
hibernate.connection.datasource 应用程序服务器上下文中定义的 JNDI 名称,您正在为应用程序使用该 JNDI 名称。 |
2 |
hibernate.jndi.class JNDI 的 InitialContext 类。 |
3 |
hibernate.jndi.<JNDIpropertyname> 将您想要的任何 JNDI 属性传递给 JNDI InitialContext。 |
4 |
hibernate.jndi.url 提供 JNDI 的 URL。 |
5 |
hibernate.connection.username The database username. |
6 |
hibernate.connection.password The database password. |
Hibernate with MySQL Database
MySQL 是当今最流行的开源数据库系统之一。我们将创建 hibernate.cfg.xml 配置文件,并将其放入应用程序类路径的根目录中。您必须确保您的 MySQL 数据库中存在 testdb 数据库,并且您有用户 test 可用于访问数据库。
XML 配置文件必须符合 Hibernate 3 Configuration DTD,可以在 http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd 中获取该 DTD。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume test is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
上述配置文件包含 <mapping> 标记,这些标记与 Hibernate 映射文件相关,我们将在下一章中看到 Hibernate 映射文件确切是什么以及我们如何以及为什么使用它?
以下是各种重要的数据库方言属性类型的列表 -
Sr.No. |
Database & Dialect Property |
1 |
DB2 org.hibernate.dialect.DB2Dialect |
2 |
HSQLDB org.hibernate.dialect.HSQLDialect |
3 |
HypersonicSQL org.hibernate.dialect.HSQLDialect |
4 |
Informix org.hibernate.dialect.InformixDialect |
5 |
Ingres org.hibernate.dialect.IngresDialect |
6 |
Interbase org.hibernate.dialect.InterbaseDialect |
7 |
Microsoft SQL Server 2000 org.hibernate.dialect.SQLServerDialect |
8 |
Microsoft SQL Server 2005 org.hibernate.dialect.SQLServer2005Dialect |
9 |
Microsoft SQL Server 2008 org.hibernate.dialect.SQLServer2008Dialect |
10 |
MySQL org.hibernate.dialect.MySQLDialect |
11 |
Oracle (any version) org.hibernate.dialect.OracleDialect |
12 |
Oracle 11g org.hibernate.dialect.Oracle10gDialect |
13 |
Oracle 10g org.hibernate.dialect.Oracle10gDialect |
14 |
Oracle 9i org.hibernate.dialect.Oracle9iDialect |
15 |
PostgreSQL org.hibernate.dialect.PostgreSQLDialect |
16 |
Progress org.hibernate.dialect.ProgressDialect |
17 |
SAP DB org.hibernate.dialect.SAPDBDialect |
18 |
Sybase org.hibernate.dialect.SybaseDialect |
19 |
Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect |
Hibernate - Sessions
使用会话来与数据库获得物理连接。会话对象是轻量的,并且设计为在需要与数据库进行交互时进行实例化。持久性对象通过会话对象保存并检索。
会话对象不应该长时间保持打开状态,因为它们通常不是线程安全的,并且应该根据需要创建和销毁它们。会话的主要功能是为映射实体类的实例提供、创建、读取和删除操作。
实例在给定的时间点可以处于以下三个状态之一 -
-
transient - 持久类的某个新实例,但未关联到会话,并且在数据库中没有表示,Hibernate 认为没有标识符值是临时的。
-
persistent - 您可以通过将一个瞬态实例与一个会话关联使其持久化。持久实例在数据库中具有一个表示,一个标识符值,并且与一个会话关联。
-
detached - 一旦我们关闭 Hibernate 会话,持久实例将变成一个分离实例。
如果其持久类是可序列化的,则会话实例是可序列化的。一个典型的交易应该使用以下习语 -
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// do some work
...
tx.commit();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
如果会话抛出异常,则必须回滚事务并放弃会话。
Session Interface Methods
Session 接口提供了一些方法,但我只列出几个重要的方法,我们将在本教程中使用它们。您可以检查 Hibernate 文档以获取与 Session 和 SessionFactory 相关联的方法的完整列表。
Sr.No. |
Session Methods & Description |
1 |
Transaction beginTransaction() 开始一个工作单元并返回关联的事务对象。 |
2 |
void cancelQuery() 取消当前查询的执行。 |
3 |
void clear() 完全清除会话。 |
4 |
Connection close() 通过释放 JDBC 连接并清理来结束会话。 |
5 |
Criteria createCriteria(Class persistentClass) 为给定的实体类或实体类的超类创建一个新的 Criteria 实例。 |
6 |
Criteria createCriteria(String entityName) 为给定的实体名称创建一个新的 Criteria 实例。 |
7 |
Serializable getIdentifier(Object object) 返回与该会话关联的给定实体的标识符值。 |
8 |
Query createFilter(Object collection, String queryString) 为给定的集合和过滤器字符串创建一个新的 Query 实例。 |
9 |
Query createQuery(String queryString) 为给定的 HQL 查询字符串创建新的 Query 实例。 |
10 |
SQLQuery createSQLQuery(String queryString) 为给定的 SQL 查询字符串创建一个新的 SQLQuery 实例。 |
11 |
void delete(Object object) 从数据存储区删除持久实例。 |
12 |
void delete(String entityName, Object object) 从数据存储区删除持久实例。 |
13 |
Session get(String entityName, Serializable id) 返回给定命名的实体的持久实例,如果不存在持久实例,则返回 null。 |
14 |
SessionFactory getSessionFactory() 获取创建此会话的会话工厂。 |
15 |
void refresh(Object object) 从底层数据库重新读取给定实例的状态。 |
16 |
Transaction getTransaction() 获取与该会话关联的事务实例。 |
17 |
boolean isConnected() 检查会话当前是否已连接。 |
18 |
boolean isDirty() 该会话是否包含必须与数据库同步的任何更改? |
19 |
boolean isOpen() 检查会话是否仍处于打开状态。 |
20 |
Serializable save(Object object) 持久给定瞬态实例,首先分配生成的标识符。 |
21 |
void saveOrUpdate(Object object) 保存 (save) 或更新 (update) 给定实例。 |
22 |
void update(Object object) 使用给定分离实例的标识符更新持久实例。 |
23 |
void update(String entityName, Object object) 使用给定分离实例的标识符更新持久实例。 |
Hibernate - Persistent Class
Hibernate 的整个概念是获取 Java 类属性中的值,并将它们持久化到数据库表中。映射文档帮助 Hibernate 确定如何从类中提取值,并将其与表和关联字段进行映射。
在数据库表中存储其对象或实例的 Java 类在 Hibernate 中称为持久类。如果这些类遵循一些简单规则,Hibernate 就能很好地工作,这些规则也称为 Plain Old Java Object (POJO) 编程模型。
以下为持久类的主要规则,但其中没有一项属于硬性要求:
-
所有将被持久化的 Java 类都需要一个默认构造函数。
-
所有类都应包含一个 ID,以便在 Hibernate 和数据库中轻松识别对象。此属性映射到数据库表的基准密钥列。
-
所有将被持久化的属性应声明为私有的,并以 JavaBean 样式定义 getXXX 和 setXXX 方法。
-
Hibernate 的中心功能代理取决于持久类是非 final 的,或者声明所有公共方法的接口的实现。
-
所有没有扩展或实现 EJB 框架所需的一些专门类和接口的类。
POJO 名称用于强调给定对象是普通的 Java 对象,而不是特殊对象,特别是不是 Enterprise JavaBean。
Simple POJO Example
基于以上提到的几个规则,我们可以如下定义一个 POJO 类:
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Hibernate - Mapping Files
对象/关系映射通常在 XML 文档中定义。此映射文件指示 Hibernate — 如何将定义的类或类映射到数据库表?
尽管许多 Hibernate 用户选择手动编写 XML,但存在许多工具可用于生成映射文档。对于高级 Hibernate 用户,这些工具包括 XDoclet, Middlegen 和 AndroMDA 。
让我们考虑我们之前定义的 POJO 类,其对象将在下一部分定义的表中持久化。
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
将有一个表对应于您愿意提供持久性的每个对象。考虑需要存储并检索到以下 RDBMS 表中的上述对象 −
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
基于上述两个实体,我们可以定义以下映射文件,指示 Hibernate 如何将定义的类或类映射到数据库表。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
您应该将映射文档保存在具有格式 <classname>.hbm.xml 的文件中。我们将映射文档保存在文件 Employee.hbm.xml 中。
让我们详细了解映射文件 -
-
映射文档是 XML 文档,拥有 <hibernate-mapping> 作为根元素,其中包含所有 <class> 元素。
-
<class> 元素是用来定义从 Java 类到数据库表间的特定映射。使用 class 元素的 name 属性指定 Java 类名,使用 table 属性指定数据库 table 名。
-
<meta> 元素是可选元素,可用来创建类描述。
-
<id> 元素将类中的唯一 ID 属性映射到数据库表的为主键。id 元素的 name 属性引用类中的属性,而 column 属性引用数据库表中的列。 type 属性保存 Hibernate 映射类型,这些映射类型将从 Java 转换为 SQL 数据类型。
-
id 元素中的 <generator> 元素用来自动生成主键值。generator 元素的 class 属性被设置为 native ,以便让 Hibernate 选择 identity, sequence 或 hilo 算法来根据底层数据库的功能创建主键。
-
<property> 元素用来将 Java 类属性映射到数据库表中的一列。element 的 name 属性引用类中的属性,而 column 属性引用数据库表中的列。 type 属性保存 Hibernate 映射类型,这些映射类型将从 Java 转换为 SQL 数据类型。
还有其他可在映射文档中使用的属性和元素,在我讨论其他 Hibernate 相关主题时将尝试涵盖尽可能多的内容。
Hibernate - Mapping Types
在准备 Hibernate 映射文档时,你会发现你将 Java 数据类型映射到 RDBMS 数据类型。在映射文件中声明和使用 types 并非 Java 数据类型;它们也不是 SQL 数据库类型。这些类型称为 Hibernate mapping types ,可以从 Java 转换为 SQL 数据类型,反之亦然。
本章列出了所有基本数据类型、日期和时间、大型对象以及其他各种内置映射类型。
Primitive Types
Mapping type |
Java type |
ANSI SQL Type |
integer |
int or java.lang.Integer |
INTEGER |
long |
long or java.lang.Long |
BIGINT |
short |
short or java.lang.Short |
SMALLINT |
float |
float or java.lang.Float |
FLOAT |
double |
double or java.lang.Double |
DOUBLE |
big_decimal |
java.math.BigDecimal |
NUMERIC |
character |
java.lang.String |
CHAR(1) |
string |
java.lang.String |
VARCHAR |
byte |
byte or java.lang.Byte |
TINYINT |
boolean |
boolean or java.lang.Boolean |
BIT |
yes/no |
boolean or java.lang.Boolean |
CHAR(1) ('Y' or 'N') |
true/false |
boolean or java.lang.Boolean |
CHAR(1) ('T' or 'F') |
Date and Time Types
Mapping type |
Java type |
ANSI SQL Type |
date |
java.util.Date or java.sql.Date |
DATE |
time |
java.util.Date or java.sql.Time |
TIME |
timestamp |
java.util.Date or java.sql.Timestamp |
TIMESTAMP |
calendar |
java.util.Calendar |
TIMESTAMP |
calendar_date |
java.util.Calendar |
DATE |
Hibernate - Examples
现在我们举个例子,了解如何使用 Hibernate 在独立的应用程序中提供 Java 持久性。我们将介绍在使用 Hibernate 技术创建 Java 应用程序时涉及到的不同步骤。
Create POJO Classes
创建应用程序的第一步是构建 Java POJO 类或类,具体取决于将持久化到数据库的应用程序。考虑我们的 Employee 类,其方法 getXXX 和 setXXX 使其成为 JavaBeans 兼容类。
POJO(普通旧 Java 对象)是不扩展或不实现 EJB 框架分别要求的某些专门类和接口的 Java 对象。所有常规 Java 对象都是 POJO。
在设计要由 Hibernate 持久化的类时,重要的是提供 JavaBeans 兼容代码以及一个属性,该属性可像 Employee 类中的 id 属性一样用作索引。
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Create Database Tables
第二步是创建数据库中的表。希望提供持久性的每个对象都会对应一个表。考虑需要将以上对象存储和检索到以下 RDBMS 表中 -
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
Create Mapping Configuration File
此步骤是创建映射文件,指示 Hibernate 如何将已定义的类或类映射到数据库表。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
应该以格式 <classname>.hbm.xml 保存映射文档到文件中。我们将映射文档保存到 Employee.hbm.xml 文件中。我们详细了解下映射文档 -
-
映射文档是 XML 文档,拥有 <hibernate-mapping> 作为根元素,其中包含所有 <class> 元素。
-
<class> 元素是用来定义从 Java 类到数据库表间的特定映射。使用 class 元素的 name 属性指定 Java 类名,使用 table 属性指定数据库表名。
-
<meta> 元素是可选元素,可用来创建类描述。
-
<id> 元素将类中的唯一 ID 属性映射到数据库表的为主键。id 元素的 name 属性引用类中的属性,而 column 属性引用数据库表中的列。 type 属性保存 Hibernate 映射类型,这些映射类型将从 Java 转换为 SQL 数据类型。
-
id 元素中的 <generator> 元素用来自动生成主键值。generator 元素的 class 属性被设置为 native ,以便让 Hibernate 选择 identity, sequence 或 hilo 算法来根据底层数据库的功能创建主键。
-
<property> 元素用来将 Java 类属性映射到数据库表中的一列。element 的 name 属性引用类中的属性,而 column 属性引用数据库表中的列。 type 属性保存 Hibernate 映射类型,这些映射类型将从 Java 转换为 SQL 数据类型。
还有其他可在映射文档中使用的属性和元素,在我讨论其他 Hibernate 相关主题时将尝试涵盖尽可能多的内容。
Create Application Class
最后,我们将使用 main() 方法创建我们的应用程序类来运行应用程序。我们将使用此应用程序来保存一些 Employee 的记录,然后我们将对这些记录应用 CRUD 操作。
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
/* List down all the employees */
ME.listEmployees();
/* Update employee's records */
ME.updateEmployee(empID1, 5000);
/* Delete an employee from the database */
ME.deleteEmployee(empID2);
/* List down new list of the employees */
ME.listEmployees();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees */
public void listEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
List employees = session.createQuery("FROM Employee").list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to UPDATE salary for an employee */
public void updateEmployee(Integer EmployeeID, int salary ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
employee.setSalary( salary );
session.update(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to DELETE an employee from the records */
public void deleteEmployee(Integer EmployeeID){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
session.delete(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
-
如配置章节中所述,创建 hibernate.cfg.xml 配置文件。
-
如上所示创建 Employee.hbm.xml 映射文件。
-
创建 Employee.java 源文件(如上所示),并进行编译。
-
按上方代码创建一个 ManageEmployee.java 源文件并进行编译。
-
执行 ManageEmployee 二进制文件来运行该程序。
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........
First Name: Zara Last Name: Ali Salary: 1000
First Name: Daisy Last Name: Das Salary: 5000
First Name: John Last Name: Paul Salary: 10000
First Name: Zara Last Name: Ali Salary: 5000
First Name: John Last Name: Paul Salary: 10000
如果检查您的 EMPLOYEE 表,它应包含以下记录 −
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+
| id | first_name | last_name | salary |
+----+------------+-----------+--------+
| 29 | Zara | Ali | 5000 |
| 31 | John | Paul | 10000 |
+----+------------+-----------+--------+
2 rows in set (0.00 sec
mysql>
Hibernate - O/R Mappings
到目前为止,我们已经看到了使用 hibernate 的非常基本的 O/R 映射,但还有三个非常重要的映射主题,我们必须详细了解。
它们是:
-
Mapping of collections,
-
实体类之间的关联映射,以及
-
Component Mappings.
Collections Mappings
如果实体或类具有针对特定变量的值集合,那么我们可以使用 java 中可用的任何一个集合接口来映射这些值。Hibernate 可以保留 java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List 和任何持久实体或值 array 的实例。
Sr.No. |
集合类型和映射说明 |
1 |
java.util.Set 这与 <set> 元素映射,并使用 java.util.HashSet 初始化 |
2 |
java.util.SortedSet 这与 <set> 元素映射,并使用 java.util.TreeSet 初始化。 sort 属性可以设置为比较器或自然顺序。 |
3 |
java.util.List 这与 <list> 元素映射,并使用 java.util.ArrayList 初始化 |
4 |
java.util.Collection 这与 <bag> 或 <ibag> 元素映射,并使用 java.util.ArrayList 初始化 |
5 |
java.util.Map 这与 <map> 元素映射,并使用 java.util.HashMap 初始化 |
6 |
java.util.SortedMap 这与 <map> 元素映射,并使用 java.util.TreeMap 初始化。 sort 属性可以设置为比较器或自然顺序。 |
Hibernate 使用 <primitive-array>(针对 Java 原始值类型)和 <array>(针对所有其他项)支持数组。但是,它们的使用频率很低,因此我不会在本教程中对它们进行讨论。
如果您想映射 Hibernate 不直接支持的用户自定义集合接口,您需要告知 Hibernate 您自定义集合的语义,但要做到这一点并不容易,也不建议使用。
Association Mappings
实体类之间的关联映射以及表之间的关系是 ORM 的核心。以下是可以表示对象之间关系基数的四种方式。关联映射可以是单向的,也可以是双向的。
Sr.No. |
Mapping type & Description |
1 |
Many-to-One 使用 Hibernate 映射多对一关系 |
2 |
One-to-One 使用 Hibernate 映射一对一关系 |
3 |
One-to-Many 使用 Hibernate 映射一对多关系 |
4 |
Many-to-Many 使用 Hibernate 映射多对多关系 |
Component Mappings
一个实体类很可能可以具有对另一个类的引用作为成员变量。如果被引用类没有自己的生命周期,并且完全依赖于所有实体类生命周期,那么被引用类因此而成为 Component class 。
以类似方式映射集合组件也是可能的,就像映射常规集合,但配置差异很小。我们将详细了解这两个映射,并提供示例。
Sr.No. |
Mapping type & Description |
1 |
Component Mappings 对拥有对另一个类的引用(作为成员变量)的类进行映射。 |
Hibernate - Annotations
到目前为止,你已经看到了 Hibernate 如何使用 XML 映射文件将数据从 POJO 转换为数据库表,反之亦然。Hibernate 注释是无需使用 XML 文件便可以定义映射的最新方式。你可以除了或替换 XML 映射元数据以外,还可以使用注释。
Hibernate 注释是以强大方式为对象和关系表映射提供元数据的方法。所有元数据与代码一起打包到 POJO Java 文件中,这有助于用户在开发期间同时了解表结构和 POJO。
如果你打算让应用程序可移植到其他 EJB 3 兼容的 ORM 应用程序,则必须使用注释来表示映射信息,但如果你仍然想要更大的灵活性,则应该选择基于 XML 的映射。
Environment Setup for Hibernate Annotation
首先,你必须确保正在使用 JDK 5.0,否则需要将 JDK 升级到 JDK 5.0 以利用对注释的本机支持。
其次,你需要安装 Hibernate 3.x 注释发行包,该包可从 Sourceforge 获得:( Download Hibernate Annotation ),并将 hibernate-annotations.jar, lib/hibernate-comons-annotations.jar 和 lib/ejb3-persistence.jar 从 Hibernate 注释发行包复制到 CLASSPATH。
Annotated Class Example
如上所述,在使用 Hibernate 注释时,所有元数据与代码一起打包到 POJO Java 文件中,这有助于用户在开发期间同时了解表结构和 POJO。
考虑我们准备使用以下 EMPLOYEE 表来存储我们的对象 -
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
以下是 Employee 类的映射,带有注释,以将对象映射到定义的 EMPLOYEE 表中 -
import javax.persistence.*;
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id @GeneratedValue
@Column(name = "id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "salary")
private int salary;
public Employee() {}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Hibernate 检测到 @Id 注释位于一个字段上,并假设它应该在运行时直接通过字段访问对象的属性。如果将 @Id 注释放在 getId() 方法上,就会默认允许通过 getter 和 setter 方法访问属性。因此,根据所选策略,所有其他的注释也都会放在字段上或 getter 方法上。
以下部分将解释在上述类中使用的注释。
@Entity Annotation
EJB 3 标准注释包含在 javax.persistence 包中,因此我们将此包作为第一步导入。其次,我们在 Employee 类中使用了 @Entity 注释,它将此类标记为实体 bean,因此它必定有一个无参数构造函数,并且至少具有受保护的作用域可见性。
@Table Annotation
@Table 注释允许您指定表中的详细信息,这些信息将用于保留数据库中的实体。
@Table 注释提供了四个属性,允许您覆盖表名称、其目录以及其架构,并强制执行表中列的唯一约束。目前,我们仅使用表名称,即 EMPLOYEE。
@Id and @GeneratedValue Annotations
每个实体 bean 都将有一个主键,您使用 @Id 注释在类上为此进行注释。主键可以是单个字段或多个字段的组合,具体取决于您的表结构。
默认情况下,@Id 注释将自动确定要使用的最合适的主键生成策略,但是您可以通过应用 @GeneratedValue 注释来覆盖该策略,该注释采用两个参数 strategy 和 generator ,我将不会在这里讨论它们,因此让我们仅使用默认的主键生成策略。让 Hibernate 确定要使用哪种生成器类型,可以使您的代码在不同的数据库之间具有可移植性。
@Column Annotation
@Column 注释用于指定字段或属性将映射到的列的详细信息。您可以将列注释与以下最常用的属性一起使用 -
-
name 属性允许显式地指定列的名称。
-
length 属性允许映射值的大小,特别是对于 String 值。
-
nullable 属性允许在生成架构时将列标记为 NOT NULL。
-
unique 属性允许将列标记为仅包含唯一值。
Create Application Class
最后,我们将使用 main() 方法创建我们的应用程序类来运行应用程序。我们将使用此应用程序来保存一些 Employee 的记录,然后我们将对这些记录应用 CRUD 操作。
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new AnnotationConfiguration().
configure().
//addPackage("com.xyz") //add package if used.
addAnnotatedClass(Employee.class).
buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
/* List down all the employees */
ME.listEmployees();
/* Update employee's records */
ME.updateEmployee(empID1, 5000);
/* Delete an employee from the database */
ME.deleteEmployee(empID2);
/* List down new list of the employees */
ME.listEmployees();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee();
employee.setFirstName(fname);
employee.setLastName(lname);
employee.setSalary(salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees */
public void listEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
List employees = session.createQuery("FROM Employee").list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to UPDATE salary for an employee */
public void updateEmployee(Integer EmployeeID, int salary ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
employee.setSalary( salary );
session.update(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to DELETE an employee from the records */
public void deleteEmployee(Integer EmployeeID){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
session.delete(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
Database Configuration
现在,让我们创建 hibernate.cfg.xml 配置文件来定义与数据库相关的参数。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
cohondob
</property>
</session-factory>
</hibernate-configuration>
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
-
从路径中删除 Employee.hbm.xml 映射文件。
-
创建 Employee.java 源文件(如上所示),并进行编译。
-
按上方代码创建一个 ManageEmployee.java 源文件并进行编译。
-
执行 ManageEmployee 二进制文件来运行该程序。
您将获得以下结果,并且记录将被创建到 EMPLOYEE 表中。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........
First Name: Zara Last Name: Ali Salary: 1000
First Name: Daisy Last Name: Das Salary: 5000
First Name: John Last Name: Paul Salary: 10000
First Name: Zara Last Name: Ali Salary: 5000
First Name: John Last Name: Paul Salary: 10000
如果检查您的 EMPLOYEE 表,它应包含以下记录 −
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+
| id | first_name | last_name | salary |
+----+------------+-----------+--------+
| 29 | Zara | Ali | 5000 |
| 31 | John | Paul | 10000 |
+----+------------+-----------+--------+
2 rows in set (0.00 sec
mysql>
Hibernate - Query Language
Hibernate 查询语言 (HQL) 是一种面向对象查询语言,类似于 SQL,但 HQL 不操作表和列,而是处理持久对象及其属性。HQL 查询由 Hibernate 转换为常规 SQL 查询,进而对数据库执行操作。
虽然可以使用本机 SQL 直接在 Hibernate 中使用 SQL 语句,但我建议尽可能使用 HQL 来避免数据库移植麻烦,并利用 Hibernate 的 SQL 生成和缓存策略。
SELECT、FROM 和 WHERE 等关键字不区分大小写,但 HQL 中的属性(如表和列名)区分大小写。
FROM Clause
如果要将一个完整的持久对象加载到内存中,可以使用 FROM 子句。以下是使用 FROM 子句的简单语法:
String hql = "FROM Employee";
Query query = session.createQuery(hql);
List results = query.list();
如果需要在 HQL 中完全限定一个类名,只需按如下所示指定包名和类名:
String hql = "FROM com.hibernatebook.criteria.Employee";
Query query = session.createQuery(hql);
List results = query.list();
AS Clause
AS 子句可用于为 HQL 查询中的类分配别名,特别是在有长查询时。例如,我们之前的简单示例将如下所示:
String hql = "FROM Employee AS E";
Query query = session.createQuery(hql);
List results = query.list();
AS 关键字是可选的,还可以按如下所示直接在类名后面指定别名:
String hql = "FROM Employee E";
Query query = session.createQuery(hql);
List results = query.list();
SELECT Clause
SELECT 子句比 from 子句可对结果集提供更多控制。如果想要获取对象的部分属性而不是整个对象,可以使用 SELECT 子句。以下是使用 SELECT 子句获取 Employee 对象的 first_name 字段的简单语法:
String hql = "SELECT E.firstName FROM Employee E";
Query query = session.createQuery(hql);
List results = query.list();
值得注意的是, Employee.firstName 是 Employee 对象的属性,而不是 EMPLOYEE 表的字段。
WHERE Clause
如果想要缩小从存储中返回的特定对象范围,可以使用 WHERE 子句。以下是使用 WHERE 子句的简单语法:
String hql = "FROM Employee E WHERE E.id = 10";
Query query = session.createQuery(hql);
List results = query.list();
ORDER BY Clause
要对 HQL 查询的结果进行排序,需要使用 ORDER BY 子句。可以按结果集中对象上的任何属性对结果进行升序 (ASC) 或降序 (DESC) 排序。以下是使用 ORDER BY 子句的简单语法:
String hql = "FROM Employee E WHERE E.id > 10 ORDER BY E.salary DESC";
Query query = session.createQuery(hql);
List results = query.list();
如果想根据多个属性排序,只需按如下所示将额外的属性以逗号分隔添加到 order by 子句的末尾:
String hql = "FROM Employee E WHERE E.id > 10 " +
"ORDER BY E.firstName DESC, E.salary DESC ";
Query query = session.createQuery(hql);
List results = query.list();
GROUP BY Clause
此子句允许 Hibernate 从数据库中提取信息,并根据属性值对信息进行分组,并且通常使用结果包含聚合值。以下是使用 GROUP BY 子句的简单语法:
String hql = "SELECT SUM(E.salary), E.firtName FROM Employee E " +
"GROUP BY E.firstName";
Query query = session.createQuery(hql);
List results = query.list();
Using Named Parameters
Hibernate 在其 HQL 查询中支持命名参数。这可以轻松编写接受用户输入的 HQL 查询,并且不必抵御 SQL 注入攻击。以下是使用命名参数的简单语法:
String hql = "FROM Employee E WHERE E.id = :employee_id";
Query query = session.createQuery(hql);
query.setParameter("employee_id",10);
List results = query.list();
UPDATE Clause
批量更新在 Hibernate 3 中是 HQL 的新增内容,并且 Hibernate 3 中的删除工作方式与 Hibernate 2 中不同。Query 界面对象现在包含一个名为 executeUpdate() 的方法,用于执行 HQL UPDATE 或 DELETE 语句。
UPDATE 子句可用于更新一个或多个对象的多个属性。以下是使用 UPDATE 子句的简单语法:
String hql = "UPDATE Employee set salary = :salary " +
"WHERE id = :employee_id";
Query query = session.createQuery(hql);
query.setParameter("salary", 1000);
query.setParameter("employee_id", 10);
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
DELETE Clause
DELETE 子句可用于删除一个或多个对象。以下是使用 DELETE 子句的简单语法:
String hql = "DELETE FROM Employee " +
"WHERE id = :employee_id";
Query query = session.createQuery(hql);
query.setParameter("employee_id", 10);
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
INSERT Clause
HQL 支持 INSERT INTO 子句,仅在可以将记录从一个对象插入到另一个对象时使用。以下是使用 INSERT INTO 子句的简单语法:
String hql = "INSERT INTO Employee(firstName, lastName, salary)" +
"SELECT firstName, lastName, salary FROM old_employee";
Query query = session.createQuery(hql);
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
Aggregate Methods
HQL 支持一系列聚合方法,与 SQL 类似。它们在 HQL 中的工作方式与在 SQL 中相同,以下是可用函数的列表:
Sr.No. |
Functions & Description |
1 |
avg(property name) 属性值的平均值 |
2 |
count(property name or *) 某个属性在结果中出现的次数 |
3 |
max(property name) 属性值的最大值 |
4 |
min(property name) 属性值的最小值 |
5 |
sum(property name) 属性值的总和 |
distinct 关键词仅对行集中唯一的值进行计数。以下查询将仅返回唯一计数 −
String hql = "SELECT count(distinct E.firstName) FROM Employee E";
Query query = session.createQuery(hql);
List results = query.list();
Pagination using Query
查询接口有两个方法用于分页。
Sr.No. |
Method & Description |
1 |
Query setFirstResult(int startPosition) 此方法采用一个整数来表示结果集中的第一行,从第 0 行开始。 |
2 |
Query setMaxResults(int maxResult) 此方法告诉 Hibernate 检索一个 maxResults 固定数量的对象。 |
将以上两个方法结合使用,我们可以在 Web 或 Swing 应用程序中构建一个分页组件。以下就是其示例,您可以对其进行扩展以一次获取 10 行 −
String hql = "FROM Employee";
Query query = session.createQuery(hql);
query.setFirstResult(1);
query.setMaxResults(10);
List results = query.list();
Hibernate - Criteria Queries
Hibernate 提供了处理对象以及反过来提供 RDBMS 表中数据的替代方法。其中一种方法是 Criteria API,它允许您以编程方式构建一个 criteria 查询对象,您可以在其中应用过滤规则和逻辑条件。
Hibernate Session 接口提供 createCriteria() 方法,它可用于创建 Criteria 对象,当您的应用程序执行 criteria 查询时,该对象会返回持久对象类的实例。
以下是 criteria 查询的最简单示例,它将简单地返回与 Employee 类对应的每个对象。
Criteria cr = session.createCriteria(Employee.class);
List results = cr.list();
Restrictions with Criteria
您可以使用 Criteria 对象提供的 add() 方法为 criteria 查询添加限制。以下是添加一个限制的示例,以返回工资等于 2000 的记录 −
Criteria cr = session.createCriteria(Employee.class);
cr.add(Restrictions.eq("salary", 2000));
List results = cr.list();
以下是涵盖不同场景的更多示例,可根据要求使用 −
Criteria cr = session.createCriteria(Employee.class);
// To get records having salary more than 2000
cr.add(Restrictions.gt("salary", 2000));
// To get records having salary less than 2000
cr.add(Restrictions.lt("salary", 2000));
// To get records having fistName starting with zara
cr.add(Restrictions.like("firstName", "zara%"));
// Case sensitive form of the above restriction.
cr.add(Restrictions.ilike("firstName", "zara%"));
// To get records having salary in between 1000 and 2000
cr.add(Restrictions.between("salary", 1000, 2000));
// To check if the given property is null
cr.add(Restrictions.isNull("salary"));
// To check if the given property is not null
cr.add(Restrictions.isNotNull("salary"));
// To check if the given property is empty
cr.add(Restrictions.isEmpty("salary"));
// To check if the given property is not empty
cr.add(Restrictions.isNotEmpty("salary"));
您可以使用 LogicalExpression 限制创建 AND 或 OR 条件,如下所示 −
Criteria cr = session.createCriteria(Employee.class);
Criterion salary = Restrictions.gt("salary", 2000);
Criterion name = Restrictions.ilike("firstNname","zara%");
// To get records matching with OR conditions
LogicalExpression orExp = Restrictions.or(salary, name);
cr.add( orExp );
// To get records matching with AND conditions
LogicalExpression andExp = Restrictions.and(salary, name);
cr.add( andExp );
List results = cr.list();
尽管上述所有条件都可以与 HQL 直接一起使用,如前一个教程中所述。
Pagination Using Criteria
查询接口有两个方法用于分页。
Sr.No. |
Method & Description |
1 |
public Criteria setFirstResult(int firstResult) 此方法采用一个整数来表示结果集中的第一行,从第 0 行开始。 |
2 |
public Criteria setMaxResults(int maxResults) 此方法告诉 Hibernate 检索一个 maxResults 固定数量的对象。 |
将以上两个方法结合使用,我们可以在 Web 或 Swing 应用程序中构建一个分页组件。以下就是其示例,您可以对其进行扩展以一次获取 10 行 −
Criteria cr = session.createCriteria(Employee.class);
cr.setFirstResult(1);
cr.setMaxResults(10);
List results = cr.list();
Sorting the Results
Criteria API 会提供 org.hibernate.criterion.Order 类按照对象的某个属性对结果集进行升序或降序排列。示例展示了如何使用 Order
类对结果集进行排序——
Criteria cr = session.createCriteria(Employee.class);
// To get records having salary more than 2000
cr.add(Restrictions.gt("salary", 2000));
// To sort records in descening order
cr.addOrder(Order.desc("salary"));
// To sort records in ascending order
cr.addOrder(Order.asc("salary"));
List results = cr.list();
Projections & Aggregations
Criteria API 会提供 org.hibernate.criterion.Projections 类,该类可以用于获取某个属性值的平均值、最大值或最小值。Projections
类类似于 Restrictions
类,因为它会提供几个用于获取 Projection 实例的静态工厂方法。
接下来是几个涉及不同场景的示例,并且能根据需求来使用——
Criteria cr = session.createCriteria(Employee.class);
// To get total row count.
cr.setProjection(Projections.rowCount());
// To get average of a property.
cr.setProjection(Projections.avg("salary"));
// To get distinct count of a property.
cr.setProjection(Projections.countDistinct("firstName"));
// To get maximum of a property.
cr.setProjection(Projections.max("salary"));
// To get minimum of a property.
cr.setProjection(Projections.min("salary"));
// To get sum of a property.
cr.setProjection(Projections.sum("salary"));
Criteria Queries Example
考虑以下 POJO 类——
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
我们创建下面的 EMPLOYEE 表以存储 Employee 对象——
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
下面是映射文件。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
最后,我们通过 main()
方法创建我们的应用程序类以运行应用程序,在其中我们将使用 Criteria 查询——
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Projections;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 2000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 5000);
Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000);
/* List down all the employees */
ME.listEmployees();
/* Print Total employee's count */
ME.countEmployee();
/* Print Total salary */
ME.totalSalary();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees having salary more than 2000 */
public void listEmployees( ) {
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(Employee.class);
// Add restriction.
cr.add(Restrictions.gt("salary", 2000));
List employees = cr.list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to print total number of records */
public void countEmployee(){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(Employee.class);
// To get total row count.
cr.setProjection(Projections.rowCount());
List rowCount = cr.list();
System.out.println("Total Coint: " + rowCount.get(0) );
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to print sum of salaries */
public void totalSalary(){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(Employee.class);
// To get total salary.
cr.setProjection(Projections.sum("salary"));
List totalSalary = cr.list();
System.out.println("Total Salary: " + totalSalary.get(0) );
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
-
如配置章节中所述,创建 hibernate.cfg.xml 配置文件。
-
如上所示创建 Employee.hbm.xml 映射文件。
-
创建 Employee.java 源文件(如上所示),并进行编译。
-
按上方代码创建一个 ManageEmployee.java 源文件并进行编译。
-
执行 ManageEmployee 二进制文件来运行该程序。
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........
First Name: Daisy Last Name: Das Salary: 5000
First Name: John Last Name: Paul Salary: 5000
First Name: Mohd Last Name: Yasee Salary: 3000
Total Coint: 4
Total Salary: 15000
如果你检查你的 EMPLOYEE 表,它应当有以下记录——
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+
| id | first_name | last_name | salary |
+----+------------+-----------+--------+
| 14 | Zara | Ali | 2000 |
| 15 | Daisy | Das | 5000 |
| 16 | John | Paul | 5000 |
| 17 | Mohd | Yasee | 3000 |
+----+------------+-----------+--------+
4 rows in set (0.00 sec)
mysql>
Hibernate - Native SQL
如果你希望利用数据库的特定功能(例如 Oracle 中的查询提示或 CONNECT 关键字),你可以使用原生 SQL 来表达数据库查询。Hibernate 3.x 让你可以指定手工编写的 SQL(包括存储过程)用于所有创建、更新、删除和加载操作。
你的应用程序会利用 Session 接口的 createSQLQuery() 方法从会话中创建一个原生 SQL 查询——
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
在你向 createSQLQuery()
方法传递包含 SQL 查询的字符串之后,你可以使用 addEntity()
、addJoin()
和 addScalar()
方法分别将 SQL 结果与现有的 Hibernate 实体、联接或标量结果关联起来。
Scalar Queries
最基本的 SQL 查询是从一张或多张表中获取标量(值)的列表。以下是使用原生 SQL 查询标量值的语法——
String sql = "SELECT first_name, salary FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List results = query.list();
Entity Queries
以上所有查询都涉及返回标量值,基本上从结果集中返回“原始”值。以下是使用 addEntity()
从原生 SQL 查询中整体获取实体对象的语法。
String sql = "SELECT * FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
List results = query.list();
Named SQL Queries
以下是使用 addEntity()
和命名 SQL 查询从原生 SQL 查询中获取实体对象的语法。
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
query.setParameter("employee_id", 10);
List results = query.list();
Native SQL Example
考虑以下 POJO 类——
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
我们创建下面的 EMPLOYEE 表以存储 Employee 对象——
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
以下是映射文件——
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
最后,我们通过 main()
方法创建我们的应用程序类以运行应用程序,在其中我们将使用 Native SQL 查询——
import java.util.*;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.SQLQuery;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 2000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 5000);
Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000);
/* List down employees and their salary using Scalar Query */
ME.listEmployeesScalar();
/* List down complete employees information using Entity Query */
ME.listEmployeesEntity();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees using Scalar Query */
public void listEmployeesScalar( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
String sql = "SELECT first_name, salary FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List data = query.list();
for(Object object : data) {
Map row = (Map)object;
System.out.print("First Name: " + row.get("first_name"));
System.out.println(", Salary: " + row.get("salary"));
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to READ all the employees using Entity Query */
public void listEmployeesEntity( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
String sql = "SELECT * FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
List employees = query.list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
-
如配置章节中所述,创建 hibernate.cfg.xml 配置文件。
-
如上所示创建 Employee.hbm.xml 映射文件。
-
创建 Employee.java 源文件(如上所示),并进行编译。
-
按上方代码创建一个 ManageEmployee.java 源文件并进行编译。
-
执行 ManageEmployee 二进制文件来运行该程序。
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........
First Name: Zara, Salary: 2000
First Name: Daisy, Salary: 5000
First Name: John, Salary: 5000
First Name: Mohd, Salary: 3000
First Name: Zara Last Name: Ali Salary: 2000
First Name: Daisy Last Name: Das Salary: 5000
First Name: John Last Name: Paul Salary: 5000
First Name: Mohd Last Name: Yasee Salary: 3000
如果检查您的 EMPLOYEE 表,它应包含以下记录 −
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+
| id | first_name | last_name | salary |
+----+------------+-----------+--------+
| 26 | Zara | Ali | 2000 |
| 27 | Daisy | Das | 5000 |
| 28 | John | Paul | 5000 |
| 29 | Mohd | Yasee | 3000 |
+----+------------+-----------+--------+
4 rows in set (0.00 sec)
mysql>
Hibernate - Caching
高速缓存是一种增强系统性能的机制。它是一种位于应用程序和数据库之间的缓冲存储器。高速缓存存储最近使用过的数据项,尽可能减少数据库命中次数。
高速缓存对 Hibernate 也很重要。它利用了如下解释的多级高速缓存方案——
First-level Cache
一级高速缓存即 Session 高速缓存,这是一个所有请求都必须经过的强制高速缓存。Session 对象在将对象提交到数据库之前,在自己的能力范围内保留该对象。
如果对对象发出多次更新,Hibernate 尝试尽可能长时间延迟更新,以减少发出的更新 SQL 语句的次数。如果关闭 Session,所有被缓存的对象都会丢失并且被保留或更新到数据库中。
Second-level Cache
二级高速缓存是一个可选的高速缓存,而且在尝试定位二级高速缓存中的对象之前,通常会咨询一级高速缓存。二级高速缓存可以在每个类和每个集合的基础上配置,并且主要负责跨 Session 缓存对象。
Hibernate 可以使用任何第三方缓存。提供了一个 org.hibernate.cache.CacheProvider 接口,必须实现该接口才能为 Hibernate 提供一个对缓存实现的句柄。
Query-level Cache
Hibernate 还为查询结果集实现了与二级缓存紧密集成在一起的缓存。
这是一个可选特性,需要两个附加的物理缓存区域来保存缓存的查询结果以及表上次更新的时间戳。这仅适用于频繁使用相同参数运行的查询。
The Second Level Cache
Hibernate 默认使用一级缓存,并且您无需执行任何操作即可使用一级缓存。我们直接进入可选的二级缓存。并不是所有类都受益于缓存,因此能够禁用二级缓存非常重要。
Hibernate 二级缓存分两步设置。首先,您必须决定使用哪种并发策略。然后,使用缓存提供程序配置缓存到期时间和物理缓存属性。
Concurrency Strategies
并发策略是一个调节器,它负责将数据项存储在缓存中并从缓存中检索数据项。如果您要启用二级缓存,则需要为每个持久类和集合决定使用哪种缓存并发性策略。
-
Transactional − 在并发事务中,对于更新这种情况不太可能发生时,防止陈旧数据至关重要的几乎只读数据,请使用此策略。
-
Read-write − 在并发事务中,对于更新这种情况不太可能发生时,防止陈旧数据至关重要的几乎只读数据,请再次使用此策略。
-
Nonstrict-read-write − 此策略不保证缓存和数据库之间的一致性。如果数据几乎从不更改,并且陈旧数据的可能很小,请使用此策略,在这种情况下,陈旧数据不是严重的问题。
-
Read-only − 适用于永不更改的数据的并发策略。仅将其用于引用数据。
如果我们准备为 Employee 类使用二级缓存,那么我们添加必需的映射元素来告诉 Hibernate 使用读写策略来缓存 Employee 实例。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<cache usage = "read-write"/>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
usage="read-write" 属性告诉 Hibernate 为定义的缓存使用读写并发策略。
Cache Provider
在考虑了并发策略后,您的下一步是用您的缓存候选类来选择缓存提供程序。Hibernate 强制您为整个应用程序选择单一缓存提供程序。
Sr.No. |
Cache Name & Description |
1 |
EHCache 它可以在内存中或磁盘上进行缓存,支持集群缓存,并支持可选的 Hibernate 查询结果缓存。 |
2 |
OSCache 支持在单个 JVM 中将数据缓存到内存和磁盘,并提供丰富的到期策略和查询缓存支持。 |
3 |
warmCache 基于 JGroups 的群集缓存。它使用集群失效,但不支持 Hibernate 查询缓存。 |
4 |
JBoss Cache 一个完全事务性复制的群集缓存,它也基于 JGroups 多播库。它支持复制或失效、同步或异步通信以及乐观和悲观锁定。支持 Hibernate 查询缓存。 |
并非每个缓存提供程序都与每个并发策略兼容。以下兼容性矩阵将帮助您选择合适的组合。
Strategy/Provider |
Read-only |
Nonstrictread-write |
Read-write |
Transactional |
EHCache |
X |
X |
X |
|
OSCache |
X |
X |
X |
|
SwarmCache |
X |
X |
||
JBoss Cache |
X |
X |
您将在 hibernate.cfg.xml 配置文件中指定一个缓存提供程序。我们选择 EHCache 作为我们的二级缓存提供程序 −
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<property name = "hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
现在,你需要指定缓存区域的属性。EHCache 有自己的配置文件, ehcache.xml ,它应该在应用程序的类路径中。Employee 类的 ehcache.xml 中的缓存配置可能如下所示:
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory = "1000"
eternal = "false"
timeToIdleSeconds = "120"
timeToLiveSeconds = "120"
overflowToDisk = "true"
/>
<cache name = "Employee"
maxElementsInMemory = "500"
eternal = "true"
timeToIdleSeconds = "0"
timeToLiveSeconds = "0"
overflowToDisk = "false"
/>
就是这样,现在我们已为 Employee 类启用二级缓存,而且 Hibernate,现在每当你导航到一个 Employee 或通过标识符加载一个 Employee 时,都会命中二级缓存。
你应分析你的所有类,并为每个类选择适当的缓存策略。有时,二级缓存可能会降低应用程序的性能。因此,建议先对你的应用程序进行基准测试,在未启用缓存的情况下进行,然后启用适合你的缓存,并检查性能。如果缓存未提高系统性能,则无需启用任何类型的缓存。
The Query-level Cache
要使用查询缓存,你必须首先使用配置文件中的 hibernate.cache.use_query_cache="true" 属性激活它。通过将此属性设置为 true,使 Hibernate 在内存中创建必需的缓存以保存查询和标识符集。
然后,要使用查询缓存,使用 Query 类的 setCacheable(Boolean) 方法。例如:
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
List users = query.list();
SessionFactory.closeSession();
Hibernate 还通过缓存区域的概念支持非常细粒度的缓存支持。缓存区域是缓存的一部分,它被赋予了一个名称。
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();
此代码使用该方法告知 Hibernate 将查询存储在缓存的 employee 区域中并查找该查询。
Hibernate - Batch Processing
设想一下这种情况:你需要使用 Hibernate 上传大量记录到你的数据库。以下是使用 Hibernate 实现此目标的代码段:
Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee employee = new Employee(.....);
session.save(employee);
}
tx.commit();
session.close();
默认情况下,Hibernate 会将所有持久化对象缓存在会话级缓存中,最终你的应用程序会在大约第 50,000 行时因 OutOfMemoryException 而崩溃。如果你将 batch processing 与 Hibernate 一起使用,则可以解决此问题。
要使用批处理功能,首先将 hibernate.jdbc.batch_size 设置为批量大小,以 20 或 50 的数字作为对象大小。这将告知 Hibernate 容器将每 X 行插入为一批。要在你的代码中实现此功能,我们需要进行如下小修改:
Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee employee = new Employee(.....);
session.save(employee);
if( i % 50 == 0 ) { // Same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
上面的代码适用于 INSERT 操作,但如果你愿意执行 UPDATE 操作,则可以使用以下代码来实现:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE").scroll();
int count = 0;
while ( employeeCursor.next() ) {
Employee employee = (Employee) employeeCursor.get(0);
employee.updateEmployee();
seession.update(employee);
if ( ++count % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Batch Processing Example
让我们修改配置文件以添加 hibernate.jdbc.batch_size 属性:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<property name = "hibernate.jdbc.batch_size">
50
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
考虑以下 POJO Employee 类:
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
让我们创建以下 EMPLOYEE 表来存储 Employee 对象:
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
以下是将 Employee 对象与 EMPLOYEE 表映射的映射文件:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
最后,我们将创建带有 main() 方法的应用程序类来运行应用程序,在应用程序中,我们将使用 Session 对象附带的 flush() 和 clear() 方法,以便 Hibernate 继续将这些记录写入数据库,而不是将它们缓存在内存中。
import java.util.*;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add employee records in batches */
ME.addEmployees( );
}
/* Method to create employee records in batches */
public void addEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
String fname = "First Name " + i;
String lname = "Last Name " + i;
Integer salary = i;
Employee employee = new Employee(fname, lname, salary);
session.save(employee);
if( i % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return ;
}
}
Hibernate - Interceptors
您已经了解到,在 Hibernate 中,将创建一个对象并将其持久化。一旦对象发生更改,就必须将其保存回数据库。该过程会持续到下次需要该对象为止,该对象将从持久化储存中加载。
对象在其生命周期中会经过不同的阶段,而 Interceptor Interface 提供可于不同阶段调用以执行一些所需任务的方法。这些方法是会话到应用程序的回调,它允许应用程序在持久对象被保存、更新、删除或加载之前检查和/或操作其属性。以下列出拦截器接口中提供的所有方法 −
Sr.No. |
Method & Description |
1 |
findDirty() 当对会话对象调用 flush() 方法时,将调用此方法。 |
2 |
instantiate() 当实例化持久化类时,将调用此方法。 |
3 |
isUnsaved() 当将对象传递给 saveOrUpdate() 方法时,将调用此方法。 |
4 |
onDelete() 当删除对象之前,将调用此方法。 |
5 |
onFlushDirty() 当 Hibernate 检测到对象在刷新期间(即更新操作)变为脏(即发生更改)时,将调用此方法。 |
6 |
onLoad() 当初始化对象之前,将调用此方法。 |
7 |
onSave() 当保存对象之前,将调用此方法。 |
8 |
postFlush() 当刷新已经发生并且对象已经在内存中更新后,将调用此方法。 |
9 |
preFlush() 当刷新之前,将调用此方法。 |
Hibernate Interceptor 让我们全面控制对象在应用程序和数据库中的呈现方式。
How to Use Interceptors?
要构建拦截器,你可以直接实现 Interceptor 类或扩展 EmptyInterceptor 类。以下是使用 Hibernate Interceptor 功能的简单步骤。
Create Interceptors
在我们的示例中,我们将扩展 EmptyInterceptor,在 Employee 对象被创建和更新时,Interceptor 的方法将自动调用。你可以根据要求实现更多方法。
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class MyInterceptor extends EmptyInterceptor {
private int updates;
private int creates;
private int loads;
public void onDelete(Object entity, Serializable id,
Object[] state, String[] propertyNames, Type[] types) {
// do nothing
}
// This method is called when Employee object gets updated.
public boolean onFlushDirty(Object entity, Serializable id,
Object[] currentState, Object[] previousState, String[] propertyNames,
Type[] types) {
if ( entity instanceof Employee ) {
System.out.println("Update Operation");
return true;
}
return false;
}
public boolean onLoad(Object entity, Serializable id,
Object[] state, String[] propertyNames, Type[] types) {
// do nothing
return true;
}
// This method is called when Employee object gets created.
public boolean onSave(Object entity, Serializable id,
Object[] state, String[] propertyNames, Type[] types) {
if ( entity instanceof Employee ) {
System.out.println("Create Operation");
return true;
}
return false;
}
//called before commit into database
public void preFlush(Iterator iterator) {
System.out.println("preFlush");
}
//called after committed into database
public void postFlush(Iterator iterator) {
System.out.println("postFlush");
}
}
Create POJO Classes
现在,让我们稍微修改第一个示例,在示例中我们将使用 EMPLOYEE 表和 Employee 类 −
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Create Database Tables
第二步是在你的数据库中创建表。将有一个表对应一个对象,你愿意提供持久性。考虑上面解释的对象,需要存储并检索到以下 RDBMS 表中 −
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
Create Mapping Configuration File
此步骤是创建映射文件,以指导 Hibernate — 如何将定义的类或类映射到数据库表。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
Create Application Class
最后,我们将创建具有 main() 方法的应用程序类来运行应用程序。此处应该注意的是,在创建会话对象时,我们使用了拦截器类作为参数。
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
/* List down all the employees */
ME.listEmployees();
/* Update employee's records */
ME.updateEmployee(empID1, 5000);
/* Delete an employee from the database */
ME.deleteEmployee(empID2);
/* List down new list of the employees */
ME.listEmployees();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees */
public void listEmployees( ){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
try {
tx = session.beginTransaction();
List employees = session.createQuery("FROM Employee").list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to UPDATE salary for an employee */
public void updateEmployee(Integer EmployeeID, int salary ){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
employee.setSalary( salary );
session.update(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to DELETE an employee from the records */
public void deleteEmployee(Integer EmployeeID){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
session.delete(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
-
如配置章节中所述,创建 hibernate.cfg.xml 配置文件。
-
如上所示创建 Employee.hbm.xml 映射文件。
-
创建 Employee.java 源文件(如上所示),并进行编译。
-
创建 MyInterceptor.java 源文件(如上所示)并编译它。
-
按上方代码创建一个 ManageEmployee.java 源文件并进行编译。
-
执行 ManageEmployee 二进制文件来运行该程序。
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
$java ManageEmployee
.......VARIOUS LOG MESSAGES WILL DISPLAY HERE........
Create Operation
preFlush
postFlush
Create Operation
preFlush
postFlush
Create Operation
preFlush
postFlush
First Name: Zara Last Name: Ali Salary: 1000
First Name: Daisy Last Name: Das Salary: 5000
First Name: John Last Name: Paul Salary: 10000
preFlush
postFlush
preFlush
Update Operation
postFlush
preFlush
postFlush
First Name: Zara Last Name: Ali Salary: 5000
First Name: John Last Name: Paul Salary: 10000
preFlush
postFlush
如果检查您的 EMPLOYEE 表,它应包含以下记录 −
mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+
| id | first_name | last_name | salary |
+----+------------+-----------+--------+
| 29 | Zara | Ali | 5000 |
| 31 | John | Paul | 10000 |
+----+------------+-----------+--------+
2 rows in set (0.00 sec
mysql>