一、Hibernate的一级缓存
1. 缓存的概述
缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源;是数据库与应用程序的中间件。
2. Hibernate的一级缓存
Hibernate中提供了两种缓存机制:一级缓存、二级缓存。
- Hibernate的一级缓存:称为是Session级别的缓存;一级缓存生命周期与Session一致(一级缓存是由Session中的一系列的Java集合构成);一级缓存是
自带的、不可卸载的
。 - Hibernate的二级缓存是SessionFactory级别的缓存,是需要配置的缓存。
具体讲解,可以参考下面博文:
Hibernate–一级缓存和二级缓存的区别
3. 证明一级缓存的存在
在证明一级缓存的存在之前,我们需对一级缓存的特点
做一了解:
- 当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
- 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库;如果缓存中没有要查询的对象,将会去数据库中查询对应对象,并添加到一级缓存中。
- 当调用Session的close()方法时,Session缓存会被清空。
下面,我们来证明一下!
public class HibernateDemo3 {
@Test
//证明一级缓存的存在
public void demo1() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer1 = session.get(Customer.class, 1l);
System.out.println(customer1);
Customer customer2 = session.get(Customer.class, 1l);
System.out.println(customer2);
System.out.println(customer1 == customer2);
tx.commit();
session.close();
}
}
运行上述代码,并进行断点调试:
当第一次调用get()方法时,缓存中没有要查询的对象,将会去数据库中查询对应对象,并添加到一级缓存中。这次,执行了SQL语句:
Customer customer1 = session.get(Customer.class, 1l);
当第二次调用get方法时,缓存中已经有了要查询的对象,直接返回即可。因此,这一次不会发送SQL语句。
这就证明了一级缓存的存在。
二、Hibernate的事务管理
1. 回顾:事务
-
什么是事务?
事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功,要么全部失败。 -
事务的特性
A. 原子性:代表事务不可分割。
B. 一致性:代表事务执行的前后,数据的完整性保持一致。
C. 隔离性:代表一个事务执行的过程中,不应该受到其他事务的干扰。
D. 持久性:代表事务执行完毕后,数据就持久到数据库中了。 -
如果不考虑隔离性,引发安全性问题
A. 读问题- 脏读:一个事务读到另一个事务未提交的数据。
- 不可重复读:一个事务读到另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致。
- 虚读:一个事务读到另一个事务已经提交的insert数据,导致在前一个事务多次查询结果不一致。
B. 写问题(了解):引发两类丢失更新
-
解决读问题
通过设置事务的隔离级别:
a. Read uncommitted:以上读问题都会发生
b. Read committed:解决脏读,但是不可重复和虚读有可能发生【Oracle√】
c. Repeatable read:解决脏读和不可重复读,但是虚读有可能发生【MySQL√】
d. Serializable:解决所有的读问题【安全性高,但效率低】
2. Hibernate中设置事务隔离级别
方法:在hibernate.cfg.xml文件中的<session-factory>标签元素中进行
,配置方法如下所示:
<!--
事务隔离级别
hibernate.connection.isolation = 4
1---Read uncommitted isolation
2---Read committed isolation
4---Repeatable read isolation
8---Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
3. Service层事务
3.1 事务为什么在Service层???
画个图,简单领悟一下:
推荐博文:为什么事务普遍加在service层?
但有一个问题:在调用Dao1中的方法xxx()时会产生一个连接对象,在调用Dao2中的方法yyy()时也会产生一个连接对象。那么,如何保证连接对象是同一个
呢?
- 方法一:向下传递(DBUtils中使用)
- 方法二:使用ThreadLocal对象
A. 将获取到的连接绑定到当前线程中
B. 在DAO方法中,通过当前的线程获得连接对象
3.2 Hibernate解决Service的事务管理
Hibernate框架内部已经绑定好了ThreadLocal
- 在SessionFactory接口中提供了一个方法getCurrentSession();
- 通过配置来完成。
我们首先修改Hibernate的工具类HibernateUtils,添加getCurrentSession()方法。
public class HibernateUtils{
public static Session getCurrentSession(){
return sf.getCurrentSession();
}
}
现在,如果我们直接调用这个方法,就会出错,因为这个方法默认未开始,这就需要我们配置了。我们需要在Hibernate的核心配置文件hibernate.cfg.xml
中配置.
在Hibernate的配置文件中,hibernate_current_session_context_class
属性用于指定Session管理方式,可选值包括:
thread :Session对象的生命周期与本地线程绑定
- jta :Session对象的生命周期与JTA事务绑定
- managed :Hibernate委托程序来管理Session对象的生命周期
<!-- 配置session绑定本地线程 -->
<property name="hibernate_current_session_context_class">thread</property>
配置完之后,我们在代码实现时,就不需要session.close()
,如果加上,程序就会报错,因为等线程执行完之后,session会自动关闭。