浅谈Hibernate缓存机制:一级缓存二级缓存
Posted VipMao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈Hibernate缓存机制:一级缓存二级缓存相关的知识,希望对你有一定的参考价值。
一:什么是缓存机制
当我们频繁访问数据库时,尤其像Hibernate持久层框架,会导致数据库访问性能降低,因此我们期望有一种机制能提供一个"缓存空间",我们将需要的数据复制到这个"缓存空间",当数据查询时,我们先在这个"缓存空间"里找,如果没有,我们再去数据库查找,这样就减少了与数据库的访问,从而提高了数据库访问性能,这就是缓存机制。
二:Hibernate缓存机制
1:一级缓存:Hibernate默认的缓存机制,它属于Session级别的缓存机制,也就是说Session关闭,缓存数据消失。
2:二级缓存:属于SessionFactory级别的缓存,二级缓存是全局性的,应用中的所有Session都共享这个二级缓存。
二级缓存默认是关闭的,一旦开启,当我们需要查询数据时,会先在一级缓存查询,没有,去二级缓存,还没有,好,咱们再去数据库,因此缓存机制大大提高了数据库的访问性能。
三:一级缓存用法
当程序调用Session的save()方法持久化对象时,程序并不会立刻将这个数据搞到数据库,而是将它放在了Session的一级缓存中,Session的get()、load()方法也是,当我们调用Session的flush()时,数据才会一并存到数据库。
下面例子演示一级缓存用法:
1:持久化类 News.java
@Entity
@Table(name="new_inf")
public class News {
@Id
@Column(name="new_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String title;
private String content;
//省略set、get方法
2:数据库表 new_inf
3:主程序 NewManager.java
public class NewManager {
public static void main(String[]args){
NewManager newManager=new NewManager();
newManager.secondCache();
}
public void secondCache(){
//获取Session
Session session=HibernateUtil.currentSession();
//开启事务
Transaction tx=session.beginTransaction();
//获取数据
List list=session.createQuery("from News news")
.list();
//将数据放入session缓存即一级缓存
News news =(News) session.load(News.class, 2);
System.out.println(news.getTitle()+"\\t"+news.getContent());
tx.commit();
System.out.println("-------------------");
//开启一个新的事务
tx=session.beginTransaction();
//从一级缓存中获取数据
News newse=(News) session.load(News.class, 3);
System.out.println(newse.getTitle()+"\\t"+newse.getContent());
tx.commit();
}
}
4:控制台输出结果
Hibernate:
select
news0_.new_id as new_id1_0_,
news0_.content as content2_0_,
news0_.title as title3_0_
from
new_inf news0_
php PHP是世界上最好的语言
-------------------
C++ C++表示不服
可以看到控制台仅仅输出了一条sql语句,也就是仅与数据库进行了一次交互,这是为什么?原因就是我们前面获取数据,然后将数据放入了一级缓存中,然后我们在后面查询id=3的数据时直接在一级缓存中查询就可以了。
前面说到,一级缓存是Session缓存,session一关闭数据就没了,真的吗?咱们试试
咱们将主程序NewManager.java改为:
public class NewManager {
public static void main(String[]args){
NewManager newManager=new NewManager();
newManager.secondCache();
}
public void secondCache(){
//获取Session
Session session=HibernateUtil.currentSession();
//开启事务
Transaction tx=session.beginTransaction();
//获取数据
List list=session.createQuery("from News news")
.list();
//将数据放入session缓存即一级缓存
News news =(News) session.load(News.class, 2);
System.out.println(news.getTitle()+"\\t"+news.getContent());
tx.commit();
//关闭Session 意味着一级缓存内的数据消失
HibernateUtil.closeSession();
System.out.println("-----------------------------");
//获取Session
session=HibernateUtil.currentSession();
//开启一个新的事务
tx=session.beginTransaction();
//这里是在数据库查询数据而不是一级缓存中
News newse=(News) session.load(News.class, 3);
System.out.println(newse.getTitle()+"\\t"+newse.getContent());
tx.commit();
}
}
控制台结果是这样的:
Hibernate:
select
news0_.new_id as new_id1_0_,
news0_.content as content2_0_,
news0_.title as title3_0_
from
new_inf news0_
PHP PHP是世界上最好的语言
-----------------------------
Hibernate:
select
news0_.new_id as new_id1_0_0_,
news0_.content as content2_0_0_,
news0_.title as title3_0_0_
from
new_inf news0_
where
news0_.new_id=?
C++ C++表示不服
上面这个主程序我们关闭了Session,一级缓存内的数据将会消失,所以查询时将会去数据库查询,也就输出了两条SQL语句,这就是为什么叫一级缓存为Session查询。
一级缓存中常用其他方法:
session.evit(Object obj) 将指定的持久化对象从一级缓存中清除,释放所占用的内存资源,该对象从持久化状态变为脱管状态,从而成为游离对象
session.clear() 将一级缓存中的所有持久化对象清除,释放其占用的内存资源。
session.contains(Object obj) 判断指定的对象是否存在于一级缓存中。
session.flush() 刷新一级缓存区的内容,使之与数据库数据保持同步。
那我们可不可以关闭Session的同时也从缓存内查询数据呢?答案是可以的,通过下面的二级缓存,也就是SessionFactory级别的缓存。
四:二级缓存
使用二级缓存步骤:
1:在配置文件hibernate.cfg.xml中开启二级缓存并设置缓存实现类如下:
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 设置缓存区的实现类,类型为内存、磁盘、事务性、支持集群 -->
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
<!-- 二级缓存配置文件的位置 -->
<property name="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>
<!-- 指定根据当前线程来界定上下文相关Session -->
<property name="hibernate.current_session_context_class">thread</property>
上面的EhCacheRegionFactory就是Hibernate常用的缓存实现类
2:将相应的缓存Jar文件添加到类加载路径
这个我们在下载好的hibernate的lib->optional->ehcache下可以找到,然后导入Jar文件。
3:添加ehcache.xml配置文件到类加载路径下 如下:
<!-- ehcache.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--
缓存到硬盘的路径
-->
<diskStore path="d:/ehcache"></diskStore>
<!--
默认设置
maxElementsInMemory : 在內存中最大緩存的对象数量。
eternal : 缓存的对象是否永远不变。
timeToIdleSeconds :可以操作对象的时间。
timeToLiveSeconds :缓存中对象的生命周期,时间到后查询数据会从数据库中读取。
overflowToDisk :内存满了,是否要缓存到硬盘。
-->
<defaultCache maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
<!--
指定缓存的对象。
下面出现的的属性覆盖上面出现的,没出现的继承上面的。
-->
<cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
</ehcache>
4:通过使用@Cache注解修饰需要启用二级缓存的实体类、实体的那些集合属性。如下
@Entity
@Table(name="new_inf")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class News {
@Id
@Column(name="new_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String title;
private String content;
二级缓存的使用策略一般有这几种:read-only、nonstrict-read-write、read-write、transactional。注意:我们通常使用二级缓存都是将其配置成 read-only ,即我们应当在那些不需要进行修改的实体类上使用二级缓存,否则如果对缓存进行读写的话,性能会变差,这样设置缓存就失去了意义。
持久化类:News.java
@Entity
@Table(name="new_inf")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class News {
@Id
@Column(name="new_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String title;
private String content;
//省略所有set、get方法
配置文件 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskPersistent="false"/>
</ehcache>
主程序 NewManager.java
public class NewManager {
public static void main(String[]args){
NewManager newManager=new NewManager();
newManager.secondCache();
}
public void secondCache(){
//获取Session
Session session=HibernateUtil.currentSession();
//开启事务
Transaction tx=session.beginTransaction();
//获取数据
List list=session.createQuery("from News news")
.list();
//通过id查找数据
News news =(News) session.load(News.class, 2);
System.out.println(news.getTitle()+"\\t"+news.getContent());
tx.commit();
//关闭Session
HibernateUtil.closeSession();
System.out.println("-----------关闭Session,启用二级缓存查询------------");
session=HibernateUtil.currentSession();
session.beginTransaction();
//通过二级缓存查询数据
News news2 =(News) session.load(News.class, 3);
System.out.println(news2.getTitle()+"\\t"+news2.getContent());
}
}
这里值得说的是,咱们在主程序中启用二级缓存时第二次Session对象和第一次Session对象都是同一对象,但是我们得明白,一级缓存是局部缓存仅对当前Session有效,但是二级缓存是全局缓存对所有的Session有效,因此在启用二级缓存时,即使我们用的是新建Session session2,程序依然可以在二级缓存中查询数据。
控制台输出结果:
Hibernate:
select
news0_.new_id as new_id1_0_,
news0_.content as content2_0_,
news0_.title as title3_0_
from
new_inf news0_
PHP PHP是世界上最好的语言
-----------关闭Session,启用二级缓存查询------------
C++ C++表示不服
可以看到虽然我们关闭了Session,但是我们启用了二级缓存,二级缓存属于SessionFactory缓存,它不会因为Session的关闭而丢失数据,因此我们依然可以通过二级缓存查询数据,如数据结果所示,第二次查询并没有同数据库交互,而是直接从二级缓存中获取数据。
五:总结
什么样的数据适合存放到第二级缓存中?
1) 很少被修改的数据
2) 不是很重要的数据,允许出现偶尔并发的数据
3) 不会被并发访问的数据
4) 常量数据
不适合存放到第二级缓存的数据?
1) 经常被修改的数据
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3) 与其他应用共享的数据。
无论是一级缓存还是二级缓存,都是对整个实体进行缓存,而不是缓存的实体属性,像上面的例子,我们是缓存的News这个实体,最后我们可以从缓存中取得实体的各个属性,如果想对普通属性进行缓存,比如缓存Nwes实体的属性new.title则可以使用查询缓存。
自己学习过程中的心得整理一下同大家分享,共同进步。
以上是关于浅谈Hibernate缓存机制:一级缓存二级缓存的主要内容,如果未能解决你的问题,请参考以下文章