Hibernate - 快速指南
Hibernate - ORM Overview
什么是JDBC?
JDBC代表Java Database Connectivity 。 它提供了一组Java API,用于从Java程序访问关系数据库。 这些Java API使Java程序能够执行SQL语句并与任何SQL兼容的数据库进行交互。
JDBC提供了一种灵活的体系结构来编写独立于数据库的应用程序,该应用程序可以在不同的平台上运行,并且可以在不进行任
JDBC的优点和缺点
JDBC的优点 | JDBC的缺点 |
---|---|
干净简单的SQL处理 大数据的良好性能 非常适合小型应用 简单的语法很容易学习 | 如果它用于大型项目,则很复杂 大量的编程开销 没有封装 很难实现MVC概念 查询是特定于DBMS的 |
为什么选择对象关系映射(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. | 不匹配和描述 |
---|---|
1 | Granularity 有时您将拥有一个对象模型,该类型的数量多于数据库中相应表的数量。 |
2 | Inheritance RDBMS没有定义类似于继承的任何东西,这是面向对象编程语言中的自然范例。 |
3 | Identity RDBMS恰好定义了“同一性”的一个概念:主键。 但是,Java定义了对象标识(a == b)和对象相等(a.equals(b))。 |
4 | Associations 面向对象语言表示使用对象引用的关联,而RDBMS表示关联作为外键列。 |
5 | Navigation 您在Java和RDBMS中访问对象的方式根本不同。 |
O bject- R elational M apping(ORM)是处理所有上述阻抗不匹配的解决方案。
什么是ORM?
ORM代表O bject- R elational M apping(ORM)是一种编程技术,用于在关系数据库和面向对象的编程语言(如Java,C#等)之间转换数据。
与普通JDBC相比,ORM系统具有以下优势 -
Sr.No. | 好处 |
---|---|
1 | 让业务代码访问对象而不是数据库表。 |
2 | 隐藏来自OO逻辑的SQL查询的详细信息。 |
3 | 基于JDBC'引擎盖'。 |
4 | 无需处理数据库实现。 |
5 | 实体基于业务概念而非数据库结构。 |
6 | 事务管理和自动密钥生成。 |
7 | 应用程序的快速发展。 |
ORM解决方案由以下四个实体组成 -
Sr.No. | 解决方案 |
---|---|
1 | 用于对持久化类的对象执行基本CRUD操作的API。 |
2 | 用于指定引用类的类和属性的查询的语言或API。 |
3 | 用于指定映射元数据的可配置工具。 |
4 | 一种与事务对象交互以执行脏检查,延迟关联提取和其他优化功能的技术。 |
Java ORM框架
Java中有几个持久性框架和ORM选项。 持久框架是一种ORM服务,用于将对象存储和检索到关系数据库中。
- Enterprise JavaBeans Entity Beans
- Java数据对象
- Castor
- TopLink
- SpringDAO
- Hibernate
- And many more
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的优点
Hibernate负责使用XML文件将Java类映射到数据库表,而无需编写任何代码行。
提供简单的API,用于直接在数据库中存储和检索Java对象。
如果数据库或任何表中存在更改,则只需更改XML文件属性。
抽象出不熟悉的SQL类型,并提供一种解决熟悉的Java对象的方法。
Hibernate不需要应用程序服务器来运行。
操纵数据库对象的复杂关联。
使用智能提取策略最大限度地减少数据库访问。
提供简单的数据查询。
支持的数据库
Hibernate几乎支持所有主要的RDBMS。 以下是Hibernate支持的一些数据库引擎列表 -
- HSQL数据库引擎
- DB2/NT
- MySQL
- PostgreSQL
- FrontBase
- Oracle
- Microsoft SQL Server数据库
- Sybase SQL Server
- Informix Dynamic Server
支持的技术
Hibernate支持各种其他技术,包括 -
- XDoclet Spring
- J2EE
- Eclipse插件
- Maven
Hibernate - Architecture
Hibernate具有分层架构,可帮助用户在不必了解底层API的情况下进行操作。 Hibernate利用数据库和配置数据为应用程序提供持久性服务(和持久对象)。
以下是Hibernate应用程序架构的高级视图。
以下是Hibernate Application Architecture及其重要核心类的详细视图。
Hibernate使用各种现有的Java API,如JDBC,Java Transaction API(JTA)和Java命名和目录接口(JNDI)。 JDBC提供了对关系数据库通用功能的基本抽象级别,允许Hibernate支持几乎所有带有JDBC驱动程序的数据库。 JNDI和JTA允许Hibernate与J2EE应用程序服务器集成。
以下部分简要介绍了Hibernate Application Architecture中涉及的每个类对象。
配置对象
Configuration对象是您在任何Hibernate应用程序中创建的第一个Hibernate对象。 它通常在应用程序初始化期间仅创建一次。 它表示Hibernate所需的配置或属性文件。
Configuration对象提供了两个键组件 -
Database Connection - 这是通过Hibernate支持的一个或多个配置文件来处理的。 这些文件是hibernate.properties和hibernate.cfg.xml 。
Class Mapping Setup - 该组件创建Java类和数据库表之间的连接。
SessionFactory对象
配置对象用于创建SessionFactory对象,该对象使用提供的配置文件为应用程序配置Hibernate,并允许实例化Session对象。 SessionFactory是一个线程安全对象,由应用程序的所有线程使用。
SessionFactory是一个重量级的对象; 它通常在应用程序启动期间创建并保留供以后使用。 每个数据库使用一个单独的配置文件需要一个SessionFactory对象。 因此,如果您使用多个数据库,则必须创建多个SessionFactory对象。
会话对象
会话用于获取与数据库的物理连接。 Session对象是轻量级的,旨在每次与数据库进行交互时进行实例化。 持久对象通过Session对象保存和检索。
会话对象不应该长时间保持打开状态,因为它们通常不是线程安全的,应该根据需要创建和销毁它们。
交易对象
事务表示数据库的工作单元,大多数RDBMS支持事务功能。 Hibernate中的事务由底层事务管理器和事务(来自JDBC或JTA)处理。
这是一个可选对象,Hibernate应用程序可以选择不使用此接口,而是在自己的应用程序代码中管理事务。
查询对象
查询对象使用SQL或Hibernate查询语言(HQL)字符串从数据库中检索数据并创建对象。 Query实例用于绑定查询参数,限制查询返回的结果数,最后执行查询。
标准对象
Criteria对象用于创建和执行面向对象的条件查询以检索对象。
Hibernate - Environment
本章介绍如何安装Hibernate和其他相关软件包以为Hibernate应用程序准备环境。 我们将使用MySQL数据库来试验Hibernate示例,因此请确保您已经拥有了MySQL数据库的设置。 有关MySQL的更多详细信息,您可以查看我们的MySQL教程 。
正在下载Hibernate
假设您已在系统上安装了最新版本的Java。 以下是在您的系统上下载和安装Hibernate的简单步骤 -
选择是否要在Windows或Unix上安装Hibernate,然后继续下一步下载Windows的.zip文件和Unix的.tz文件。
从http://www.hibernate.org/downloads下载最新版本的Hibernate。
在编写本教程时,我下载了hibernate-distribution3.6.4.Final ,当您解压缩下载的文件时,它将为您提供目录结构,如下图所示
安装Hibernate
下载并解压缩最新版本的Hibernate安装文件后,需要执行以下两个简单步骤。 确保正确设置CLASSPATH变量,否则在编译应用程序时将遇到问题。
现在,将/lib所有库文件复制到CLASSPATH中,并更改classpath变量以包含所有JAR -
最后,将hibernate3.jar文件复制到CLASSPATH中。 该文件位于安装的根目录中,是Hibernate完成其工作所需的主要JAR。
Hibernate先决条件
以下是Hibernate所需的软件包/库列表,您应该在开始使用Hibernate之前安装它们。 要安装这些软件包,您必须将/lib库文件复制到CLASSPATH中,并相应地更改CLASSPATH变量。
Sr.No. | 包/库 |
---|---|
1 | dom4j XML解析www.dom4j.org/ |
2 | Xalan |
3 | Xerces Xerces Java Parser https://xml.apache.org/xerces-j/ |
4 | cglib 运行时对Java类的适当更改http://cglib.sourceforge.net/ |
5 | log4j 记录Faremwork https://logging.apache.org/log4j |
6 | Commons |
7 | SLF4J Logging Facade for Java https://www.slf4j.org |
Hibernate - Configuration
Hibernate需要提前知道 - 在哪里可以找到定义Java类与数据库表相关的映射信息。 Hibernate还需要一组与数据库和其他相关参数相关的配置设置。 所有这些信息通常作为标准的Java属性文件hibernate.properties ,或者作为名为hibernate.cfg.xml的XML文件提供。
我将考虑XML格式的文件hibernate.cfg.xml来指定我的示例中所需的Hibernate属性。 大多数属性采用其默认值,除非确实需要,否则不需要在属性文件中指定它们。 此文件保存在应用程序类路径的根目录中。
Hibernate属性
以下是重要属性的列表,您将需要在独立情况下配置数据库 -
Sr.No. | 属性和描述 |
---|---|
1 | hibernate.dialect 此属性使Hibernate为所选数据库生成适当的SQL。 |
2 | hibernate.connection.driver_class JDBC驱动程序类。 |
3 | hibernate.connection.url 数据库实例的JDBC URL。 |
4 | hibernate.connection.username 数据库用户名。 |
5 | hibernate.connection.password 数据库密码。 |
6 | hibernate.connection.pool_size 限制在Hibernate数据库连接池中等待的连接数。 |
7 | hibernate.connection.autocommit 允许自动提交模式用于JDBC连接。 |
如果您正在使用数据库以及应用程序服务器和JNDI,那么您必须配置以下属性 -
Sr.No. | 属性和描述 |
---|---|
1 | hibernate.connection.datasource 在应用程序服务器上下文中定义的JNDI名称,用于应用程序。 |
2 | hibernate.jndi.class JNDI的InitialContext类。 |
3 | hibernate.jndi.《JNDIpropertyname》 将您喜欢的任何JNDI属性传递给JNDI InitialContext 。 |
4 | hibernate.jndi.url 提供JNDI的URL。 |
5 | hibernate.connection.username 数据库用户名。 |
6 | hibernate.connection.password 数据库密码。 |
使用MySQL数据库进行Hibernate
MySQL是当今最流行的开源数据库系统之一。 让我们创建hibernate.cfg.xml配置文件并将其放在应用程序类路径的根目录中。 您必须确保在MySQL数据库中有可用的testdb数据库,并且您可以使用用户test来访问数据库。
XML配置文件必须符合Hibernate 3 Configuration DTD,可在http://www.hibernate.org/dtd/hibernate-configuration-3.0.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>
上面的配置文件包含与hibernatemapping文件相关的《mapping》标签,我们将在下一章中看到hibernate映射文件究竟是什么以及我们如何以及为何使用它?
以下是各种重要数据库方言属性类型的列表 -
Sr.No. | 数据库和方言属性 |
---|---|
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
会话用于获取与数据库的物理连接。 Session对象是轻量级的,旨在每次与数据库进行交互时进行实例化。 持久对象通过Session对象保存和检索。
会话对象不应该长时间保持打开状态,因为它们通常不是线程安全的,应该根据需要创建和销毁它们。 Session的主要功能是为映射实体类的实例提供,创建,读取和删除操作。
在给定时间点,实例可能存在以下三种状态之一 -
transient - 持久化类的新实例,它与Session无关,在数据库中没有任何表示,Hibernate认为没有标识符值是瞬态的。
persistent - 您可以通过将瞬态实例与Session相关联来使其持久化。 持久化实例在数据库中具有表示,标识符值并与会话相关联。
detached - 一旦我们关闭Hibernate Session,持久化实例将成为一个分离的实例。
如果Session实例的持久化类是可序列化的,则它是可序列化的。 典型的交易应使用以下习语 -
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接口提供了许多方法,但我将仅列出一些重要的方法,我们将在本教程中使用它们。 您可以查看Hibernate文档以获取与Session和SessionFactory相关的方法的完整列表。
Sr.No. | 会话方法和描述 |
---|---|
1 | Transaction beginTransaction() 开始一个工作单元并返回关联的Transaction对象。 |
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() 获取与此会话关联的Transaction实例。 |
17 | boolean isConnected() 检查会话当前是否已连接。 |
18 | boolean isDirty() 此会话是否包含必须与数据库同步的任何更改? |
19 | boolean isOpen() 检查会话是否仍处于打开状态。 |
20 | Serializable save(Object object) 保留给定的瞬态实例,首先分配生成的标识符。 |
21 | void saveOrUpdate(Object object) 保存(Object)或更新(Object)给定实例。 |
22 | void update(Object object) 使用给定分离实例的标识符更新持久性实例。 |
23 | void update(String entityName, Object object) 使用给定分离实例的标识符更新持久性实例。 |
Hibernate - Persistent Class
Hibernate的整个概念是从Java类属性中获取值并将它们保存到数据库表中。 映射文档帮助Hibernate确定如何从类中提取值并使用表和关联字段映射它们。
其对象或实例将存储在数据库表中的Java类在Hibernate中称为持久化类。 如果这些类遵循一些简单的规则(也称为Plain Old Java Object (POJO)编程模型),Hibernate的效果最佳。
持久化类有以下主要规则,但是,这些规则都不是硬性要求 -
将持久化的所有Java类都需要默认构造函数。
所有类都应包含一个ID,以便在Hibernate和数据库中轻松识别您的对象。 此属性映射到数据库表的主键列。
将持久化的所有属性都应声明为private,并且在JavaBean样式中定义了getXXX和setXXX方法。
Hibernate的一个核心功能是代理,它依赖于持久化的类是非final的,或者是声明所有公共方法的接口的实现。
所有不扩展或实现EJB框架所需的专用类和接口的类。
POJO名称用于强调给定对象是普通Java对象,而不是特殊对象,特别是不是Enterprise JavaBean。
简单的POJO示例
根据上面提到的几条规则,我们可以按如下方式定义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>
您应该将映射文档保存在格式为
让我们看一下有关映射文件中使用的映射元素的一些细节 -
映射文档是具有《hibernate-mapping》作为根元素的XML文档,其包含所有《class》元素。
《class》元素用于定义从Java类到数据库表的特定映射。 使用class元素的name属性指定Java类名,并使用table属性指定数据库table名。
《meta》元素是可选元素,可用于创建类描述。
《id》元素将类中的唯一ID属性映射到数据库表的主键。 id元素的name属性引用类中的属性, column属性引用数据库表中的列。 type属性包含hibernate映射类型,这种映射类型将从Java转换为SQL数据类型。
id元素中的《generator》元素用于自动生成主键值。 生成器元素的class属性设置为native ,让hibernate选择identity, sequence或hilo算法来创建主键,具体取决于底层数据库的功能。
《property》元素用于将Java类属性映射到数据库表中的列。 元素的name属性引用类中的属性, column属性引用数据库表中的列。 type属性包含hibernate映射类型,这种映射类型将从Java转换为SQL数据类型。
还有其他可用的属性和元素,这些属性和元素将用于映射文档中,我将尝试在讨论其他Hibernate相关主题时尽可能多地介绍它们。
Hibernate - Mapping Types
准备Hibernate映射文档时,您会发现将Java数据类型映射到RDBMS数据类型。 在映射文件中声明和使用的types不是Java数据类型; 它们也不是SQL数据库类型。 这些类型称为Hibernate mapping types ,可以从Java转换为SQL数据类型,反之亦然。
本章列出了所有基本,日期和时间,大对象以及各种其他内置映射类型。
原始类型
映射类型 | Java类型 | ANSI SQL类型 |
---|---|---|
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') |
日期和时间类型
映射类型 | Java类型 | ANSI SQL类型 |
---|---|---|
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 |
二进制和大对象类型
映射类型 | Java类型 | ANSI SQL类型 |
---|---|---|
binary | byte[] | VARBINARY (or BLOB) |
text | java.lang.String | CLOB |
serializable | 任何实现java.io.Serializable的Java类 | VARBINARY (or BLOB) |
clob | java.sql.Clob | CLOB |
blob | java.sql.Blob | BLOB |
JDK-related Types
映射类型 | Java类型 | ANSI SQL类型 |
---|---|---|
class | java.lang.Class | VARCHAR |
locale | java.util.Locale | VARCHAR |
timezone | java.util.TimeZone | VARCHAR |
currency | java.util.Currency | VARCHAR |
Hibernate - Examples
现在让我们举个例子来了解我们如何使用Hibernate在独立应用程序中提供Java持久性。 我们将介绍使用Hibernate技术创建Java应用程序所涉及的不同步骤。
创建POJO类
创建应用程序的第一步是构建Java POJO类或类,具体取决于将持久保存到数据库的应用程序。 让我们考虑使用getXXX和setXXX方法的Employee类,使其成为JavaBeans兼容类。
POJO(Plain Old Java Object)是一个Java对象,它不扩展或实现EJB框架分别需要的某些专用类和接口。 所有普通的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;
}
}
创建数据库表
第二步是在数据库中创建表。 每个对象都会有一个表,您愿意提供持久性。 考虑上面的对象需要存储和检索到以下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>
您应该将映射文档保存在格式为
映射文档是一个XML文档,其中
作为包含所有 元素的根元素。 《class》元素用于定义从Java类到数据库表的特定映射。 使用class元素的name属性指定Java类名,并使用table属性指定数据库表名。
《meta》元素是可选元素,可用于创建类描述。
《id》元素将类中的唯一ID属性映射到数据库表的主键。 id元素的name属性引用类中的属性, column属性引用数据库表中的列。 type属性包含hibernate映射类型,这种映射类型将从Java转换为SQL数据类型。
id元素中的《generator》元素用于自动生成主键值。 生成器元素的class属性设置为native ,让hibernate选择identity, sequence或hilo算法来创建主键,具体取决于底层数据库的功能。
《property》元素用于将Java类属性映射到数据库表中的列。 元素的name属性引用类中的属性, column属性引用数据库表中的列。 type属性包含hibernate映射类型,这种映射类型将从Java转换为SQL数据类型。
还有其他可用的属性和元素,这些属性和元素将用于映射文档中,我将尝试在讨论其他Hibernate相关主题时尽可能多地介绍它们。
创建应用程序类 (Create Application Class)
最后,我们将使用main()方法创建应用程序类来运行应用程序。 我们将使用此应用程序来保存很少的员工记录,然后我们将对这些记录应用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.
集合映射
如果实体或类具有特定变量的值集合,那么我们可以使用java中可用的任何一个集合接口映射这些值。 Hibernate可以持久保存java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List以及任何持久化实体或值array的实例。
Sr.No. | 集合类型和映射描述 |
---|---|
1 | java.util.Set 这是使用 |
2 | java.util.SortedSet 它使用 |
3 | java.util.List 它使用 |
4 | java.util.Collection 它使用 |
5 | java.util.Map 它使用 |
6 | java.util.SortedMap 这是使用 sort属性可以设置为比较器或自然排序。 |
Hibernate支持数组,其中
如果要映射Hibernate不直接支持的用户定义集合接口,则需要告诉Hibernate有关自定义集合的语义,这不是很容易,也不建议使用。
关联映射
实体类之间的关联映射和表之间的关系是ORM的灵魂。 以下是可以表达对象之间关系的基数的四种方式。 关联映射可以是单向的,也可以是双向的。
Sr.No. | 映射类型和描述 |
---|---|
1 | Many-to-One 使用Hibernate映射多对一关系 |
2 | One-to-One 使用Hibernate映射一对一关系 |
3 | One-to-Many 使用Hibernate映射一对多关系 |
4 | Many-to-Many 使用Hibernate映射多对多关系 |
组件映射
Entity类很可能将另一个类的引用作为成员变量。 如果引用的类没有自己的生命周期并完全依赖于拥有实体类的生命周期,那么引用的类因此被称为Component class 。
组件集合的映射也可以以类似的方式进行,就像具有较小配置差异的常规集合的映射一样。 我们将通过示例详细地看到这两个映射。
Sr.No. | 映射类型和描述 |
---|---|
1 | Component Mappings 将具有对另一个类的引用的类映射为成员变量。 |
Hibernate - Annotations
到目前为止,您已经了解了Hibernate如何使用XML映射文件将数据从POJO转换为数据库表,反之亦然。 Hibernate注释是在不使用XML文件的情况下定义映射的最新方法。 除了XML映射元数据之外,您还可以使用注释或替代XML映射元数据。
Hibernate Annotations是为Object和Relational Table映射提供元数据的强大方法。 所有元数据都与代码一起分配到POJO java文件中,这有助于用户在开发过程中同时理解表结构和POJO。
如果要将应用程序移植到其他符合EJB 3的ORM应用程序,则必须使用注释来表示映射信息,但如果您需要更大的灵活性,则应该使用基于XML的映射。
Hibernate注释的环境设置
首先,您必须确保使用JDK 5.0,否则需要将JDK升级到JDK 5.0以利用对注释的本机支持。
其次,您需要安装Hibernate 3.x annotations分发包,可从sourceforge获得:( 下载Hibernate Annotation )并复制hibernate-annotations.jar, lib/hibernate-comons-annotations.jar和lib/ejb3-persistence.jar从Hibernate Annotations发行版到你的CLASSPATH。
带注释的类示例
正如我在使用Hibernate Annotation时所提到的,所有元数据都与代码一起被分配到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包中,因此我们将此包作为第一步导入。 其次,我们将@Entity注释用于Employee类,它将此类标记为实体bean,因此它必须具有至少受保护范围可见的无参数构造函数。
@Table注释
@Table注释允许您指定将用于在数据库中保留实体的表的详细信息。
@Table注释提供了四个属性,允许您覆盖表的名称,目录及其架构,并对表中的列强制执行唯一约束。 目前,我们只使用表名,即EMPLOYEE。
@Id和@GeneratedValue Annotations
每个实体bean都有一个主键,您可以使用@Id注释在类上进行注释。 主键可以是单个字段,也可以是多个字段的组合,具体取决于您的表结构。
默认情况下,@ Id注释将自动确定要使用的最合适的主键生成策略,但您可以通过应用@GeneratedValue注释来覆盖它,这需要两个参数strategy和generator ,我不打算在这里讨论,所以让我们只使用默认密钥生成策略。 让Hibernate确定使用哪种生成器类型使您的代码可以在不同的数据库之间移植。
@Column Annotation
@Column注释用于指定字段或属性将映射到的列的详细信息。 您可以将列注释与以下最常用的属性一起使用 -
name属性允许显式指定列的名称。
length属性允许用于映射值的列的大小,特别是String值。
nullable属性允许在生成模式时将列标记为NOT NULL。
unique属性允许将列标记为仅包含唯一值。
创建应用程序类 (Create Application Class)
最后,我们将使用main()方法创建应用程序类来运行应用程序。 我们将使用此应用程序来保存很少的员工记录,然后我们将对这些记录应用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();
}
}
}
数据库配置
现在让我们创建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查询,后者依次对数据库执行操作。
虽然你可以使用Native SQL直接使用Hibernate的SQL语句,但我建议尽可能使用HQL来避免数据库可移植性的麻烦,并利用Hibernate的SQL生成和缓存策略。
SELECT,FROM和WHERE等关键字不区分大小写,但是表和列名称等属性在HQL中区分大小写。
来自条款
如果要将完整的持久对象加载到内存中,则将使用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条款
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子句提供对结果集的更多控制,然后是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子句。 以下是使用WHERE子句的简单语法 -
String hql = "FROM Employee E WHERE E.id = 10";
Query query = session.createQuery(hql);
List results = query.list();
ORDER BY子句
要对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条款
该子句允许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();
使用命名参数
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();
更新条款
批量更新对于使用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子句可用于删除一个或多个对象。 以下是使用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条款
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);
聚合方法
HQL支持一系列聚合方法,类似于SQL。 它们在HQL中的工作方式与在SQL中的工作方式相同,以下是可用功能的列表 -
Sr.No. | 功能和描述 |
---|---|
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();
使用Query进行分页
分页的Query接口有两种方法。
Sr.No. | 方法和描述 |
---|---|
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,它允许您以编程方式构建条件查询对象,您可以在其中应用过滤规则和逻辑条件。
Hibernate Session接口提供了createCriteria()方法,该方法可用于创建Criteria对象,该对象在应用程序执行条件查询时返回持久对象类的实例。
以下是条件查询的最简单示例之一,它将简单地返回与Employee类对应的每个对象。
Criteria cr = session.createCriteria(Employee.class);
List results = cr.list();
标准限制
您可以使用Criteria对象可用的add()方法为条件查询添加限制。 以下是添加限制以返回薪水等于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一起使用,如前面的教程中所述。
使用标准的分页
分页的Criteria接口有两种方法。
Sr.No. | 方法和描述 |
---|---|
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();
对结果进行排序
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();
预测和聚合
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"));
条件查询示例
考虑以下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
将包含SQL查询的字符串传递给createSQLQuery()方法后,可以使用addEntity(),addJoin()和addScalar()方法将SQL结果与现有的Hibernate实体,连接或标量结果相关联分别。
标量查询
最基本的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();
实体查询
上面的查询都是关于返回标量值,基本上返回结果集中的“原始”值。 以下是通过addEntity()从本机sql查询中获取整个实体对象的语法。
String sql = "SELECT * FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
List results = query.list();
命名的SQL查询
以下是通过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();
本机SQL示例
考虑以下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对象在将对象提交到数据库之前将其保持在自己的权限之下。
如果对对象发出多个更新,Hibernate会尽可能地延迟执行更新以减少发出的更新SQL语句的数量。 如果关闭会话,则缓存的所有对象都将丢失,并在数据库中保留或更新。
Second-level Cache
二级缓存是可选缓存,在尝试在二级缓存中定位对象之前,将始终查询第一级缓存。 可以基于每个类和每个集合配置二级缓存,主要负责跨会话缓存对象。
任何第三方缓存都可以与Hibernate一起使用。 提供了一个org.hibernate.cache.CacheProvider接口,必须实现该接口才能为Hibernate提供缓存实现的句柄。
Query-level Cache
Hibernate还为查询结果集实现了一个缓存,它与二级缓存紧密集成。
这是一个可选功能,需要两个额外的物理缓存区域,用于保存缓存的查询结果和上次更新表时的时间戳。 这仅适用于使用相同参数频繁运行的查询。
二级缓存
Hibernate默认使用第一级缓存,而您无需使用第一级缓存。 让我们直接进入可选的二级缓存。 并非所有类都受益于缓存,因此能够禁用二级缓存非常重要。
Hibernate二级缓存分两步设置。 首先,您必须决定使用哪种并发策略。 之后,使用缓存提供程序配置缓存过期和物理缓存属性。
并发策略
并发策略是一个中介,它负责在缓存中存储数据项并从缓存中检索它们。 如果要启用二级缓存,则必须为每个持久化类和集合决定使用哪种缓存并发策略。
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对定义的缓存使用读写并发策略。
缓存提供商
在考虑并发策略之后,您将使用缓存候选类来选择缓存提供程序。 Hibernate强制您为整个应用程序选择单个缓存提供程序。
Sr.No. | 缓存名称和描述 |
---|---|
1 | EHCache 它可以缓存在内存或磁盘和集群缓存中,它支持可选的Hibernate查询结果缓存。 |
2 | OSCache 支持在单个JVM中缓存到内存和磁盘,并具有丰富的过期策略和查询缓存支持。 |
3 | warmCache 基于JGroups的集群高速缓存。 它使用群集失效,但不支持Hibernate查询缓存。 |
4 | JBoss Cache 完全事务复制的群集缓存,也基于JGroups多播库。 它支持复制或失效,同步或异步通信以及乐观和悲观锁定。 支持Hibernate查询缓存。 |
每个缓存提供程序都与每个并发策略都不兼容。 以下兼容性矩阵将帮助您选择合适的组合。
战略/供应商 | 只读 | Nonstrictread写 | 读写 | 交易 |
---|---|---|---|---|
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 ,它应该位于应用程序的CLASSPATH中。 在Employe类的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时都会访问二级缓存。
您应该分析所有类,并为每个类选择适当的缓存策略。 有时,二级缓存可能会降低应用程序的性能。 因此,建议首先对应用程序进行基准测试,而不启用缓存,然后启用适合的缓存并检查性能。 如果缓存没有提高系统性能,那么启用任何类型的缓存都没有意义。
查询级缓存
要使用查询缓存,必须首先使用配置文件中的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 。 如果您使用Hibernate进行batch processing ,则可以解决此问题。
要使用批处理功能,首先将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();
批处理示例
让我们修改配置文件以添加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 ;
}
}
编译和执行 (Compilation and Execution)
以下是编译和运行上述应用程序的步骤。 确保在进行编译和执行之前已适当地设置了PATH和CLASSPATH。
如上所述创建hibernate.cfg.xml配置文件。
创建Employee.hbm.xml映射文件,如上所示。
如上所示创建Employee.java源文件并进行编译。
创建ManageEmployee.java源文件,如上所示并编译它。
执行ManageEmployee二进制文件以运行该程序,该程序将在EMPLOYEE表中创建100000条记录。
Hibernate - Interceptors
正如您已经了解到的那样,在Hibernate中,将创建并保留一个对象。 对象更改后,必须将其保存回数据库。 此过程一直持续到下一次需要该对象时,它将从持久性存储中加载。
因此,对象在其生命周期中经历不同的阶段,并且Interceptor Interface提供方法,可以在不同阶段调用这些方法来执行一些所需的任务。 这些方法是从会话到应用程序的回调,允许应用程序在保存,更新,删除或加载之前检查和/或操纵持久对象的属性。 以下是Interceptor接口中可用的所有方法的列表 -
Sr.No. | 方法和描述 |
---|---|
1 | findDirty() 在Session对象上调用flush()方法时,将调用此方法。 |
2 | instantiate() 在实例化持久化类时调用此方法。 |
3 | isUnsaved() 将对象传递给saveOrUpdate()方法/时调用此方法 |
4 | onDelete() 在删除对象之前调用此方法。 |
5 | onFlushDirty() 当Hibernate在刷新即更新操作期间检测到对象脏(即已更改)时,将调用此方法。 |
6 | onLoad() 在初始化对象之前调用此方法。 |
7 | onSave() 在保存对象之前调用此方法。 |
8 | postFlush() 在发生刷新并且内存中已更新对象后调用此方法。 |
9 | preFlush() 在刷新之前调用此方法。 |
Hibernate Interceptor使我们可以完全控制对象对应用程序和数据库的外观。
如何使用拦截器?
要构建拦截器,您可以直接实现Interceptor类或扩展EmptyInterceptor类。 以下是使用Hibernate Interceptor功能的简单步骤。
创建拦截器
我们将在我们的示例中扩展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");
}
}
创建POJO类
现在,让我们稍微修改一下我们使用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;
}
}
创建数据库表
第二步是在数据库中创建表。 每个对象都会有一个表,您愿意提供持久性。 考虑上面解释的对象,需要存储和检索到以下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>
创建应用程序类 (Create Application Class)
最后,我们将使用main()方法创建应用程序类来运行应用程序。 在这里,应该注意的是,在创建会话对象时,我们使用Interceptor类作为参数。
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>