Hibernate笔记 4:核心知识:Session缓存

来自Wikioe
Eijux讨论 | 贡献2022年6月14日 (二) 18:28的版本 (创建页面,内容为“category:Hibernate == 关于 == Session 缓存:是一块内存空间,用来存放相互管理的对象。 作用: # 减少访问数据库的频率。【缓存查询数据】 # 保证缓存中的对象与数据库中的相关记录保持同步。【持久态自动更新】 === 缓存查询数据 === 在使用 Hibernate 查询对象的时候,首先会使用对象属性的 <span style="color: blue">'''OID'''</span> 值在 Hibernate 的一级缓存…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索


关于

Session 缓存:是一块内存空间,用来存放相互管理的对象。

作用:

  1. 减少访问数据库的频率。【缓存查询数据】
  2. 保证缓存中的对象与数据库中的相关记录保持同步。【持久态自动更新】

缓存查询数据

在使用 Hibernate 查询对象的时候,首先会使用对象属性的 OID 值在 Hibernate 的一级缓存中查找:
1、如果找到匹配 OID 值的对象:就直接将该对象从“一级缓存”中取出使用,不会再查询数据库;
2、如果没有匹配 OID 值的对象:去数据库中查找相应数据,并将查询到的数据信息放置到“一级缓存”。

示例:

	@Test 
	// 测试 Hibernate 一级缓存的存在 
	public void demo() { 
		Session session = HibernateUtils.openSession(); 
		Transaction tx = session.beginTransaction(); 
	
		// 查询客户1:发出 sql 查询,并将结果放入一级缓存
		Customer customerl = session.get(Customer.class, 11);
		System.out.println(customerl); 

		// 查询客户1:从一级缓存中获取数据,而不再发出 sql 查询
		Customer customer2 = session.get(Customer.class, 11) ;
		System.out.println(customer2); 

		// 验证两次查询结果:true。(一级缓存的内容是对象的地址)
		System.out.println(customerl == customer2);
		
		tx.commit(); 
		session.close(); 
	}
从控制台输出(已配置 Hibernate 输出底层 sql 语句)可以发现,以上代码只发出了一次查询语句。

持久态自动更新

Hibernate 会依据 persistent 状态的实体对象的属性变化,而自动更新(无需 update 等操作)数据库中相对应的记录。


注意:在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而是发生在 Transaction 终止,执行 commit 之后。

示例:

	@Test 
	// 测试:持久态对象的自动更新
	public void demo() { 
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransac七ion(); 
		
		// 获得持久态对象.
		Customer customer = session.get(Customer.class, 11);
		// 修改持久态对象
		customer.setCust name("主五"); 
		
		// session.update(custorner);   // 不用手动调用 update 方法就可以更新
		
		tx.commit(); 
		session.close(); 
	}
如上,更改持久态对象属性之后,不调用 update 等方法,依旧可以更新数据库记录。
持久态的自动更新,依赖于 Hibernate 一级缓存的“脏检查和缓存清理机制”。

相关方法

Session 接口(及其他查询接口,如:Query)的相关方法会导致 Session 缓存的:增加、删除、清空。
  1. 增加
    1. 调用 Session 接口的 save()update()saveOrUpdate() 时:如果 Session 缓存中没有相应的对象,Hibernate 就会把从数据库中查询到的相应对象信息加入缓存。
    2. 调用 Session 接口的 load()get(),及 Query接口的 list()iterator() 时:如果 Session 缓存中有相应的对象,则直接取出并返回;否则,从数据库查询数据并放到缓存。
  2. 删除:调用 Session 的 evict() 方法时,会从 Session 缓存移除对应数据。
  3. 清空:调用 Session 的 clear()close() 方法时,会清空 Session 缓存。

脏检查机制(快照)

脏检查:当刷新缓存(缓存清理)时,Hibernate 会对 Session 中的持久状态的对象进行检测,判断对象的数据是否发生了改变,这种判断称为“脏检查”。


原理:快照
1、Hibernate 向“一级缓存”放入数据时,同时复制一份数据放入到 Hibernate “快照区”中。
2、持久化对象发生更改时,只会修改缓存中数据,并不会修改对象的快照,也不会直接更改到数据库。
3、Session刷新缓存时,通过 OID 判断 Session 缓存中的对象和快照中的对象是否一致:
  ——如果两个对象中的属性发生变化(脏对象),则执行 update 语句, 将缓存的内容同步到数据库,并更新快照。
  ——如果数据一致,则不执行 update 语句。


快照:相当于数据库数据的副本,确保缓存的数据与数据库一致

通常脏数据的检查有如下两种办法:

  1. 数据对象监控:通过“拦截器”对数据对象的 setter 方法进行监控来实现的。(类似于数据库中的触发器)
    当某一个对象的属性调用了 setter 方法而发生了改变,这时拦截器会捕获这个动作,并且将改属性标志为已经改变,在之后的数据库操作时将其更新到数据库中。
    • 特点:提高了数据更新的同步性。【实时更新】
      但,如果同一实体对象发生多次属性变化,将会造成大量拦截器回调方法的调用。这些拦截器都是通过 Dynamic Proxy 或者 CGLIB 实现的,在执行时都会付出一定的执行代价,所以有可能造成更新操作的较大延时。
  2. 数据版本比对:通过保存数据对象的最近读取版本来实现。
    在持久化框架中保存数据对象的最近读取版本,当提交数据时将提交的数据与这个保存的版本进行比对,如果发现发生了变化则将其同步跟新到数据库中。
    • 特点:降低了数据更新的同步性。【非实时更新】
      但,如果同一实体对象发生多次属性变化,由于持久层框架缓存的存在,比对版本时可以充分利用缓存,这反而减少了更新数据的延迟。
Hibernate 采用的是“数据版本比对”的方法来进行脏数据检查的。

缓存清理机制(缓存刷新机制)

缓存清理:Session 在某些时间点,对当前持久化状态的缓存数据进行检查(脏检查),并且按照缓存中持久态对象的属性变化来同步更新数据库的过程。

    ——【“缓存清理”并非“清空缓存”!!!可以理解为“(从缓存到数据库的)缓存刷新”】




清理时机

当 Session 缓存中对象的属性发生了变化,Session 并不会立即清理缓存和执行相关的 SQL update 语句,而是在特定的时间点才清理缓存,这使得 Session 能够把几条相关的 SQL 语句合并为一条 SQL 语句,以便减少访问数据库的次数,从而提高应用程序的数据访问性能。

默认清理时机

默认情况下(FlushMode.AUTO),Session 缓存清理的时机:(触发策略)

  1. 提交前:当调用 Transaction 的 commit() 方法时,commit() 方法先清理缓存(前提是:FlushMode.COMMIT/AUTO),然后再向数据库提交事务。
    • 之所以在事务快结束时:一方面是因为可以减少访问数据库的频率,另一方面是因为可以尽可能缩短当前事务对数据库中相关资源的锁定时间;
    • commit() 会隐式调用 flush() 方法。(通过 SessionIml 的 flushBeforeTransactionCompletion() 实现隐式调用)
  2. 查询前:当应用程序调用 Session 的 find() 或者 iterate() 时,如果缓存中的持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能反映持久化对象的最新状态。
  3. flush:当应用程序显示调用 Session 的 flush() 方法的时候。

如下图:

FlushMode Session.find()/iterate() Session.commit() Session.flush()
FlushMode.AUTO
FlushMode.COMMIT
FlushMode.NEVER
  • 清理(✔),不清理(✘)

指定清理时机