你的位置:首页 > ASP.net教程

[ASP.net教程]在Hibernate框架中详谈一级缓存


在学习Hibernate的过程中我们肯定会碰上一个名词---缓存,一直都听说缓存机制是Hibernate中的一个难点,它分为好几种,有一级缓存,二级缓存和查询缓存

今天呢,我就跟大家分享分享我所理解的一级缓存

要想完美的体现出缓存机制的话,我想通过查询语句生成的sql应该就能够很清楚的看到

那些Hibernate的配置信息我就不展示了,直接看关键代码

场景:我要查询同一个对象,查询两次,观察在不同的情况下,sql语句的生成情况

我事先准备了一个HibernateUtil工具类,具体如下

package util;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;public class HibernateUtil {  //初始化一个ThreadLocal对象  private static final ThreadLocal sessionTL =new ThreadLocal();  private static Configuration configuration;  private final static SessionFactory sessionFactory;  static {    try{      configuration=new Configuration().configure();      sessionFactory=configuration.buildSessionFactory();    }catch(Throwable ex){      ex.printStackTrace();      throw new ExceptionInInitializerError(ex);    }      }    //获取session  public static Session currentSession(){    Session session=(Session)sessionTL.get();    if(session==null){      session=sessionFactory.openSession();      sessionTL.set(session);    }    return session;  }    //关闭session  public static void closeSession(){    Session session= (Session)sessionTL.get();    sessionTL.set(null);    session.close();   }}

 

正常我们访问DB端时应该是访问几次就发送几次sql,如下所示

  //查询学生信息  public static void select(){        //由班级查询该班级学生信息    Session session=HibernateUtil.currentSession();    Grade grade=(Grade) session.get(Grade.class, 14);    //输出班级信息    System.out.println(grade.getGname());    Grade grade2=(Grade) session.get(Grade.class, 17);    //输出班级信息    System.out.println(grade2.getGname());  }

 

结果应该是这样

 

那么问题就来了,我们现在有如下几个场景

场景一:使用同一个session连续查询两次同一个对象

//查询学生信息  public static void select(){        //由班级查询该班级学生信息    Session session=HibernateUtil.currentSession();    Grade grade=(Grade) session.get(Grade.class, 14);    //输出班级信息    System.out.println(grade.getGname());    Grade grade2=(Grade) session.get(Grade.class, 14);    //输出班级信息    System.out.println(grade2.getGname());  }

 

这个时候我们不难发现,此时我查询的是同一个对象,按照正常理解,我查询了两遍应该向DB端发送两条sql语句才对,下面看看实际的sql数

这个时候可能有的小伙伴就有疑问了,我们后面再解释这种情况,我们先接着看第二种场景

场景二:在第一次查询完毕后,关闭session对象,重新开启一个session然后继续查询同一个对象

//查询学生信息  public static void select(){        //由班级查询该班级学生信息    Session session=HibernateUtil.currentSession();    Grade grade=(Grade) session.get(Grade.class, 14);    //输出班级信息    System.out.println(grade.getGname());    //关闭session    HibernateUtil.closeSession();    //重新获取session    session=HibernateUtil.currentSession();    Grade grade2=(Grade) session.get(Grade.class, 14);    //输出班级信息    System.out.println(grade2.getGname());  }

 

这个时候我们查询的任然是同一个对象,结果却如下图

那么,通过以上两个场景的模拟,有些小伙伴可能已经明白是怎么回事了,可能有些小伙伴们还有些迷糊,下面我就讲讲我的看法吧~

总结:1:当我没有关闭session时用的同一个session两次访问同一个对象时,只会向DB端发送一条sql语句
    * 原因:因为我第一次访问数据库的时候Hibernate会自动的将我查询出来的结果保留一份查询出来的对象到一级缓存
          并且这个额对象是根据OID唯一标识的,也可以理解为数据库中的主键值,然后当我再一次访问一个对象时,Hibernate
        机制会自动的先去一级缓存中查找看有没有OID与我要查询的OID相同的对象,如果有的话,则直接从一级缓存中 拿数据
          如果相同的OID则说明缓存中没有我要的记录,那么就会直接去访问DB端了,这样的话,又会重新发送一条sql
    2:当我第一次查询完数据后立即关闭session,这时重新开启一个session来访问同一个对象,这时我们会发现它居然向数据库发送了两条Sql语句。这是为什么呢?
    * 原因:其实原因很简单,因为我们虽然说是访问的同一个对象,但是我们随即就关闭了这个session而重新开启了一个session,

        此时我们访问时的session是不一致的也就是说是两个不同的session发出的请求,这样理解的话,我们就不难理解了。

          所以总结出,一级缓存是一个会话级别的缓存,当一次回话结束后该会话里的缓存则会全部的销毁,所有我们自然就只能重新发送一条sql啦。