Hibernate缓存策略之一级缓存

本文深入探讨Hibernate中的一级缓存机制,包括如何通过load、get、list和iterate等操作加载和查询实体对象,并将它们放入一级缓存。此外,还讨论了一级缓存的管理和清理方法,以及解决关联丢失的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

缓存相当于Map结构,讲的是命中率,就像Entryset中的key和Value。

 

Hibernate中的缓存:

  1. 一级缓存,也叫session级别的缓存,缓存的是实体
  2. 二级缓存,是SessionFactory级别的缓存,缓存的也是实体
  3. 查询缓存,也是SessionFactory级别的缓存,它缓存的是普通结果集,但如果缓存的是实体则缓存实体的Id列表

session级别的缓存即一级缓存,不能被取消,它一直存在,随着session的关闭,缓存也会随之失效!sessionFactory级别的缓存是可以取消的,甚至不用

 

    现在让我们先来看看session级别的缓存,也就是一级缓存:

  • 可以通过session的load/get操作加载实体对象,通过session的list/iterate查询实体对象,这四种操作都能把实体对象放入一级缓存

如:

 

//将发出SQL查询语句查询实体对象的数据
Person p1 = (Person)session.load(Person.class, 1);
System.out.println(p1.getName());
           
 //实体对象已经被缓存,所以下列操作不再发出SQL语句
Person p2 = (Person)session.load(Person.class, 1);
System.out.println(p2.getName());
 

 

         因此可以这样理解,持久化对象都在一级缓存里边,如果session关闭之后,那么当前session对象中的一级缓存已经被清空 ,这时候你再启动一个session的话,他还是会发送sql语句到内存中查找那个实体,因为当前的session对象已被一级缓存给清空了。

       接着看看list和iterate操作:他们都能够缓存实体到一级缓存中

			//下面这个操作,会导致hibernate发出SQL语句查询两个实体对象到一级缓存中
List persons = session.createQuery(
                    "select p from Person p where p.id in (1,2)")	.list();
for (Iterator iterator = persons.iterator(); iterator.hasNext();) {
				Person p = (Person ) iterator.next();
				System.out.println(p.getName());
			}
			
			//不会再发出查询语句
			Person cp = (Person )session.load(Person .class, 1);
			System.out.println(cp.getName());
			
			//实体对象已经被缓存,所以下列操作不再发出SQL语句
			Person cp1 = (Person )session.get(Person .class, 1);
			System.out.println(cp1.getName());
			
			//实体对象已经被缓存,所以下列操作不再发出SQL语句
			Person cp2 = (Person )session.get(Person .class, 2);
			System.out.println(cp2.getName());
			
			//因为本实体对象不在一级缓存中,所以下面将发出SQL语句查询实体对象
			Person cp3 = (Person )session.get(Person .class, 3);
			System.out.println(cp3.getName());

  具体的list和iterate区别见1+N文章!

 

  • 接下来研究下一级缓存的管理:

清空一级缓存,表示这个对象不再是持久化对象了,他应该是个离线的对象。

session.clear()清空所有的一级缓存,session.evict()清楚一级缓存中某个实体缓存。

Person p = (Person)session.get(Person.class, 4);
p.setName("士兵乙");
//清空一级缓存里面的所有的持久化对象!
//session.clear();
//清楚一级缓存里面的某个持久化对象!
session.evict(p);
session.getTransaction().commit();
  1.  再看看下面更新关联丢失的解决之道:
//这是一个离线对象,假设这个对象从呈现层中传过来的!
Person p = new Person();
p.setId(1);
p.setQq("1234567");
p.setAddress("上海!");
p.setName("xxxxx");
//假设为了不丢失Person与Group对象之间的关联,我们先加载原来的Person
Person oldp = (Person)session.get(Person.class, p.getId());
//把原来的关联关系,设置到新的对象中
p.setGroup(oldp.getGroup());
//更新新的对象!
//update方法会把p对象放入一级缓存
//上面的get操作,把oldp对象也放入了一级缓存
//p对象和oldp对象,不是同一个对象
//但p对象和oldp对象具有相同的数据库标识!
//所以下面的update操作,将抛出异常:org.hibernate.NonUniqueObjectException


: a different object 
//with the same identifier value was already associated with the session翻译过来如下:
//因为在Hibernate中,同一个session中,不允许存在两个具有相同数据库标识的同一种类型的实体对象




//session.update(p);
session.getTransaction().commit();

 那应该怎么解决呢?方法一:

//解决上述问题的方法之一:就是
//利用session一级缓存的管理机制,先把oldp对象从一级缓存中清除
session.evict(oldp);
session.update(p);

 解决它的第二个办法:

//解决上述问题的方法之二:
//利用session中的merge方法来更新p对象!
//merge方法,相当于把某个离线状态的对象拷贝到某个对应的持久化对象中
//相当于把p拷贝到oldP中,从而更新oldP中所有的属性
session.merge(p);
  1.   在批量插入数据时如有100万条数据时,这时不可能一次性把这么多数据全部持久化到数据库中,我们应当批量地插入,如100条的插入,当插入了100条后,即全部写进去后,也就是flush干净 了,这时候把一级缓存清空一下以便下一批数据的批量插入,从而保证缓存的可用,而不会导致一次性插入大量数据致使内存溢出的情况
for(int i=0; i<1000000000; i++){
	Person p = new Person();
	p.setAddress("xxx");
	//...p.setXXX
	session.save(p);
	if(i % 100 == 0){
		session.flush();
		session.clear();
	}
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值