1 前言

Hibernate中提供了两个级别的缓存:

  1. 第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,一般情况下无需进行干预—内置缓存
  2. 第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存—外置缓存

2 对象的三种状态

hibernate中的实体对象存在三种状态:

  1. 瞬时对象(transient)
  2. 持久化对象 (prsistence)
  3. 脱管对象 (detached)

3 一级缓存

session中提供一个缓存,称为一级缓存。 当对象变成持久化对象时,一定会被存入缓存。

3.1 变成持久化对象的途径

三种:

  1. 瞬时对象->持久化对象 save(), saveOrUpdate()。
  2. 脱管对象->持久化对象 update(), saveOrUpdate()。
  3. 直接从数据库查询 get(), load(), list() …

3.2 从缓存中移除数据

两种方式

  1. clear()
  2. evict()

4 缓存的用途

当在同一session中进行查询时,首先会在缓存中查询,如果存在则不需要再到数据库中查询,提高了性能。 所有查询首先都要经过缓存就意味着缓存中的数据跟数据库中的数据必须是一致的,同步的。

4.1 把数据库中的数据同步到缓存中

使用 refresh() , 此方法只要调用就会查询数据库,并把最新的数据同步到缓存中。

4.2 把缓存中的数据同步到数据库中

三种方法:

  1. 对缓存中对象进行更新后调用查询时
  2. 调用flush()
  3. 调用commit()

4.3 同步实现的方式

数据库中的数据同步到缓存中意味着select语句
缓存中的数据同步到数据库中意味着update语句

原则上如果可以应该尽量减少对数据库的操作,因此如果数据库中的数据跟缓存中的数据是相同的那么就没有必要select或update。 那么,每次比较时应该如果做呢,直接查询数据库中的数据然后跟缓存中的数据进行比较?这肯定是没有意义的,达不到优化的目的。 因此,hibernate的方式是: 当持久化对象被载入到缓存中时,分别保存它当前的快照和它的地址。 之后对持久化对象(也就是缓冲中存储的地址指向的对象)进行操作。 当上面提到的同步方式被触发时,hibernate会把快照跟持久化对象进行比较,如果相同,那么就什么也不做。如果不同,则操作数据库。

5 二级缓存

一级缓存存在于session中,当session关闭时,一级缓存即消失。二级缓存存在于sessionFactory中,意味着其是全局范围的缓存。

5.1 二级缓存的作用

减少对数据库的查询操作。

5.2 二级缓存的构成

主要由四部分构成。

  1. 对象区(region)
  2. 集合区
  3. query区
  4. update timesatmp区

5.2.1 对象区

所有通过hibernate从数据库查询出的对象都会被放入对象区中,存储的方式是存储对象的属性值,而不是整个对象。 对象区中的对象,被集合区及query区使用。

5.2.2 集合区

集合缓存时会把集合中对象的id保存到集合区中,之后再查询相同集合时会拿着每个对象的id到对象区中去查找。

5.2.3 query区

如果希望list也使用缓存,那么可以设置查询缓存。 查询缓存会把查询语句及参数作为key, 对象的结果集中对象的id作为value保存到查询缓存区中

list方法不使用缓存(包括一级和二级),get load方法使用缓存 无论设不设置lazy属性,只要持久话类(entity)能够被继承,load方法都会生成子类(即代理对象),get方法不会生成子类。 如果设置lazy属性,那么会对get和load方法都产生影响。

5.3 配置二级缓存

  1. 导入EHcache相关jar
    backport-util-concurrent.jar
    commons-logging.jar
    ehcache-1.5.0.jar
    
  2. 在hibernate.cfg.xml中配置开启二级缓存
       <!-- 开启二级缓存 -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <!-- 要指定缓存的供应商-->
    <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

    <!-- 配置哪些类使用二级缓存 -->
    <class-cache usage="read-write" class="test.domain.Customer"/>
    <class-cache usage="read-write" class="test.domain.Order"/>

    <!-- 配置哪些集合使用二级缓存 -->
    <collection-cache usage="read-write" collection="test.domain.Customer.orders"/>
  3. 在src 下提供ehcache默认的配置文件:ehcache.xml

5.4 配置查询缓存:

  1. 查询缓存是建立在二级缓存基础之上的,所以先配置二级缓存
  2. 之后在hibernate.cfg.xml中配置启用查询缓存:
    <!-- 开启查询缓存 -->
    <property name="hibernate.cache.use_query_cache">true</property>
  3. 最后在代码调用过程中进行声明即可
    Session session = HibernateUtils.getSession();
    Transaction tx = session.beginTransaction();
    String hql = "FROM Customer";
    Query query = session.createQuery(hql);
    query.setCacheable(true);//将查询出的数据放入查询缓存
    query.list();

    Query query2 = session.createQuery(hql);
    query2.setCacheable(true);//如果查询缓存中有数据,直接使用
    query2.list();

    tx.commit();
    session.close();