Hibernate 一级缓存和快照
1 Hibernate的一级缓存
Hibernate的一级缓存就是指Session缓存。通过查看Session接口的实现类——SessionImpl.java的源码可发现有如下两个类:
- actionQueue它是一个行动队列,它主要记录crud操作的相关信息。
- persistenceContext它是持久化上下文,它其实才是真正的缓存。
在Session
中定义了一系列的集合来存储数据,它们构成了Session的缓存。只要Session没有关闭,它就会一直存在。当我们通过Hibernate中的Session提供的一些API例如save()、get()、update()等进行操作时,就会将持久化对象保存到Session中,当下一次再去查询缓存中具有的对象(通过OID值来判断),就不会去从数据库中查询了,而是直接从缓存中获取。Hibernate的一级缓存存在的目的就是为了减少对数据库的访问。
当然了,在Hibernate中还有一个二级缓存,它是SessionFactory级别缓存,后面文章再做介绍。
1.1 演示一级缓存的存在
现在我举例来演示一级缓存的存在。首先我们肯定要搭建好Hibernate的开发环境,读过前面文章的童鞋,应该可以快速搭建好的,在此不做过多赘述。
在com.yiidian.test包下新建一个单元测试类——Demo.java,我们首先编写如下方法来测试一级缓存的存在:
// 测试一级缓存
@Test
public void test1() {
// 1.得到session
Session session = HibernateUtil.getSession();
session.beginTransaction();
Customer customer = session.get(Customer.class, 1); // 查询id=1的Customer对象,如果查询到,会将Customer对象存储到一级缓存中
Customer customer2 = session.get(Customer.class, 1); // 会从一级缓存中查询,而不会向数据库再发送sql语句查询
// 2.事务提交,并关闭session
session.getTransaction().commit();
session.close();
}
首次查询id为1的Customer对象时,Hibernate会向MySQL数据库发送如下SQL语句:
Hibernate:
select
customer0_.id as id1_0_0_,
customer0_.name as name2_0_0_,
customer0_.gender as gender3_0_0_
from
t_customer customer0_
where
customer0_.id=?
而第二次查询时,并没有向MySQL数据库发送select语句。这是因为首次查询id为1的Customer对象时,如果查询到,就会将Customer对象存储到一级缓存中,第二次查询时,会从一级缓存中查询,而不会向数据库再发送select语句查询。
2 持久化对象可以自动更新数据库
现在我举例来演示持久化对象具有自动更新数据库的能力。在Demo单元测试类中编写如下方法:
// 持久化对象具有自动更新数据库的能力
@Test
public void test2() {
// 1.得到session
Session session = HibernateUtil.getSession();
session.beginTransaction();
Customer customer = session.get(Customer.class, 1); // 查询id=1的Customer对象,如果查询到,会将Customer对象存到一级缓存中
customer.setName("Tom"); // 操作持久化对象来修改属性
// 2.事务提交,并关闭session
session.getTransaction().commit();
session.close();
}
测试以上test2方法,将发现数据库t_customer表中,id为1的那条记录的name字段变为Tom,这足以说明持久化对象具有自动更新数据库的能力了。但是为什么持久化对象具有自动更新数据库的能力呢?原因涉及到一个概念——快照,快照就是当前一级缓存里面对象的散装数据(对象的属性,如name、id……)。当执行完以下这句代码:
Customer customer = session.get(Customer.class, 1);
就会向一级缓存中存储数据,一级缓存其底层使用了一个Map集合来存储,Map的key存储的是一级缓存对象,而value存储的是快照。通过在这句代码上打个断点,然后以debug的方式运行,Watch一下session会看得更加清楚,如下:
接着执行以下这句代码:
customer.setName("Tom");
执行完毕会修改一级缓存中的数据,如下:
3 一级缓存常用API
一级缓存特点:
- 当我们通过session的save、update、saveOrUpdate方法进行操作时,如果一级缓存中没有对象,那么会从数据库中查询到这些对象,并存储到一级缓存中。
- 当我们通过session的load、get、Query的list等方法进行操作时,会先判断一级缓存中是否存在数据,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
- 当调用session的close方法时,session缓存将清空。
一级缓存常用的API:
- clear():清空一级缓存。
- evict():清空一级缓存中指定的某个对象。
- refresh():重新查询数据库,用数据库中的信息来更新一级缓存与快照区。
现在我举例来演示一级缓存常用的API。在Demo单元测试类中编写如下方法:
// 测试一级缓存操作常用的API
@Test
public void test3() {
// 1.得到session
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 操作
List<Customer> list = session.createQuery("from Customer").list(); // 这个操作会存储数据到一级缓存
session.clear(); // 清空一级缓存
Customer c = session.get(Customer.class, 1); // 会先从session的一级缓存中获取,如果不存在,才会从数据库里面获取
session.evict(c); // 从一级缓存中删除一个指定的对象
Customer cc = session.get(Customer.class, 1);
cc.setName("kkkk");
session.refresh(cc); // refresh方法的作用是:它会用数据库里面的数据来同步我们的一级缓存以及快照区,
// 这样的话,再操作cc时,就不会发送update语句。
// refresh方法:重新查询数据库,用数据库中的信息来更新一级缓存与快照区
// 2.事务提交,并关闭session
session.getTransaction().commit();
session.close();
}
可通过debug方式运行,这样会看得更清楚。在此不做过多赘述,读者自行操作。
热门文章
优秀文章