hibernate缓存机制(学习笔记)

本文详细介绍了Hibernate缓存机制,包括一级缓存与二级缓存的概念、用途、工作原理及使用方法。重点阐述了如何通过缓存减少数据库查询次数,提高系统性能,并提供了实例演示。此外,还讲解了缓存命中率的计算方法及如何通过Statistics检查缓存效果。

                            WWWH学习法理解

1.W----->What:是什么?(首先引入问题,该知识点的初始,这是一个什么技术?)

  Hibernate缓存机制分为一级缓存(session级缓存),二级缓存(sessionfactory)

缓存的作用主要用来提高性能,可以简单的理解成一个Map,使用缓存涉及到三个操作,把数据放入缓存,从缓存中获取数据,删除缓存中的无效数据。

 

2.W----->When:什么时候用?(接着利用该技术我们能够做什么?什么时候该用,什么时候不该用?)

· 一级缓存:

    hibernate内部实现,不用手动配置。使用evict(),clear()方法可以清除session缓存里的对象。

·  二级缓存:

因为一级缓存内存有限,而且生命周期在session中有效,很短。所以这时会按需要配置二级缓存(sessionFactory级缓存)。

 

3.H------->How:怎么用?(然后就是该技术的用法,我们应该注意什么小细节?)

·一级缓存(session缓存):

该技术是hibernate内部实现,我们只需要了解该技术的实现原理就可以了。无需手动实现改技术。

    A.什么时候向一级缓存中存放数据

save,update,saveOrUpdate,load,get,list,iterate,lock

这些方法会将对象放在一级缓存中,一级缓存不能控制缓存数量,所以要注意大批量操作数据可能造成内存溢出,可以使用evict,clear方法清除缓存中的内容(对象)。

  ·从hibernate缓存原理图,我们可以看出,当我们去查询对象的时候,首先到一级缓存取数据,如果有则不到数据库里面去取数据,同时在一级缓存中放入对象。

·一级缓存原理:

    当第一次查询一条记录的时候Student stu1=(Student)session.get(Student.class, 2);首先会导致hibernate到一级缓存(session缓存)里面去查找数据,有没有id为2的学生在一级缓存里面存在,如果hibernate找不到改条记录,hibernate会向数据库里面去查询。这时找到2号学生的信息了。当找到2号学生以后,则放入一级缓存。当你使用对象2,Student stu2=(Student) session.get(Student.class, 2);查询id为2的学生时,hibernate会向一级缓存里面去查找,如果hibernate在一级缓存里面找到了id为2的学生信息,hibernate就不会向数据库里面去查找了,前提session是open的。只有查询一条新的记录时,hibernate才会向数据库发出sql语句进行查询。这就算是hibernate的一级缓存机制。

·一级缓存原理图:

·一级缓存的细节:

什么操作向一级缓存存取数据。

save,update,saveOrUpdate,load,get,list,iterate,lock

·save操作向一级缓存存入数据:

Student s=new Student();

        s.setSaddress("成都");

        s.setSage(21);

        s.setSdept("化学系");

        s.setSname("小小");

        s.setSsex("M");

        session.save(s);(放入一级缓存)

       

        System.out.println("******************************");

        马上查询

        Studentss=(Student) session.get(Student.class, s.getSid());

        System.out.println(ss.getSname());

        ts.commit();

 

·控制台打印结果如下:

Hibernate:

   insert

   into

       studentclass.dbo.student

       (sname, ssex, sdept, sage, saddress)

   values

       (?, ?, ?, ?, ?)

******************************

小小

 

·理解:首先当save一个对象时hibernate会将该对象加入一级缓存(session级缓存),然后马上我有查询该对象,此时hibernate会向一级缓存里面查询数据,一旦发现该数据就不会向数据库发出sql语句查询了。当commit 的时候才向数据库保存该条记录。

B.什么操作会从一级缓存取出数据:

get load  list

·get/load:

  会首先从一级缓存中取数据,如果没有,再有不同的操作,get会立即向数据库发请求,而load会返回一个代理对象,知道用户真的使用数据时,才会向数据库发出请求。

·List:代码如下

Student s1=(Student)session.get(Student.class, 4);

        System.out.println(s1.getSname());

        System.out.println("------------------------------------------");

        //这里使用uniqueResult(),因为返回的是一条记录。

        Students2=(Student) session.createQuery("from Student wheresid=4").uniqueResult();

        System.out.println(s2.getSname());

 

控制台打印结果:

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

周星驰

------------------------------------------

Hibernate:

   select

       student0_.sid as sid1_,

       student0_.sname as sname1_,

       student0_.ssex as ssex1_,

       student0_.sdept as sdept1_,

       student0_.sage as sage1_,

       student0_.saddress as saddress1_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=4

周星驰

 

理解:由此可见query.list(),query.uniqueResult()不会从一级缓存中取出数据,但会放入数据。

·list会向一级缓存存放数据:

Student s2=(Student)session.createQuery("from Student where sid=4").uniqueResult();

        System.out.println(s2.getSname());

        System.out.println("------------------------------------------");

        Students1=(Student) session.get(Student.class, 4);

        System.out.println(s1.getSname());

控制台打印结果:

Hibernate:

    select

        student0_.sid as sid1_,

        student0_.sname as sname1_,

        student0_.ssex as ssex1_,

        student0_.sdept as sdept1_,

        student0_.sage as sage1_,

        student0_.saddress as saddress1_

    from

        studentclass.dbo.student student0_

    where

        student0_.sid=4

周星驰

------------------------------------------

周星驰

  理解:由此可见query.list(),query.uniqueResult()会向一级缓存存放数据。(因为控制台只打印了一条sql语句)

  总结:get,load即向一级缓存存入数据,又向一级缓存取出数据,list只会想一级缓存存入数据,而不会向一级缓存取出数据。

C.一级缓存无需配置就可以使用,一级缓存本身没有保护机制,所以需要程序员需要通过evict或者clear来清除一级缓存(session缓存)中的对象。

·evict:

    Studentstu1=(Student) session.get(Student.class, 2);

        System.out.println(stu1.getSname());

        session.evict(stu1);//这句话的意思是,在一级缓存中清除stu1这个对象

        System.out.println("*************************");

        Studentstu2=(Student) session.get(Student.class, 2);

        System.out.println(stu2.getSname());

·控制台打印结果:

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

林青霞

*************************

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

林青霞

理解:由此可见当使用session.evict()方法时,将会在一级缓存中清除一个对象。而session.clear()则会清除所以session缓存对象。

D.session缓存中对象的生命周期,当session关闭后,就自动销毁。

Session session=null;

        Transactionts=null;

       

        try{

            //获取一个新的session

            session=MySessionFactory.openSession();

            ts=session.beginTransaction();

            Studentstu1=(Student) session.get(Student.class, 7);

            System.out.println(stu1.getSname());

        }catch (Exception e) {

            if(ts!=null){

                ts.rollback();

            }

        }finally{

            if(session!=null&&session.isOpen()){

                session.close();

            }

        }

        System.out.println("------------------------------------------------");

       

        try{

            //获取一个新的session

            session=MySessionFactory.openSession();

            ts=session.beginTransaction();

            Studentstu2=(Student) session.get(Student.class, 7);

            System.out.println(stu2.getSname());

        }catch (Exception e) {

            if(ts!=null){

                ts.rollback();

            }

        }finally{

            if(session!=null&&session.isOpen()){

                session.close();

            }

        }

·控制台打印结果:

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

王天鑫

------------------------------------------------

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

王天鑫

理解:由此可见对象的生命周期,在session关闭时被销毁。

E.利用HashMap模式一个session缓存。加深理解。

·代码如下:

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

 

/**

 * 模拟缓存机制

 *@author Administrator

 *

 */

public class MyCache {

    //使用map模拟缓存

    staticMap<Integer, Student> map=new HashMap<Integer, Student>();

   

    publicstatic void main(String[] args) {

        getStudent(1);

        getStudent(1);

        getStudent(2);

        getStudent(3);

        getStudent(2);

    }

    publicstatic Student getStudent(Integer sid){

        Studentstu=null;

        //先到缓存取

        if(map.containsKey(sid)){

            //缓存中有

            System.out.println("从缓存中取出了!");

        }else{

            System.out.println("从数据库中取出!");

            //到数据库中取

             stu=Mydb.getStudentFormDB(sid);

            //放入到缓存中,这句话很重要,其重要是当到数据库中取出记录后马上就该记录放入缓存。          

            map.put(sid,stu);

        }

        returnstu;

    }

}

 

//db

class Mydb{

    staticList<Student> list=new ArrayList<Student>();

    //程序会首先执行静态块,而且只执行一次

    static{

        //假如有三个学生

        Studentstu1=new Student();

        stu1.setSid(1);

        stu1.setSname("aa");

        Studentstu2=new Student();

        stu2.setSid(2);

        stu2.setSname("bb");

        Studentstu3=new Student();

        stu3.setSid(3);

        stu3.setSname("cc");

        //将学生放入集合

        list.add(stu1);

        list.add(stu2);

        list.add(stu3);

    }

    //提供一个利用id查询一个学生的方法

    publicstatic Student getStudentFormDB(Integer sid){

        for(Studentstudent:list){

            if(student.getSid().equals(sid))

                returnstudent;

        }

        returnnull;//没有这个学生

    }

}

 

//domain

class Student{

    privateInteger sid;

    privateString sname;

    publicInteger getSid() {

        returnsid;

    }

    publicvoid setSid(Integer sid) {

        this.sid= sid;

    }

    publicString getSname() {

        returnsname;

    }

    publicvoid setSname(String sname) {

        this.sname= sname;

    }

}

·控制台打印结果:

 

从数据库中取出!

从缓存中取出了!

从数据库中取出!

从数据库中取出!

从缓存中取出了!

执行原理:首先会到map集合(缓存)中取,如果发现没有与之对应的记录,就会到数据库取出数据。第二次也是先到map集合中取出记录,发现有与之对应的记录,就不会到数据库去查询记录了。

·二级缓存(sessionFactory缓存):

A.为什么需要二级缓存(sessionFactory级缓存)?

  因为一级缓存内存有限,并且生命周期在一个session中有效(很短),所以需要二级缓存来弥补这个问题。

    ·需要配置二级缓存。

    ·二级缓存由第三方处理,常见的hashtable,OSCache,EHCache。

    ·二级缓存原理图:

由上图可见:当用户要查询一个对象的时候,先到一级缓存里面去获取,发现一级缓存里面没有要查询到该对象。就会到二级缓存里面去查询,这时发现二级缓存里面也没有查询到该对象,就会发出select语句向数据库里面去查询。此时查询到了结果。第二次继续查询该对象,到先一级缓存里面去查找数据,发现一级缓存evict,clear或者session是close的,所以在一级缓存里面没有查到该数据,就会到二级缓存里面去查询,因为二级缓存又叫sessionFactory级缓存,它的生命周期在sessionFactory里是有效的,所以此时就会查询到该对象了。

这就是hibernate的二级缓存机制(sessionFactory级缓存)。

B.二级缓存的对象可能放在内存,也可能放在磁盘。

·使用OSCache来演示二级缓存的使用:

    a.将OSCache-2.1.jar文件add build path进项目中。

b.配置二级缓存:

<!--    启用二级缓存-->

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

<!--    指定使用OSCache作为二级缓存-->

    <propertyname="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>

<!--    指定对那个domain对象启用二级缓存-->

    <class-cacheusage="read-write"class="com/wtx/domain/Student.hbm.xml"/>

·Hibernate二级缓存策略:

    ☞只读缓存(read-only)

    ☞读写缓存(read-write)【银行,财务软件】

    ☞不严格读写缓存(nonstrict-read-write)【bbs,被浏览了多少次,网站统计量】

    ☞事务缓存(transactional)

    ☞统计消息打开generate_statustucs;用户sessionFactory.getSatistics()获取统计信息。

案例演示:

Session session=null;

        Transactionts=null;

        try{

            //获取一个新的session

            session=MySessionFactory.openSession();

            ts=session.beginTransaction();

            Studentstu1=(Student) session.get(Student.class, 7);

            System.out.println(stu1.getSname());

        }catch (Exception e) {

            if(ts!=null){

                ts.rollback();

            }

        }finally{

            if(session!=null&&session.isOpen()){

                session.close();

            }

        }

        System.out.println("------------------------------------------------");

       

        try{

            //获取一个新的session

            session=MySessionFactory.openSession();

            ts=session.beginTransaction();

            Studentstu2=(Student) session.get(Student.class, 7);

            System.out.println(stu2.getSname());

        }catch (Exception e) {

            if(ts!=null){

                ts.rollback();

            }

        }finally{

            if(session!=null&&session.isOpen()){

                session.close();

            }

        }

控制台打印结果:

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

王天鑫

------------------------------------------------

王天鑫

 

理解:由此可见即便关闭了一级缓存(session级缓存),也只是向数据库发送了一条select语句,这就是hibernate的二级缓存机制。

C.二级缓存的命中率:

    案例演示:

Session session=null;

        Transactionts=null;

        try{

            //获取一个新的session

            session=MySessionFactory.openSession();

            ts=session.beginTransaction();

            Studentstu1=(Student) session.get(Student.class, 7);//1,1,0

            System.out.println(stu1.getSname());

        }catch (Exception e) {

            if(ts!=null){

                ts.rollback();

            }

        }finally{

            if(session!=null&&session.isOpen()){

                session.close();

            }

        }

        System.out.println("------------------------------------------------");

       

        try{

            //获取一个新的session

            session=MySessionFactory.openSession();

            ts=session.beginTransaction();

            Studentstu2=(Student) session.get(Student.class, 7);//1,1,1

            System.out.println(stu2.getSname());

            Studentstu3=(Student) session.get(Student.class, 8);//2,2,1

            System.out.println(stu3.getSname();

        }catch (Exception e) {

            if(ts!=null){

                ts.rollback();

            }

        }finally{

            if(session!=null&&session.isOpen()){

                session.close();

            }

        }

       

        //完成统计命中率,sessionFactory中完成

        Statisticssti=MySessionFactory.getSessionFactory().getStatistics();

        System.out.println("二级缓存放入 "+sti.getSecondLevelCachePutCount()+" 次");

        System.out.println("二级缓存错过 "+sti.getSecondLevelCacheMissCount()+" 次");

        System.out.println("二级缓存命中 "+sti.getSecondLevelCacheHitCount()+" 次");

 

·控制台打印结果:

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

       student0_.sid=?

王天鑫

------------------------------------------------

王天鑫

Hibernate:

   select

       student0_.sid as sid1_0_,

       student0_.sname as sname1_0_,

       student0_.ssex as ssex1_0_,

       student0_.sdept as sdept1_0_,

       student0_.sage as sage1_0_,

       student0_.saddress as saddress1_0_

   from

       studentclass.dbo.student student0_

   where

        student0_.sid=?

张小小

二级缓存放入 2 次

二级缓存错过 2 次

二级缓存命中 1 次

D.在配置了二级缓存后,注意可以通过Statistics去查看命中率,失误越低(miss),命中越高(hit),说明配得越好。

·可以将oscache.properties文件放在src目录下,这样可以指定放入二级缓存对象capacity(容量)大小,默认1000个。

 

4.W----->Why:为什么要这么用?(最后,深入理解该技术为我们带来了什么优点,又有什么缺点。一门新技术的产生,必然是旧技术不能解决或者不能更好的解决该问题,那么用了该技术能够解决现有的问题吗?不用改技术可以吗?)

·为什么要用hibernate的一级缓存机制:

因为缓存机制可以为我们提高性能,当我反复多次的查询一个对象时,hibernate只需要向数据库发出一次select请求就可以查出数据。即第一次向一级缓存取出数据,当一级缓存发现没有与之对应的数据会向数据库查询,然后放入一级缓存,第二次查询只需要在一级缓存中取出即可。所以提高了查询效率。

·为什么要用二级缓存:

因为一级缓存内存有限,而且生命周期在session中有效,很短。所以这时会按需要配置二级缓存(sessionFactory级缓存)。并且二级缓存将对象保存在磁盘中,这样就可以解决一级缓存内存有限的问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值