我是如何学习Hibernate的(六)

前述

缓存在操作系统中经常被使用,在CPU和内存之间有高速cache。在内存读取硬盘的时候也有缓存,也有相应的缓存页面置换算法: FIFO(First In First Out)先进先出、LRU(Least Recently Used)最近很少使用、 LFU(Least Frequently Used)最近不常被使用(命中率低)。在Hibernate中也有使用一级缓存和二级缓存来加快读取数据库的操作,节省开销。

一级缓存

一级缓存,又称为session级别的缓存。当获得一次会话,hibernate在session中创建多个集合(Map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用,没有再查询。

  • 缓存工作原理
  • 一级缓存是默认的,查询都将数据存放在一级缓存中。
  • 一级缓存缓存的是对象本身。
  • 一级缓存常用的API
    evict():用于对某个对象从session的一级缓存中清除
    clear():将一级缓存中所有对象全部清除
    flush():刷新一级缓存区的内容,使之与数据库数据保持同步。
    注意: 常用的Query方法中,query.list()是不使用一级缓存的,每次都是重新查询数据库的。

一级缓存的操作

//1 查询 id = 1
User user = (User) session.get(User.class, 1);
System.out.println(user);
//2 再查询 -- 不执行select语句,将从一级缓存获得
User user2 = (User) session.get(User.class, 1);   //此时 user 与user2是同一个对象

清除缓存

User user = (User) session.get(User.class, 1); 
System.out.println(user);
//清除
//session.clear();
session.evict(user);
// 一级缓存没有缓存对象,从数据库直接查询
User user2 = (User) session.get(User.class, 1); 
System.out.println(user2);    //此时user 与 user2 为两个不同对象

一级缓存的快照(备份)

快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与一级数据缓存必须一致。如果一级缓存修改,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存保存到数据库中。

  • 问题:一级缓存什么时候刷新?能否修改刷新时机?
    默认情况下,执行commit()时。当修改了一级缓存后,三种情况:session查询,手动刷新flush,commit底层自动flush

二级缓存

使用annotation配置缓存:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
//@BatchSize(size=5)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@region="gionName"
public class Category {
private int id;
private String name;
@Id
@GeneratedValue
public int getId() {
return id;
}

缓存策略(CacheConcurrencyStrategy)的取值

  • 只读缓存(Strategy:read only)(常用)
    如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。甚至在集群中,它也能完美地运作。
  • 读写/缓存(Strategy:read/write)(常用)
    如果应用程序需要更新数据,那么使用读/写缓存 比较合适。 如果应用程序要求“序列化事务”的隔离级别(serializable transaction isolation level),那么就决不能使用这种缓存策略。 如果在 JTA 环境中使用缓存,你必须指定 hibernate.transaction.manager_lookup_class 属性的值, 通过它,Hibernate 才能知道该应用程序中 JTA 的TransactionManager的具体策略。 在其它环境中,你必须保证在 Session.close()、或 Session.disconnect() 调用前, 整个事务已经结束。 如果你想在集群环境中使用此策略,你必须保证底层的缓存实现支持锁定(locking)。Hibernate 内置的缓存策略并不支持锁定功能。
  • 非严格读/写缓存(Strategy:nonstrict read/write)
    如果应用程序只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离,那么比较适合使用非严格读/写缓存策略。如果在 JTA 环境中使用该策略,你必须为其指定 hibernate.transaction.manager_lookup_class 属性的值,在其它环境中,你必须保证在Session.close()、或 Session.disconnect() 调用前,整个事务已经结束。
  • 事务缓存(transactional)
    Hibernate 的事务缓存策略提供了全事务的缓存支持,例如对 JBoss TreeCache 的支持。这样的缓存只能用于 JTA 环境中,你必须指定为其 hibernate.transaction.manager_lookup_class 属性。

    region的取值

    在ehcache.xml中,可以自定义缓存类型,比如:
    1
    2
    3
    4
    5
    6
    7
    <cache name="sampleCache1"
    maxElementsInMemory="10000" //最大缓存数量
    eternal="false" //是否能被清除
    timeToIdleSeconds="300" //300秒没被访问就被清除
    timeToLiveSeconds="600" //已经存在600秒就可以被清除
    overflowToDisk="true" //溢出时放在硬盘上
    />

那么@region=”sampleCache1”
配置文件中:
//不使用二级缓存
org.hibernate.cache.NoCacheProvider
//hibernate中默认的二级缓存ehcache
true
org.hibernate.cache.EhCacheProvider
//启用查询缓存
true

  • load默认使用二级缓存,iterate默认使用二级缓存
  • list默认往二级缓存加数据,但是查询的时候不使用
  • 如果要query用二级缓存,需打开查询缓存
    true
    调用Query的setCachable (true)方法指明使用二级缓存
    例如:session.createQuery(“from Category”).setCacheable(true).list();