HIbernate缓存机制

需求:

   A查询数据库中id为1的雇员信息,B接着也查询id为1的雇员信息,我们知道数据库查询的成本是很高的,考虑: A查询后, B再查询的时候直接使用A查询出的结果,我们可以用缓存的方式实现,即是把A查询的结果保存在某个地方,当B查询的时候先去找这个地方,如果找到了直接返回,如果没找到再去数据库查询,

使用缓存的条件:

            1,读取大于修改

            2,数据量不能超过内存容量

            3,数据要有独享的控制, (已存在缓存,不能另一边直接修改数据)

            4,能容忍一定的无效数据

缓存可以简单的看成一个Map,通过key在缓存里面找value,下面写一个简单示例来说明原理。

public class Main {

/**模拟缓存存放池*/

private static Map  map = new HashMap();

public static void main(String[] args) {

/**第一次查询*/

     Emp emp1=getEmpById(7788L);

System.out.println("employee name:"+emp1.getEname());

/**第二次查询*/

Emp emp2=getEmpById(7788L);

System.out.println("employee name:"+emp2.getEname());

}

public static Emp getEmpById(Long id) {

Session session = null;

Transaction tx = null;

Emp emp = null;

try {

/**产生标识某emp对象的key*/

String empKey=Emp.class.getName() + id;

/**在Map里取出这个对象*/

emp=(Emp)map.get(empKey);

/**判断是否取到*/

if(emp!=null)

/**取到对象后返回,这里就不用再次查询数据库了*/

  return emp;

/**如果没有取到*/

session = HibernateSessionFactory.getSession();

/**开始事务*/

tx = session.beginTransaction();

/**到数据库里查询*/

emp = (Emp) session.get(Emp.class, id);

/**保存到Map里,下次查询就不需要再次到数据库里查询了*/

map.put(empKey, emp);

tx.commit();

catch (Exception ex) 

{   if(tx!=null)

tx.rollback();

ex.printStackTrace();

finally {

if(session!=null)

session.close();

}

/**返回结果*/

return emp;

}

}

执行两次查询可发现只执行了一次数据库查询

Hibernate: 

    select

        emp0_.EMPNO as EMPNO1_0_,

        .....

    from

        SCOTT.EMP emp0_ 

    where

        emp0_.EMPNO=?

employee name:SCOTT

employee name:SCOTT

在上面演示原理的例子中,可能你会想到如果第二次查询的时候数据库的数据被更新了,那么查询出来的数据就是无效数据了,是的,但是在Hibernate内部已经为我们解决了这个问题,在更新的时候也去更新了缓存,一级缓存是这样,二级缓存表现得更明显..

因此我们按以下的操作只会有一条查询语句输出,这是一级缓存的用法

session = HibernateSessionFactory.getSession();

tx = session.beginTransaction();

emp = (Emp) session.get(Emp.class, id);

emp = (Emp) session.get(Emp.class, id);

tx.commit();

hibernate的session提供了一级缓存,save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放入一级缓存中,evict,clear方法用来清除缓存,每个session,对同一个id进行两次load或get,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了,一级缓存是在一个session有效,由此不能满足我们开始提到的需求.

二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,(Hibernate不自己实现缓存功能,它交给第三方类库实现),比如ehcache、oscache等,

  首先加入对应的jar包,这里使用oscache-2.1.jar,还要加入ehcache.xml配置文件,完整的  

  hibernate包下有这个文件

 maxElementsInMemory="10000" <!-- 缓存最大数目 --> 

  eternal="false" <!-- 缓存是否持久 -->  

 overflowToDisk="true"<!-- 是否保存到磁盘,当系统当机时--> 

 timeToIdleSeconds="300" <!-- 当缓存闲置n秒后销毁 --> 

 timeToLiveSeconds="180" <!-- 当缓存存活n秒后销毁-->  

我们在hibernate.properties中找到这样一段,这里即是实现缓存支持的第三方类库选项

#hibernate.cache.use_structured_entries true

## choose a cache implementation

#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider

#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider

hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider

#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider

#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider

#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider

一般我们使用oscache,接下来开始配置使用,修改hibernate.cfg.xml

<mapping resource="com/sxy/dao/Dept.hbm.xml" />

<mapping resource="com/sxy/dao/Emp.hbm.xml" />

<!-- 打开二级缓存-->

<property name="cache.use_second_level_cache">true</property>

<!-- 配置使用哪一个缓存 -->

<property name="cache.provider_class">

org.hibernate.cache.OSCacheProvider

</property>

<!--需要缓存的类,可添加很多个,注意:这句必须放在mapping的下面 -->

 <class-cache class="com.sxy.dao.Emp" usage="read-write"/>

配置需要缓存的类,还有个方法,在类的映射文件里配置

<hibernate-mapping>

    <class name="com.sxy.dao.Emp" table="EMP" schema="SCOTT" >

        <cache usage="read-write"/>

     <id name="empno" type="java.lang.Long">

        ......

usage的取值:

 read-only  只读的,不可修改,不需要更新缓存,效率高

 read-writ  读写,能保证并发修改的正确性,大多时候采用的,因此效率有一定影响

 nonstrict-read-write 非严格读写,并发修改可能性很小,对出现错误可以忍受,

 Transactional 很少使用

下面我们来看一下效果,执行以下代码,可以发现只输出了一条查询语句.

这两次查询明显不是在一个Session内执行的,第一次查询后session已关闭,

public static void main(String[] args) {

        /**第一次查询*/

getEmpById(7788L);

       /**第次查询*/

getEmpById(7788L);

}

public static Emp getEmpById(Long id) {

Session session = null;

Transaction tx = null;

Emp emp = null;

try {

session = HibernateSessionFactory.getSession();

tx = session.beginTransaction();

emp = (Emp) session.get(Emp.class, id);

tx.commit();

catch (Exception ex) 

{   if(tx!=null)

tx.rollback();

ex.printStackTrace();

finally {

if(session!=null)

session.close();

}

return emp;

}

设置产生统计信息,监控内部信息

<property name="generate_statistics">true</property>

使用方法

public static void main(String[] args) {

     /**第一次查询*/

getEmpById(7788L);

     /**第次查询*/

getEmpById(7788L);

SessionFactory sf=

HibernateSessionFactory.getSessionFactory();

/**获得统计信息,里面包含了很多内部信息*/

Statistics st=sf.getStatistics();

System.out.println("加入次数:"+st.getSecondLevelCachePutCount());

System.out.println("命中次数:"+st.getSecondLevelCacheHitCount());

System.out.println("错过次数:"+st.getSecondLevelCacheMissCount());

}

输出:

加入次数:1   //第一次查询缓存里没有,往里面加入一次

命中次数:1   //第二次查询缓存里已经存在,命中一次

错过次数:1   //第一次查询在缓存里没找到,错过一次

对象被加入到了二级缓存,但也加入到了一级缓存,只是一级缓存的生命周期太短无法利用

当使用save方法时,native的主键生成器不会把对象加入到二级缓存中,SessionFactory类提供了一些方法,用于清理缓存。

默认情况下 query查询不会利用缓存,也就是说它不到缓存里去找,而是直接访问数据库,因为这样的查询命中率是很低的,如果需要实现可以通过下面的方式

修改hibernate.cfg.xml

<property name="cache.use_query_cache">true</property>

public static void main(String[] args)

{

search();

search();

}

public static void search() {

Session session = null;

Transaction tx = null;

try {

session = HibernateSessionFactory.getSession();

tx = session.beginTransaction();

Query query=session.createQuery("from Emp");

/**设置支持查询缓存*/

query.setCacheable(true);

List<Emp> list=query.list();

for(Emp e:list)

System.out.println(e.getEname());

tx.commit();

finally {

if (session != null)

session.close();

}

}

如果不设置“查询缓存”,那么hibernate只会缓存使用load()方法获得的单个持久化对象,如果想缓存使用findall()、list()、Iterator()、createCriteria()、createQuery()等方法获得的数据结果集的话,就需要设置hibernate.cache.use_query_cache true才行。

                                         

                                                                          ©石头

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值