在学习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啦。