Hibernate 一级缓存和二级缓存

本文详细介绍了Hibernate中的一级缓存和二级缓存的工作原理及配置方法。一级缓存基于Session级别,自动管理,无需配置;二级缓存基于SessionFactory级别,需手动配置并选择合适的缓存策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一级缓存[Session级别]

一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据

  1. 每个事务都有单独的第一级缓存进程范围或集群范围

    缓存被同一个进程或集群范围内的所有事务共享并发访问策略由于每个事务都拥有单独的第一级缓存,不会出现并发问题
    
  2. 处于一级缓存中的对象永远不会过期

    除非应用程序显式清空缓存或者清除特定的对象,必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间物理存储介质内存内存和硬盘。
    
  3. 对象的散装数据首先存放在基于内存的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。

  4. save、update、saveOrupdate、load、list、iterate、lock会向一级缓存存放数据

  5. Session为应用程序提供了两个管理缓存的方法:

         evict(Object obj):从缓存中清除参数指定的持久化对象。 
         clear():清空缓存中所有持久化对象。
    

query.list()和query.iterate()

query.list()会一次性查询出所有的对象并且放入一级缓存中

@Test
    public void testQueryList() throws Exception {
        Session session = HibernateUtils.getSession();
        Query query = session.createQuery("from Student");
        //把查询出的所有学生信息放入一级缓存
        List<Student> list = (List<Student>) query.list();
        //此时再查询会直接从一级缓存中取
        Student student = session.get(Student.class,0);
    }

输出结果

Hibernate: 
select
    student0_.id as id1_0_,
    student0_.phone as phone2_0_,
    student0_.postcode as postcode3_0_,
    student0_.birth as birth4_0_,
    student0_.c_id as c_id7_0_,
    student0_.gender as gender5_0_,
    student0_.name as name6_0_ 
from
    student student0_
Hibernate: 
    select
        classes0_.c_id as c_id1_1_0_,
        classes0_.c_name as c_name2_1_0_ 
    from
        class classes0_ 
    where
        classes0_.c_id=?

query.iterate()会查询出所有对象的id 当要获取某个对象时再去数据库中取

@Test
    public void testIterate() throws Exception {
        Session session = HibernateUtils.getSession();
        Query query = session.createQuery("from Student ");
        //查询出所有student的id
        Iterator<Student> iterate = query.iterate();
        //当要获取某个student对象时 再去数据库取
        while (iterate.hasNext()){
            System.out.println(iterate.next());
        }
    }

输出结果

Hibernate: 
select
    student0_.id as col_0_0_ 
from
    student student0_
Hibernate: 
    select
        student0_.id as id1_0_0_,
        student0_.phone as phone2_0_0_,
        student0_.postcode as postcode3_0_0_,
        student0_.birth as birth4_0_0_,
        student0_.c_id as c_id7_0_0_,
        student0_.gender as gender5_0_0_,
        student0_.name as name6_0_0_,
        classes1_.c_id as c_id1_1_1_,
        classes1_.c_name as c_name2_1_1_ 
    from
        student student0_ 
    left outer join
        class classes1_ 
            on student0_.c_id=classes1_.c_id 
    where
        student0_.id=?
Student{id=0, name='heqianqian', gender='female', birth=2017-04-30, address=Address{postCode='335400', phone='10086'}

如果我们需要在一个session当中要两次查询出很多对象,可以第一次使用List将对象信息放入一级缓存,第二次使用iterate获取所有对象的id 再根据id去一级缓存中获取数据

注意:

一级缓存不需要配置,就可以使用,它本身没有保护机制,所以我们程序员要考虑这个问题,我们可以同 evict 或者 clear来清除session缓存中对象. evict 是清除一个对象,clear是清除所有的sesion缓存对象

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

二级缓存[SessionFactory级别]

使用hibernate二级缓存,我们首先需要对其进行配置,配置步骤如下:

1.添加jar包
hibernate并没有提供相应的二级缓存的组件,常用的二级缓存包是EHcache

<!--cache-->
      <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>5.2.10.Final</version>
        </dependency>
  <dependency>
       <groupId>net.sf.ehcache</groupId>
       <artifactId>ehcache</artifactId>
       <version>2.10.2</version>
   </dependency>
   <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
       <version>1.2</version>
   </dependency>

2.在hibernate配置文件中开启二级缓存总开关 并且配置ehcache配置文件的位置

 <!-- 开启二级缓存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- 二级缓存的提供类 在hibernate4.0版本以后我们都是配置这个属性来指定二级缓存的提供类-->
   <property name="hibernate.cache.region.factory_class">org.hibernate.cache.SingletonEhCacheRegionFactory
        </property>
        <!-- 二级缓存配置文件的位置 -->
        <property name="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>
        <!--如果是使用hibernate3的版本的话,那么二级缓存的提供类则要配置成这个:-->
        <property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>

3.在ehcache.xml中配置缓存信息

<ehcache>
    <!--指定数据在磁盘中的存储位置-->
    <diskStore path="F:\ehcache"/>
    <!--当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略-->
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">

   <!--必选参数
        1.maxElementsInMemory:在内存中缓存的element的最大数目
        2.maxElementsOnDisk:在磁盘上缓存的element的最大数目,若是0表示无穷大
        3.eternal:设定缓存的elements是否永远不过期
            如果为true,则缓存的数据始终有效
            如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
        4.overflowToDisk:设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
   -->
   <!--可选参数
        1.timeToIdleSeconds:当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
        2.timeToLiveSeconds:缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
        3.diskSpoolBufferSizeMB:设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区
        4.diskPersistent:在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false
        5.diskExpiryThreadIntervalSeconds:磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
        6.memoryStoreEvictionPolicy:当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。
                默认是LRU(最近最少使用)
                可选的有LFU(最不常使用)和FIFO(先进先出)
   -->
</defaultCache>
</ehcache>

4.开启二级缓存

1)使用xml方式

<class name="com.hqq.bean.Student" table="student" schema="hibernate">
         <!--二级缓存一般设置为只读的 -->
        <cache usage="read-only"/>

        <id name="id" column="id"/>
        <property name="name" column="name"/>
        <property name="gender" column="gender"/>
        <property name="birth" column="birth"/>
        <!--<property name="address" column="address"/>-->
        <component name="address" class="com.hqq.bean.Address">
            <property name="phone" column="phone"/>
            <property name="postCode" column="postcode"/>
        </component>
    </class>

二级缓存的使用策略一般有这几种:read-only、nonstrict-read-write、read-write、transactional

2)使用注解的方式

@Entity
@Table(name = "student", schema = "hibernate")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)//表示开启二级缓存,并使用read-only策略
public class Student implements Serializable {

5.配置完毕 测试二级缓存是否开启

@Test
    public void test() throws Exception {
        Session session = HibernateUtils.getSession();
        Student student = session.get(Student.class, 0);
        System.out.println(student);
        session.close();

        Session session2 = HibernateUtils.getSession();
        Student student2 = session2.get(Student.class, 0);
        System.out.println(student2);
    }

测试结果

Hibernate: 
select
    classes0_.c_id as c_id1_1_0_,
    classes0_.c_name as c_name2_1_0_ 
from
    class classes0_ 
where
    classes0_.c_id=?

Student{id=0, name='heqianqian', gender='female', birth=2017-04-30, address=Address{postCode='335400', phone='10086'}

注意:

  1. 二级缓存缓存的是对象 不会缓存属性
  2. 二级缓存不会缓存hql语句 需要配置:

hibernate.cfg.xml

  <!-- 开启查询缓存 -->
        <property name="hibernate.cache.use_query_cache">true</property>

Student类上添加@Cachable注解

@Entity
@Table(name = "student", schema = "hibernate")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)//表示开启二级缓存,并使用read-only策略
public class Student implements Serializable {

测试

@Test
    public void testHql() throws Exception {
        Session session = HibernateUtils.getSession();
        List<Student> list = session.createQuery("from Student ").setCacheable(true).list();
        Iterator<Student> students = (Iterator<Student>) list.iterator();
        while (students.hasNext()){
            System.out.println(students.next());
        }
        //session.close();
        Session session1 = HibernateUtils.getSession();
        List<Student> list1 = session.createQuery("from Student ").setCacheable(true).list();
        Iterator<Student> students1 = (Iterator<Student>) list1.iterator();
        while (students1.hasNext()){
            System.out.println(students1.next());
        }
    }

输出:

Hibernate: 
select
    student0_.id as id1_0_,
    student0_.phone as phone2_0_,
    student0_.postcode as postcode3_0_,
    student0_.birth as birth4_0_,
    student0_.c_id as c_id7_0_,
    student0_.gender as gender5_0_,
    student0_.name as name6_0_ 
from
    student student0_
Hibernate: 
    select
        classes0_.c_id as c_id1_1_0_,
        classes0_.c_name as c_name2_1_0_ 
    from
        class classes0_ 
    where
        classes0_.c_id=?

Student{id=0, name='heqianqian', gender='female', birth=2017-04-30, address=Address{postCode='335400', phone='10086'}

Student{id=0, name='heqianqian', gender='female', birth=2017-04-30, address=Address{postCode='335400', phone='10086'}

参考文章:

http://www.cnblogs.com/xiaoluo501395377/p/3377604.html
http://blog.youkuaiyun.com/xlgen157387/article/details/40071651

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值