hibernate的二级缓存----collection和query的二级缓存

collection二级缓存:

不使用集合的二级缓存时:
  运行下面的代码:

@Test
    public void testCollectionSecondLevelCache1(){
        Department dept = (Department) session.get(Department.class, 3);
        System.out.println(dept.getId()+"   "+dept.getName());
        System.out.println(dept.getEmps().size()); 
        
        transaction.commit();
        session.close();
        
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        
        Department dept2 = (Department) session.get(Department.class,3);
        Set<Employee> emps=dept2.getEmps();
        for(Employee employee:emps){
            System.out.println(employee.getId()+"      " +employee.getName());
        }
    }

产生的结果如下:

Hibernate:
select
department0_.ID as ID1_0_0_,
department0_.NAME as NAME2_0_0_
from
GG_DEPARTMENT department0_
where
department0_.ID=?
3 B
Hibernate:
select
emps0_.DEPT_ID as DEPT_ID5_0_1_,
emps0_.ID as ID1_1_1_,
emps0_.ID as ID1_1_0_,
emps0_.NAME as NAME2_1_0_,
emps0_.SALARY as SALARY3_1_0_,
emps0_.EMAIL as EMAIL4_1_0_,
emps0_.DEPT_ID as DEPT_ID5_1_0_
from
GG_EMPLOYEE emps0_
where
emps0_.DEPT_ID=?
3
--------------------------------------------------
Hibernate:
select
department0_.ID as ID1_0_0_,
department0_.NAME as NAME2_0_0_
from
GG_DEPARTMENT department0_
where
department0_.ID=?
Hibernate:
select
emps0_.DEPT_ID as DEPT_ID5_0_1_,
emps0_.ID as ID1_1_1_,
emps0_.ID as ID1_1_0_,
emps0_.NAME as NAME2_1_0_,
emps0_.SALARY as SALARY3_1_0_,
emps0_.EMAIL as EMAIL4_1_0_,
emps0_.DEPT_ID as DEPT_ID5_1_0_
from
GG_EMPLOYEE emps0_
where
emps0_.DEPT_ID=?
5 EE
7 GG
6 FF

1如果Session没有关闭的话应该是发送两条select语句的吧,因为Session的缓存中已经初始化了department和employee对象啦,但是Session关闭后,Session的一级缓存没有了吧,所以此时的department和employee对象都是游离对象,当需要要再次获得时必须发送select语句给数据吖

但是如果我们启用了集合的二级缓存呢??

集合二级缓存的操作步骤:

I. 配置对集合使用二级缓存

<collection-cache usage="read-write" collection="com.atguigu.hibernate.entities.Department.emps"/>

也可以在 .hbm.xml 文件中进行配置

<set name="emps" table="GG_EMPLOYEE" inverse="true" lazy="true">
<cache usage="read-write"/>
<key>
<column name="DEPT_ID" />
</key>
<one-to-many class="com.atguigu.hibernate.entities.Employee" />
</set>

II. 注意: 还需要配置集合中的元素对应的持久化类也使用二级缓存! 否则将会多出 n 条 SQL 语句.(集合缓存依赖于对持久化类的二级缓存)

例如在Hibernate.cfg.xml文件中配置集合的二级缓存:

<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Employee"/>
<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Department"/>
<collection-cache usage="read-write" collection="com.atguigu.hibernate.entities.Department.emps"/>

代码示例:

使用了集合二级缓存后结果为这个:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        department0_.NAME as NAME2_0_0_ 
    from
        GG_DEPARTMENT department0_ 
    where
        department0_.ID=?
3   B
Hibernate: 
    select
        emps0_.DEPT_ID as DEPT_ID5_0_1_,
        emps0_.ID as ID1_1_1_,
        emps0_.ID as ID1_1_0_,
        emps0_.NAME as NAME2_1_0_,
        emps0_.SALARY as SALARY3_1_0_,
        emps0_.EMAIL as EMAIL4_1_0_,
        emps0_.DEPT_ID as DEPT_ID5_1_0_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
3
 -------------------------------------------------- 
6      FF
5      EE
7      GG

查询缓存:

查询缓存是二级缓存的一种用法 
查询缓存(Query Cache):  
对于经常使用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,Hibernate会把 查询结果存放在二级缓存中。以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能。  
查询缓存适用于以下场合: 1在应用程序运行时经常使用的查询语句。 2 很少对与查询语句关联的数据库数据进行插入、删除或更新操作。 

在hibernate的使用中,大家多数时间都在讨论一级缓存和二级缓存,而往往忽略了查询缓存。其实hibernate的查询缓存在使用过程中也起着同样重要的作用。hibernate的查询缓存是主要是针对普通属性结果集的缓存, 而对于实体对象的结果集只缓存id。在一级缓存,二级缓存和查询缓存都打开的情况下作查询操作时这样的:
查询普通属性---》会先到查询缓存中取,如果没有,则查询数据库;
查询实体---》 会先到查询缓存中取id,如果有,则根据id到缓存(一级/二级)中取实体,如果缓存中取不到实体,再查询数据库。

 实现步骤:
1查询缓存是属于二级缓存的一个子类别,所以查询缓存依赖于二级缓存,所以在使用查询缓存之前,必须配置好二级缓存,二级缓存上面有说,这里就不提了
2在 hibernate 配置文件中声明开启查询缓存

<property name="cache.use_query_cache">true</property>
3调用 Query 或 Criteria 的 setCacheable(true) 方法,启用当前(就是在你需要的查询语句后执行 setCacheable(true)方法)的查询缓存

例子说明:
测试代码:

@Test
    public void testQueryCache(){
        Query query = session.createQuery("FROM Employee");
        List<Employee> emps = query.list();
        System.out.println(emps.size());
        System.out.println(emps.iterator().next().getClass());
        
        emps = query.list();
        System.out.println(emps.size());
        }

运行结果:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_
25
class com.atguigu.hibernate.entities.Employee
Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_
25

在这里两次我都是执行同样的HQL语句进行查询,但是Hibernate却帮我发送了两条select语句,有一条是不是就是多余的啦?这就说明了在默认情况下, 设置的缓存对 HQL 及 QBC 查询时无效的, 但可以通过设置查询缓存的方式使其生效,(注意QBC查询也需要这样去设置)
启动了查询缓存后运行结果为:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_
25
class com.atguigu.hibernate.entities.Employee
25

只发送了一条select语句,那这样是不是大大减轻了我的程序的负担了吧

  还有一个需要注意的问题 查询缓存和一级/二级缓存不同,查询缓存的生命周期 ,是不确定的,当前关联的表发生改变时,查询缓存的生命周期结束。
例如运行下面测试代码:
@Test
    public void testQueryCache(){
        Query query = session.createQuery("FROM Employee e where e.id=1");
        query.setCacheable(true);
        
        List<Employee> emps = query.list();
        System.out.println(emps.size());
        System.out.println(emps.iterator().next().getClass());
        
        Employee employee=new Employee();
        
        employee.setEmail("sdkfjsd@qq.com");
        employee.setName("jeremy");
        employee.setSalary(8000F);
        session.save(employee);
        
        emps = query.list();
        System.out.println(emps.size());
        
        //Criteria criteria = session.createCriteria(Employee.class);
        //criteria.setCacheable(true);
    }

运行结果:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ 
    where
        employee0_.ID=10
1
class com.atguigu.hibernate.entities.Employee
Hibernate: 
    insert 
    into
        GG_EMPLOYEE
        (NAME, SALARY, EMAIL, DEPT_ID) 
    values
        (?, ?, ?, ?)
Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ 
    where
        employee0_.ID=10
1


我在进行了第一次HQL查询后又对了数据表进行了增加操作了,此时的数据表已经发生了改变了,此时查询缓存被关闭了(就算更新操作对我HQL语句查询的结果没影响,但是查询缓存还是被关闭了)

如果查询缓存没有被关闭,那已是发送一条select语句和一条insert语句,但是现在是发送了两条select语句,一条insert语句 ,所以证明了,当查询缓存相关的表更新后,
查询缓存会自动关闭,这一点需要记住
原文地址:https://www.cnblogs.com/jeremy-blog/p/4022187.html