hibernate缓存机制(四)-查询缓存

上一篇我们将到hibernate的二级缓存,hibernate的二级缓存彻底解决了n+1问题,并提供了线程使用的缓存机制,但是hibernate的二级缓存只能缓存对象,不能缓存对象的属性,即不能对hql语句进行缓存。

我们如果要配置查询缓存,只需要在hibernate.cfg.xml中加入一条配置即可:

<!-- 开启查询缓存 -->

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

然后我们如果在查询hql语句时要使用查询缓存,就需要在查询语句后面设置这样一个方法:

List<Student>ls = session.createQuery("from Student where name like ?")

                    .setCacheable(true)  //开启查询缓存,查询缓存也是SessionFactory级别的缓存

                    .setParameter(0, "%王%")

                   .setFirstResult(0).setMaxResults(50).list();

如果是在annotation中,我们还需要在这个类上加上这样一个注解:@Cacheable

接下来我们来通过测试用例来看看我们的查询缓存

①查询缓存也是sessionFactory级别的缓存

TestCase1:

  @Test

    public void test2() {

        Session session = null;

        try {

            /**

             * 此时会发出一条sql取出所有的学生信息

             */

            session =HibernateUtil.openSession();

            List<Student> ls =session.createQuery("from Student")

                    .setCacheable(true//开启查询缓存,查询缓存也是sessionFactory级别的缓存

                   .setFirstResult(0).setMaxResults(50).list();

            Iterator<Student> stus =ls.iterator();

            for(;stus.hasNext();) {

                Student stu = stus.next();

                System.out.println(stu.getName());

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            HibernateUtil.close(session);

        }

        try {

            /**

             * 此时会发出一条sql取出所有的学生信息

             */

            session =HibernateUtil.openSession();

            List<Student> ls =session.createQuery("from Student")

                    .setCacheable(true//开启查询缓存,查询缓存也是sessionFactory级别的缓存

                    .setFirstResult(0).setMaxResults(50).list();

            Iterator<Student> stus =ls.iterator();

            for(;stus.hasNext();) {

                Student stu = stus.next();

               System.out.println(stu.getName());

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            HibernateUtil.close(session);

        }

    }

Hibernate: selectstudent0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_,student0_.rid as rid2_ from t_student student0_ limit ?

我们看到,此时如果我们发出两条相同的语句,hibernate也只会发出一条sql,因为已经开启了查询缓存了,并且查询缓存也是sessionFactory级别的

②只有当 HQL 查询语句完全相同时,连参数设置都要相同,此时查询缓存才有效

TestCase2:

  @Test

    public void test3() {

        Session session = null;

        try {

            /**

             * 此时会发出一条sql取出所有的学生信息

             */

            session =HibernateUtil.openSession();

            List<Student> ls =session.createQuery("from Student where name like ?")

                    .setCacheable(true)//开启查询缓存,查询缓存也是SessionFactory级别的缓存

                    .setParameter(0, "%王%")

                   .setFirstResult(0).setMaxResults(50).list();

            Iterator<Student> stus =ls.iterator();

            for(;stus.hasNext();) {

                Student stu = stus.next();

               System.out.println(stu.getName());

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            HibernateUtil.close(session);

        }

        session = null;

        try {

            /**

             * 此时会发出一条sql取出所有的学生信息

             */

            session =HibernateUtil.openSession();

            /**

             * 只有当HQL完全相同的时候,连参数都要相同,查询缓存才有效

             */

//            List<Student> ls =session.createQuery("from Student where name like ?")

//                    .setCacheable(true)//开启查询缓存,查询缓存也是SessionFactory级别的缓存

//                    .setParameter(0, "%%")

//                   .setFirstResult(0).setMaxResults(50).list();

            List<Student> ls =session.createQuery("from Student where name like ?")

                    .setCacheable(true)//开启查询缓存,查询缓存也是SessionFactory级别的缓存

                    .setParameter(0, "%张%")

                   .setFirstResult(0).setMaxResults(50).list();

            Iterator<Student> stus = ls.iterator();

            for(;stus.hasNext();) {

                Student stu = stus.next();

               System.out.println(stu.getName());

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            HibernateUtil.close(session);

        }

    }

Hibernate: selectstudent0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_,student0_.rid as rid2_ from t_student student0_ where student0_.name like ?limit ?

 

Hibernate: selectstudent0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_,student0_.rid as rid2_ from t_student student0_ where student0_.name like ?limit ?

我们看到,如果我们的hql查询语句不同的话,我们的查询缓存也没有作用

③查询缓存也能引起 N+1 的问题

查询缓存也能引起 N+1 的问题,我们这里首先先将 Student 对象上的二级缓存先注释掉:

     <!-- 二级缓存一般设置为只读的 -->

        <!--  <cacheusage="read-only"/>  -->

TestCase4:

  @Test

    public void test4() {

        Session session = null;

        try {

            /**

             * 查询缓存缓存的不是对象而是id

             */

            session =HibernateUtil.openSession();

            List<Student> ls =session.createQuery("from Student where name like ?")

                    .setCacheable(true)//开启查询缓存,查询缓存也是SessionFactory级别的缓存

                    .setParameter(0, "%王%")

                    .setFirstResult(0).setMaxResults(50).list();

            Iterator<Student> stus =ls.iterator();

            for(;stus.hasNext();) {

                Student stu = stus.next();

               System.out.println(stu.getName());

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            HibernateUtil.close(session);

        }

       

        session = null;

        try {

            /**

             * 查询缓存缓存的是id,此时由于在缓存中已经存在了这样的一组学生数据,但是仅仅只是缓存了

             * id,所以此处会发出大量的sql语句根据id取对象,这也是发现N+1问题的第二个原因

             * 所以如果使用查询缓存必须开启二级缓存

             */

            session =HibernateUtil.openSession();

            List<Student> ls =session.createQuery("from Student where name like ?")

                    .setCacheable(true)//开启查询缓存,查询缓存也是SessionFactory级别的缓存

                    .setParameter(0, "%王%")

                   .setFirstResult(0).setMaxResults(50).list();

            Iterator<Student> stus =ls.iterator();

            for(;stus.hasNext();) {

                Student stu = stus.next();

               System.out.println(stu.getName());

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            HibernateUtil.close(session);

        }

    }

Hibernate: selectstudent0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_,student0_.rid as rid2_ from t_student student0_ where student0_.name like ?limit ?

 

Hibernate: selectstudent0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_,student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name asname1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name asname0_1_, special2_.type as type0_1_ from t_student student0_ left outer joint_classroom classroom1_ on student0_.rid=classroom1_.id left outer joint_special special2_ on classroom1_.sid=special2_.id where student0_.id=?

Hibernate: selectstudent0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_,student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name asname1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name asname0_1_, special2_.type as type0_1_ from t_student student0_ left outer joint_classroom classroom1_ on student0_.rid=classroom1_.id left outer joint_special special2_ on classroom1_.sid=special2_.id where student0_.id=?

Hibernate: selectstudent0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_,student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name asname1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name asname0_1_, special2_.type as type0_1_ from t_student student0_ left outer joint_classroom classroom1_ on student0_.rid=classroom1_.id left outer joint_special special2_ on classroom1_.sid=special2_.id where student0_.id=?

Hibernate: selectstudent0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_,student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name asname1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name asname0_1_, special2_.type as type0_1_ from t_student student0_ left outer joint_classroom classroom1_ on student0_.rid=classroom1_.id left outer joint_special special2_ on classroom1_.sid=special2_.id where student0_.id=?

 

.........................

我们看到,当我们将二级缓存注释掉以后,在使用查询缓存时,也会出现 N+1 的问题,为什么呢?

因为查询缓存缓存的也仅仅是对象的id,所以第一条 sql 也是将对象的id都查询出来,但是当我们后面如果要得到每个对象的信息的时候,此时又会发sql语句去查询,所以,如果要使用查询缓存,我们一定也要开启我们的二级缓存,这样就不会出现 N+1 问题了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值