1、初始Hibernate
1.1 理解ORM原理
目前面向对象思想是软件开发的基本思想,关系数据库又是应用系统中必不可少的一环,但是面向对象是从软件工程的基本原则发展而来,而关系数据库确是从数学理论的基础诞生的,两者的区别是巨大的,为了解决这个问题,ORM便应运而生。
ORM(Object Relational Mapping)是对象到关系的映射,它的作用是在关系数据库和对象之间做一个自动映射,将数据库中数据表映射成为对象,也就是持久化类,对关系型数据以对象的形式进行操作,减少应用开发过程中数据持久化的编程任务。可以把ORM理解成关系型数据和对象的一个纽带,开发人员只需关注纽带的一端映射的对象即可。ORM原理如下图示。
1.2 Hibernate简介
作为一个优秀的持久层框架,Hibernate充分体现了ORM的设计理念,提供了高效的对象到关系型数据库的持久化服务,它将持久化服务从软件业务层中完全抽取出来,让业务逻辑的处理更加简单,程序之间的各种业务并非紧密耦合,更加有利于高效地开发与维护。使开发人员在程序中可以利用面向对象的思想对关系型数据进行持久化操作,为关系型数据库和对象型数据打造了一个便捷的高速公路。下图就是一个简单的Hibernate体系概要图。
从这个概要图可以清楚地看出,Hibernate是通过数据库和配置信息进行数据持久化服务和持久化对象的。Hibernate封装了数据库的访问细节,通过配置的属性文件这条纽带连接着关系型数据库和程序中的实体类。
在Hibernate中有非常重要的3个类,它们分别是配置类(Configuration)、会话工厂类(SessionFactory)和会话类(Session)。
类 | 说明 |
---|---|
配置类(Configuration) | 配置类主要负责管理Hibernate的配置信息以及启动Hibernate,在Hibernate运行时配置类会读取一些底层实现的基本信息,其中包括数据库的URL,数据库用户名、数据库密码、数据库驱动类和数据库适配器(dialect)。 |
会话工厂类(SessionFactory) | 会话工厂类是生成Session的工厂,它保存了当前数据库中所有的映射关系,可能只有一个可选的二级数据缓存,并且它是线程安全的。但是会话工厂类是一个重量级对象,它的初始化创建过程会消耗大量的系统资源。 |
会话类(Session) | 会话类是Hibernate中数据库持久化操作的核心,它将负责Hibernate所有的持久化操作,通过它开发人员可以实现数据库基本的增、删、改、差的操作。但会话类并不是线程安全的,应注意不要多个线程共享一个Session。 |
2、Hibernate入门
2.1 获取Hibernate
从Hibernate的官方网站获取所需的jar包,官方网址为http://www.hibernate.org,在该网站可以免费获取Hibernate的帮助文档和jar包。解压下载后的Hibernate文件,将目录:hibernate-release-5.4.4.Final\lib\required 下的所有jar包文件添加到项目中。
注意:除了需要下载Hibernate的jar包,还需要下载JDBC数据库驱动并添加到项目中。
了解搭建Hibernate开发环境的详细说明,请浏览本博客的文章:Hibernate搭建开发环境
2.2 Hibernate配置文件
Hibernate通过读取默认的XML配置文件hibernate.cfg.xml加载数据库的配置信息,该配置文件被默认放于项目的classpath根目录下(src目录下)。
【示例】连接应用的MySQL数据库,XML文件的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库驱动 -->
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<!-- 数据库连接的URL -->
<property name="connection.url">jdbc:mysql://localhost:3306/db_admin?useSSL=false&serverTimezone=GMT%2b8</property>
<!-- 数据库连接用户名 -->
<property name="connection.username">root</property>
<!-- 数据库连接密码 -->
<property name="connection.password">123456</property>
<!-- Hibernate方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 打印SQL语句 -->
<property name="show_sql">true</property>
<!-- 映射文件 -->
<mapping resource="com/pjb/entity/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
从配置文件中可以看出配置的信息包括整个数据库的信息,例如数据库的驱动、URL地址、用户名、密码和Hibernate使用的方言,还需要管理程序中各个数据库表的映射文件。配置文件中<property>元素的常用配置属性如下表所示。
<property>元素的常用配置属性:
属性 | 说明 |
---|---|
connection.driver_class | 连接数据库的驱动。 |
connection.url | 连接的数据库的URL地址。 |
connection.username | 连接数据库用户名。 |
connection.password | 连接数据库密码。 |
dialect | 设置连接数据库使用的方言。 |
show_sql | 是否在控制台打印SQL语句。 |
format_sql | 是否格式化SQL语句。 |
hbm2ddl.auto | 是否自动生成数据库表。 |
在程序开发的过程中,一般会将show_sql属性设置为true,以便在控制台打印自动生成的SQL语句,方便程序调试。
以上只是Hibernate配置的一部分,还可以配置表的自动生成、Hibernate的数据连接池等。
2.3 了解并编写持久化类
Hibernate中持久化类是Hibernate操作的对象,也就是通过对象-关系映射(ORM)后数据库表所映射的实体类,用来描述数据库表的结构信息。在持久化类中的属性应该与数据库表中的字段相匹配。
【示例】创建名称为User的JavaBean。
package com.pjb.entity;
/**
* 用户信息实体类
* @author pan_junbiao
**/
public class User
{
private int id; //用户编号
private String name; //用户姓名
private String blog; //博客地址
private int age; //年龄
private String remark; //备注
//默认的构造方法
public User() {
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getBlog()
{
return blog;
}
public void setBlog(String blog)
{
this.blog = blog;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getRemark()
{
return remark;
}
public void setRemark(String remark)
{
this.remark = remark;
}
}
User类作为一个简单的持久化类,它符合最基本的JavaBean编码规范,也就是POJO(Plain Old Java Object)编程模型。持久化类中的每个属性都有相应的setXXX()和getXXX()方法,它不依赖于任何接口和继承任何类。
Hibernate中的持久化类有4条编程规则:
(1)实现一个默认的构造函数
所有的持久化类中都必须含有一个默认的无参数构造方法(User类中就含有无参数的构造方法),以便Hibernate通过Constructor.newInstance()实例化持久化类。
(2)提供一个标识属性(可选)
标识属性一般映射的是数据库表中的主键字段,例如User中的属性id,建议在持久化类中添加一致的标识属性。
(3)使用非final类(可选)
如果使用了final类,Hibernate就不能使用代理来延迟关联加载,这会影响开发人员进行性能优化的选择。
(4)为属性声明访问器(可选)
持久化类的属性不能声明为public的,最好以private的setXXX()和getXXX()方法对属性进行持久化。
2.4 Hibernate映射
Hibernate的核心就是对象关系映射,对象和关系型数据库之间的映射通常是用XML文档来实现的。这个映射文档被设计成易读的,并且可以手工修改。映射文件的命名规则为*.hbm.xml,以User的持久化类的映射文件为例,代码如下:对User对象进行配置。
【示例】创建文件名为User.hbm.xml的配置文件,对User对象进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 用户信息字段配置信息 -->
<hibernate-mapping>
<class name="com.pjb.entity.User" table="tb_user">
<!-- 用户编号id值 -->
<id name="id" column="id" type="int">
<!-- 持久化类的唯一性标识 -->
<generator class="native"/>
</id>
<!-- 用户姓名 -->
<property name="name" type="string" not-null="true" length="20">
<column name="name"/>
</property>
<!-- 博客地址 -->
<property name="blog" type="string" not-null="true" length="20">
<column name="blog"/>
</property>
<!-- 年龄 -->
<property name="age" type="int">
<column name="age"/>
</property>
<!-- 备注 -->
<property name="remark" type="string" length="50">
<column name="remark"/>
</property>
</class>
</hibernate-mapping>
<DOCTYPE>元素:
在所有的Hibernate映射文件中都需要定义如上所示的<DOCTYPE>元素,用来获取DTD文件。
<hibernate-mapping>元素:
<hibernate-mapping>元素是映射文件中其他元素的根元素,这个元素中包含一些可选属性,例如,schema属性是指该文件映射表所在数据库的schema名称;package属性是指定一个包前缀,如果在<class>元素中没有指定全限的类,就将使用package属性定义的包前缀作为包名。
<class>元素:
<class>元素主要用于指定持久化类和映射的数据库表名。name属性需要指定持久化类的全限定的类名(如:com.pjb.entity.User);table属性就是持久化类所映射的数据库表名。
<class>元素中包含了一个<id>属性和多个<property>元素,<id>属性用于持久化类的唯一标识与数据库表的主键字段的映射,在<id>属性中通过<generator>元素定义主键的生成策略。<property>元素用于持久化类的其他属性和数据库表中非主键字段的映射。
持久化类映射文件<property>元素的常用配置属性:
属性名称 | 说明 |
---|---|
name | 持久化类属性的名称,以小写字母开头。 |
column | 数据库字段名。 |
type | 数据库的字段类型。 |
length | 数据库字段定义的长度。 |
not-null | 该数据库字段是否可以为空,该属性为布尔变量。 |
unique | 该数据库字段是否唯一,该属性为布尔变量。 |
lazy | 是否延迟抓取,该属性为布尔变量。 |
注意:如果在映射文件中没有配置column和type属性,Hibernate将会默认使用持久化类中的属性名称和属性类型匹配数据表中的字段。
2.5 Hibernate主键策略
<id>元素的子元素<generator>元素是一个Java类的名字,用来为持久化类的实例生成唯一的标识映射数据库中的主键字段。在配置文件中通过设置<generator>元素的属性设置Hibernate的主键生成策略,主要的内置属性如下表所示。
属 性 名 称 | 说明 |
---|---|
increment | 用于为long、short、或者int类型生成唯一标识。在集群下不要使用该属性。 |
identity | 由底层数据库生成主键,前提是底层数据库支持自增字段类型。 |
sequence | 根据底层数据库的序列生成主键,前提是底层数据库支持序列。 |
hilo | 根据高/低算法生成,把特定表的字段作为高位值来源,在默认的情况下选用hibernate_unique_key表的next_hi字段。 |
native | 根据底层数据库对自动生成标识符的支持能力选择identity、sequence或hilo。 |
assigned | 由程序负责主键的生成,此时持久化类的唯一标识不能声明为private类型。 |
select | 通过数据库触发器生成主键。 |
foreign | 使用另一个相关联的对象的标识符,通常和<one-to-one>一起使用。 |
3、Hibernate数据持久化
持久化操作是Hibernate的核心,下面将介绍如何创建线程安全的Hibernate初始化类,并利用Hibernate的Session对象实现基本的数据库增、删、改、查的操作。
3.1 Hibernate实例状态
Hibernate的实例状态分为三种,分别为瞬时状态(Transient)、持久化状态(Persistent)、脱管状态(Detached)。
瞬时状态(Transient):
实体对象是通过Java中的new关键字开辟内存空间创建的Java对象,但是它并没有纳入Hibernate的Session的管理中,如果没有变量对它引用,它将被JVM(垃圾回收器)回收。瞬时状态的对象在内存中是孤立存在的,它与数据库中的数据无任何关联,仅是一个信息携带的载体。
假如一个瞬时状态对象被持久化状态对象引用,它也会自动变为持久化状态对象。
持久化状态(Persistent):
持久化状态对象存在于数据库中的数据关系,它总是与会话状态(Session)和事务(Transaction)关联在一起。当持久化状态对象发送改动时并不会立即执行数据库操作。只有当事务结束时,才会更新数据库,以便保证Hibernate的持久化对象和数据库操作的同步性。当持久化状态对象变为托管状态对象时,它将不再Hibernate持久层的管理范围之内。
脱管状态(Detached):
当持久化状态的对象的Session关闭之后,这个对象就从持久化状态的对象变为脱管状态的对象。脱管状态的对象仍然存在与数据库中的数据关联,只是它并不在Hibernate的Session管理范围之内。如果将脱管状态的对象重新关联某个新的Session上,它将变回持久化状态对象。
Hibernate中3种实例状态的关系如图:
3.2 Hibernate初始化类
Session对象是Hibernate中数据库持久化操作的核心,它将负责Hibernate所有的持久化操作,通过它开发人员可以实现数据库基本的增、删、改、查的操作。而Session对象又是通过SessionFactory对象获取的,那么SessionFactory对象又是如何创建的呢?可以通过Configuration对象创建SessionFactory,关键代码如下:
Configuration cfg = new Configuration().configure(); // 加载Hibernate配置文件
factory = cfg.buildSessionFactory(); // 实例化SessionFactory
Configuration对象会加载Hibernate的基本配置信息,如果没有在configure()方法中指定加载配置XML文档的路径信息,Configuration对象会默认加载项目classpath根目录(src目录)下的hibernate.cfg.xml文件。
【示例】创建HibernateUtil类,用于实例化对Hibernate的初始化。
package com.pjb.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Hibernate公共类
* @author pan_junbiao
**/
public class HibernateUtil {
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();//ThreadLocal对象
private static SessionFactory sessionFactory = null;//SessionFactory对象
//静态块
static {
try {
// 加载Hibernate配置文件
Configuration cfg = new Configuration().configure();
sessionFactory = cfg.buildSessionFactory();
} catch (Exception e) {
System.err.println("创建会话工厂失败");
e.printStackTrace();
}
}
/**
* 获取Session
* @return Session
* @throws HibernateException
*/
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession(): null;
threadLocal.set(session);
}
return session;
}
/**
* 重建会话工厂
*/
public static void rebuildSessionFactory() {
try {
// 加载Hibernate配置文件
Configuration cfg = new Configuration().configure();
sessionFactory = cfg.buildSessionFactory();
} catch (Exception e) {
System.err.println("创建会话工厂失败");
e.printStackTrace();
}
}
/**
* 获取SessionFactory对象
* @return SessionFactory对象
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* 关闭Session
* @throws HibernateException
*/
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();//关闭Session
}
}
}
通过这个Hibernate公共类,就可以有效地管理Session,避免了Session的多线程共享数据的问题。
3.3 保存数据
Hibernate对JDBC的操作进行了轻量级的封装,使开发人员可以利用Session对象以面向对象的思想实现对关系型数据库的操作,轻而易举地实现数据库最基本的增、删、改、查操作。
Hibernate数据持久化流程图:
【示例】新增用户信息。
(1)在MySQL数据库中创建tb_user用户信息表。
-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tb_user;
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tb_user
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
NAME VARCHAR(20) NOT NULL COMMENT '用户姓名',
blog VARCHAR(50) NOT NULL COMMENT '博客地址',
age INT COMMENT '年龄',
remark VARCHAR(50) COMMENT '备注'
) COMMENT = '用户信息表';
(2)创建新增用户类AddUser.java。
package com.pjb.main;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.pjb.hibernate.HibernateUtil;
import com.pjb.entity.User;
/**
* 新增用户信息
* @author pan_junbiao
**/
public class AddUser
{
public static void main(String[] args)
{
Session session = null; //声明Session对象
Transaction ts = null; //声明事务对象
User user = new User(); //实例化持久化类
//为持久化类属性赋值
user.setName("pan_junbiao的博客");
user.setBlog("https://blog.csdn.net/pan_junbiao");
user.setAge(32);
user.setRemark("您好,欢迎访问 pan_junbiao的博客!");
try
{
session = HibernateUtil.getSession(); //获取Session对象
ts = session.beginTransaction(); //开启事务
session.save(user); //执行数据库添加操作
ts.commit(); //事务提交
System.out.println("数据添加成功");
}
catch(Exception ex){
//事务回滚
if(ts!=null)
{
ts.rollback();
}
System.out.println("数据添加失败");
ex.printStackTrace();
}
finally
{
HibernateUtil.closeSession(); //关闭Session对象
}
}
}
程序运行后,在数据库tb_user表查询信息:
说明:持久化对象User在创建之后是瞬时状态,在Session执行save()方法之后持久化对象User的状态变为持久化状态,但是这时数据操作并未提交给数据库,在事务执行commit()方法之后,才完成数据库的操作,此时的持久化对象User成为脏(dirty)对象。Session关闭之后,持久化对象User的状态变为托管状态,并最后被JVM回收。
3.4 查询数据
Session对象提供了种对象装载的方法,分别是get()方法和load()方法。
3.4.1 get()方法
如果开发人员不确定数据库中是否有匹配的记录存在,就可以使用get()方法进行对象封装,因为它会立刻访问数据库。如果数据库中没有匹配记录存在,会返回null。
【示例】使用get()方法获取User对象。
package com.pjb.main;
import org.hibernate.Session;
import com.pjb.hibernate.HibernateUtil;
import com.pjb.entity.User;
/**
* 获取用户信息
* @author pan_junbiao
**/
public class GetUser
{
public static void main(String[] args)
{
Session session = null; //声明Session对象
try {
//Hibernate的持久化操作
session = HibernateUtil.getSession();//获取Session
User user = (User) session.get(User.class, 1); //装载对象
System.out.println("用户编号:"+user.getId());
System.out.println("用户姓名:"+user.getName());
System.out.println("博客地址:"+user.getBlog());
System.out.println("年龄:"+user.getAge());
System.out.println("备注:"+user.getRemark());
} catch (Exception e) {
System.out.println("对象装载失败");
e.printStackTrace();
} finally{
HibernateUtil.closeSession();//关闭Session
}
}
}
说明:get()方法中包含两个参数:一个是持久化对象,另一个是持久化对象中的唯一标识。get()方法的返回值可能为null,也可能是一个持久化对象。
3.4.2 load()方法
load()方法返回对象的代理,只有在返回对象被调用时,Hibernate才会发出SQL语句去查询对象。
【示例】使用load()方法获取User对象。
package com.pjb.main;
import org.hibernate.Session;
import com.pjb.hibernate.HibernateUtil;
import com.pjb.entity.User;
/**
* 获取用户信息
* @author pan_junbiao
**/
public class GetUser
{
public static void main(String[] args)
{
Session session = null; //声明Session对象
try {
//Hibernate的持久化操作
session = HibernateUtil.getSession();//获取Session
User user = (User) session.load(User.class, 1); //装载对象
System.out.println("用户编号:"+user.getId());
System.out.println("用户姓名:"+user.getName());
System.out.println("博客地址:"+user.getBlog());
System.out.println("年龄:"+user.getAge());
System.out.println("备注:"+user.getRemark());
} catch (Exception e) {
System.out.println("对象装载失败");
e.printStackTrace();
} finally{
HibernateUtil.closeSession();//关闭Session
}
}
}
另外,load()方法还可以加载到指定的对象实例上。
session = HibernateUtil.getSession(); //获取Session
User user = new User(); //实例化对象
session.load(user,1); //装载对象
两种方法的执行结果:
(1)get()方法执行结果。
(2)load()方法执行结果。
说明:由于load()方法返回对象在被调用时Hibernate才会发出SQL语句去查询对象,所以在用户编号信息输出之后才输出SQL语句。因为用户编号在程序中是已知的,并不需要查询。
3.5 删除数据
在Session对象中需要使用delete()方法进行数据的删除操作。但是只有对象在持久化状态时才能进行执行delete()方法,所以在删除数据之前,首先需要将对象的状态转换为持久化状态。
【示例】使用delete()方法删除指定的用户信息。
package com.pjb.main;
import com.pjb.entity.User;
import com.pjb.hibernate.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
/**
* 删除用户信息
* @author pan_junbiao
**/
public class DeleteUser
{
public static void main(String[] args)
{
Session session = null; //声明Session对象
Transaction ts = null; //声明事务对象
try {
session = HibernateUtil.getSession(); //获取Session
User user = (User) session.get(User.class, 2); //装载对象
if(user!=null)
{
ts = session.beginTransaction(); //开启事务
session.delete(user); //删除持久化对象
ts.commit(); //事务提交
System.out.println("删除成功");
}
else
{
System.out.println("用户不存在");
}
} catch (Exception e) {
//事务回滚
if(ts!=null)
{
ts.rollback();
}
System.out.println("删除失败");
e.printStackTrace();
} finally{
HibernateUtil.closeSession();//关闭Session
}
}
}
执行结果:
3.6 修改数据
在Hibernate的Session的管理中,如果程序对持久化状态的对象做出了修改,当Session刷出时Hibernate会对实例进行持久化操作,利用Hibernate的该特性就可以实现商品信息的修改操作。
【示例】修改用户信息。
package com.pjb.main;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.pjb.entity.User;
import com.pjb.hibernate.HibernateUtil;
/**
* 修改用户信息
* @author pan_junbiao
**/
public class UpdateUser
{
public static void main(String[] args)
{
Session session = null; //声明Session对象
Transaction ts = null; //声明事务对象
try {
session = HibernateUtil.getSession(); //获取Session
User user = (User) session.get(User.class, 1); //装载对象
if(user!=null)
{
user.setName("pan_junbiao的博客_002");
user.setRemark("您好,欢迎访问 pan_junbiao的博客_002!");
ts = session.beginTransaction(); //开启事务
session.update(user); //删除持久化对象
ts.commit(); //事务提交
System.out.println("修改成功");
}
else
{
System.out.println("用户不存在");
}
} catch (Exception e) {
//事务回滚
if(ts!=null)
{
ts.rollback();
}
System.out.println("修改失败");
e.printStackTrace();
} finally{
HibernateUtil.closeSession();//关闭Session
}
}
}
执行结果:
程序运行后,在数据库tb_user表查询信息:
3.7 关于延迟加载
在load()方法的讲解中,其实已经涉及了延迟加载的策略。在使用load()方法加载持久化对象时,它返回的是一个未初始化的代理(代理无须从数据库中抓取数据对象的数据),直到调用代理的某个方法时Hibernate才会访问数据库。在非延迟加载过程中,Hibernate会直接访问数据库,并不会使用代理对象。
当装载的对象长时间没有调用的时候,就会被垃圾回收器所回收,在程序中合理地使用延迟加载策略将会优化系统的性能。采用延迟加载可以使Hibernate节省系统的内存空间,否则每加载一个持久化对象就需要将其关联的数据信息装载到内存中,这将为系统节约部分不必要的开销。
在Hibernate中可以通过使用一些采用延迟加载策略封装的方法实现延迟加载的功能,如load()方法,同时还可以通过设置映射文件中的<property>元素中的lazy属性实现该功能。
【示例】以用户信息的XML文档配置为例,实现延迟加载设置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 用户信息字段配置信息 -->
<hibernate-mapping>
<class name="com.pjb.entity.User" table="tb_user">
<!-- 用户编号id值 -->
<id name="id" column="id" type="int">
<!-- 持久化类的唯一性标识 -->
<generator class="native"/>
</id>
<!-- 用户姓名 -->
<property name="name" type="string" not-null="true" length="20" lazy="true">
<column name="name"/>
</property>
</class>
</hibernate-mapping>
通过该方式的设置,用户名称属性就被设置成了延迟加载。
4、使用Hibernate缓存
缓存是数据库数据在内存中的临时容器,是数据库与应用程序的中间件。
在Hibernate中也采用了缓存的技术,使Hibernate可以更加高效地进行数据持久化操作。Hibernate数据缓存分为两种,分别为一级缓存(Session Level,也称为内部缓存)和二级缓存(SessionFactory Level)。
4.1 一级缓存的使用
Hibernate的一级缓存属于Session级缓存,所以它的生命周期与Session是相同的,它随Session的创建而创建,随Session的销毁而销毁。
当程序使用Session加载持久化对象时,Session首先会根据加载的数据类和唯一性标识在缓存中查找是否存在此对象的缓存实例,如果存在将其作为结果返回,否则Session会继续向二级缓存中查找实例对象。
注意:在Hibernate中不同的Session之间是不能共享一级缓存的,也就是说,一个Session不能访问其他Session在一级缓存中的对象缓存示例。
【示例】在同一Session中查询两次用户信息。
package com.pjb.main;
import org.hibernate.Session;
import com.pjb.hibernate.HibernateUtil;
import com.pjb.entity.User;
/**
* 获取用户信息
* @author pan_junbiao
**/
public class GetUser
{
public static void main(String[] args)
{
Session session = null; //声明Session对象
try {
//Hibernate的持久化操作
session = HibernateUtil.getSession();//获取Session
User user1 = (User) session.get(User.class, 1); //第一次装载对象
System.out.println("第一次装载对象");
User user2 = (User) session.get(User.class, 1); //第二次装载对象
System.out.println("第二次装载对象");
} catch (Exception e) {
e.printStackTrace();
} finally{
HibernateUtil.closeSession();//关闭Session
}
}
}
执行结果:
从控制台输出的信息中可以看出,Hibernate只访问了一次数据库,第二次对象加载时是从一级缓存中将该对象的缓存实例以结果的形式直接返回。
4.2 二级缓存的介绍
Hibernate的二级缓存将由从属于一个SessionFactory的所有Session对象共享。当程序使用Session加载持久化对象时,Session首先会根据加载的数据类和唯一性标识在缓存中查找是否存在此对象的缓存实例,如果存在将其作为结果返回,否则Session会继续向二级缓存中查找实例对象,如果二级缓存中也无匹配对象,Hibernate将直接访问数据库。
由于Hibernate本身并未提供二级缓存的产品化实现,所以需要引入第三方插件实现二级缓存的策略。例如使用EHCache作为Hibernate默认的二级缓存。