当前位置:网站首页>Hibernate 的 Session 缓存相关操作
Hibernate 的 Session 缓存相关操作
2022-08-11 07:05:00 【血莲丹】
简介
Session 是 Hibernate 中最重要的接口,它提供了基本的保存、更新、删除和加载方法,是加载而不是查询,将对象加载到 session 缓存中。而查询是通过 Query 和 Criteria 两个对象来做的,但是也是通过 session 对象创建的,因此 session 很重要,是 Hibernate 的核心。
Session 缓存
先来看下 session 的缓存。再 Mybatis 中有一级二级缓存,同样的,Hibernate 也有。
先来加载两次相同的记录。
@Test
public void test2() {
SessionFactory sessionFactory;
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
News news1 = (News) session.get(News.class, 1);
System.out.println(news1);
News news2 = (News) session.get(News.class, 1);
System.out.println(news2);
transaction.commit();
session.close();
sessionFactory.close();
}
查看日志:
这就是基于 session 的一级缓存。除了我们创建的 news 变量引用这内存中的对象,session 缓存中也持有一份对象。当再次查询该对象时,会先去 session 缓存中查询有没有,有的话让新引用指向缓存中的对象。二级对象后面再说。
操作 session 缓存
session 操作缓存主要有如下的方法。他们之间的关系如下图所示。
flush() 方法
在讲 flush 方法之前,先对测试类进行一下改造,不然每次都要打那么多固定的代码,利用 JUnit 的生命周期方法能有效的减少冗余。
package com.zxb.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest2 {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@Test
public void test() {
}
@After
public void destroy() {
transaction.commit();
session.close();
sessionFactory.close();
}
}
注:以后所有代码都基于上述代码结构。
现在来看如下的代码:
@Test
public void test() {
News news = (News) session.get(News.class, 1);
news.setTitle("Java");
}
执行上述代码前,先看下数据库中的记录。
此时再来执行上述代码。
可以看到,除了发送 SELECT 语句之外,还发送了 UPDATE 语句。数据库中的记录也发生了更改,这是为什么?
因为在 session 的 commit() 方法中,在调用事务的 commit() 方法前,先调用了 flush() 方法,然后再去 commit 事务的。
这个 flush() 方法的作用就是使表中的记录和 session 缓存中的记录保持一致,如果不一致,就会发送 SQL 去修改表中的记录。但是 单一的调用 flush() 方法是不会修改表中记录的,因为没有提交事务。
flush() 方法是让数据库表中的记录和 session 缓存中的对象保持一致,缓存中的对象是爷。
也可以打断点看看,当执行到 session.commit() 后才会发送 SQL。
但是除了显示的调用 session.flush() 方法和提交事务之外,还有如下场景也会在方法内部调用 flush() 方法。
1、 执行 HQL 或者 QBC 查询时,要求查询的对象必须是最新的状态,因此会先 flush() 一下,让数据库表中记录保持最新。
@Test
public void test3() {
News news = (News) session.get(News.class, 1);
// 在 QBC 查询前将缓存中记录修改
news.setAuthor("oracle");
Criteria criteria = session.createCriteria(News.class);
News news2 = (News) criteria.add(Restrictions.eq("id", 1)).uniqueResult();
System.out.println(news2);
}
可以在输出这行打个断点,此时查看控制台,发现已经调用 flush() 方法,发送了 UPDATE 语句,而不是在 commit 时调用。
2、 如果数据库表中的记录使用 native 生成器生成 OID(Object ID),那么当调用 session 的 save() 方法时内部也会调用 flush() 方法,立即发送 INSERT 语句向数据库插入该实体,因为只有这样 Hibernate 才能拿到记录的 ID,后面才能对该记录进行操作。所以说对象 ID 很重要,实体类中一定要有 ID 字段。
@Test
public void test4() {
News news = new News("C++", "ZXB", new Date());
session.save(news);
System.out.println("save...");
}
可以在输出语句这里打个断点,查看控制台会发现发送了一条插入语句,让数据库中和缓存中保持一致。这里也是调用 save() 方法时就调用了 flush() 方法,并非在 commit 才调用。
小结:
- flush() :根据缓存中的对象属性变化来同步更新数据库。
- 默认情况下,Session 在以下时间点 flush 缓存
– 显示调用 flush() 方法。
– 事务提交时,先调用 flush() 方法,再提交事务。
– 执行 HQL、QBC 查询时,如果缓存中的对象发生了变化,会先 flush(),保证查询的结果和缓存中的对象保持一致。
– 当对象使用 native 策略生成主键时,调用 session.save() 方法时,会立即 flush(),发送一条 INSERT 语句,使得数据库表中和缓存一致。 - commit() 和 flush() 方法的区别:flush() 方法会执行一系列 sql,但是不会提交事务。commit() 方法内部会先调用 flush() 方法,随后再提交事务。
refresh() 方法
根据 session 缓存的图示来看,refresh() 方法箭头是由数据库指向 session 缓存,所以也能猜到,refresh() 方法就是让缓存中的对象和数据库中的记录保持一致。
refresh() 方法是 session 缓存中的对象和数据库表中的记录保持一致,数据库表中的记录是爷。
举个例子:
@Test
public void test5() {
News news = (News) session.get(News.class,1);
System.out.println(news); // 断点
session.refresh(news);
System.out.println(news);
}
上述代码在第一次输出 news 时打个断点,当程序执行到断点处,我们手动修改数据库表中的记录,然后执行 refresh,看看第二次输出的 news 和第一次是否一致。
通过控制台的输出发现,两次打印结果是一致的,但是的确是发送了两次 SQL,这是 refresh 起效果了,不然有缓存,只会发送一次。这其实和 MySQL 的隔离级别有关,默认是可重复读,如果想要看到效果,可以在 hibernate.cfg.xml 中修改隔离级别。
<!-- 修改 Hibernate 的事务隔离级别,1,2,4,8 分别从低到高对应四种隔离界别 -->
<property name="hibernate.connection.isolation">2</property>
修改过后,再去执行,就能看到效果了。
clear() 方法
clear 方法就是清除 session 中的缓存,这个没啥好说的。
@Test
public void test6() {
News news1 = (News) session.get(News.class, 1);
session.clear();
News news2 = (News) session.get(News.class, 1);
}
此时发送两条查询语句,因为缓存已被清空。
边栏推荐
- 分布式锁-Redission - 缓存一致性解决
- 测试用例很难?有手就行
- TF generates (feature, label) set through feature and label, tf.data.Dataset.from_tensor_slices
- 1.1-回归
- Find the latest staff salary and the last staff salary changes
- Pico neo3在Unity中的交互操作
- 国密规范 SM2 SM3 SM4
- Project 1 - PM2.5 Forecast
- Pico neo3 Unity打包设置
- Square, multi-power, square root calculation in Tf
猜你喜欢
随机推荐
项目2-年收入判断
tf.cast(), reduce_min(), reduce_max()
3.2 - classification - Logistic regression
线程交替输出(你能想出几种方法)
1076 Wifi Password (15 points)
易观分析联合中小银行联盟发布海南数字经济指数,敬请期待!
CIKM 2022 AnalytiCup Competition: 联邦异质任务学习
【LeetCode每日一题】——844.比较含退格的字符串
Internet phone software or consolidation of attack must be "free" calls security clearance
The growth path of a 40W test engineer with an annual salary, which stage are you in?
我的创作纪念日丨感恩这365天来有你相伴,不忘初心,各自精彩
年薪40W测试工程师成长之路,你在哪个阶段?
2022-08-09 Group 4 Self-cultivation class study notes (every day)
What are the things that should be planned from the beginning when developing a project with Unity?How to avoid a huge pit in the later stage?
1.2-误差来源
结合均线分析k线图的基本知识
linux 安装mysql服务报错
Four states of Activity
When MySQL uses GROUP BY to group the query, the SELECT query field contains non-grouping fields
【latex异常和错误】Missing $ inserted.<inserted text>You can‘t use \spacefactor in math mode.输出文本要注意特殊字符的转义