HIbernate学习笔记(八) hibernate缓存机制

时间:2021-05-27 07:31:50

hibernate缓存

一、 Session级缓存(一级缓存)

一级缓存很短和session的生命周期一致,因此也叫session级缓存或事务级缓存

hibernate一级缓存

那些方法支持一级缓存:

* get()

* load()

* iterate(查询实体对象)

如何管理一级缓存:

*session.clear(),session.evict()

如何避免一次性大量的实体数据入库导致内存溢出

* 先flush,再clear

如果数据量特别大,考虑采用jdbc实现,如果jdbc也不能满足要求可以考虑采用数据本身的特定导入工具

二、 二级缓存

Hibernate默认的二级缓存是开启的。

二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存),它与session级缓存不一样,一级缓存只要session关闭缓存就不存在了。而二级缓存则只要进程在二级缓存就可用。

二级缓存可以被所有的session共享

二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存

二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存

二级缓存一般使用第三方的产品,如EHCache

1、   二级缓存的配置和使用:

配置二级缓存的配置文件:模板文件位于hibernate\etc目录下(如ehcache.xml),将模板存放在ClassPath目录中,一般放在根目录下(src目录下)

<ehcache>

<!-- 设置当缓存对象益出时,对象保存到磁盘时的保存路径。

   如 d:\xxxx

The following properties aretranslated:

user.home - User's home directory

user.dir - User's current workingdirectory

java.io.tmpdir - windows的临时目录 -->

<diskStore path="java.io.tmpdir"/>

<!--默认配置/或对某一个类进行管理

maxInMemory       - 缓存中可以存入的最多个对象数

eternal           - true:表示永不失效,false:不是永久有效的。

timeToIdleSeconds - 空闲时间,当第一次访问后在空闲时间内没有访问,则对象失效,单位为秒

timeToLiveSeconds - 被缓存的对象有效的生命时间,单位为秒

overflowToDisk  当缓存中对象数超过核定数(益出时)时,对象是否保存到磁盘上。true:保存;false:不保存

如果保存,则保存路径在标签<diskStore>中属性path指定

-->

<defaultCache

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="true"

/>

</ehcache>

2、   二级缓存的开启:

Hibernate中二级缓存默认就是开启的,也可以显示的开启

二级缓存是hibernate的配置文件设置如下:

<!--开启二级缓存,hibernate默认的二级缓存就是开启的 -->

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

3、   指定二级缓存产品提供商:

修改hibernate的 配置文件,指定二级缓存提供商,如下:

<!--指定二级缓存提供商-->

<property name="hibernate.cache.provider_class">

org.hibernate.cache.EhCacheProvider

</property>

以下为常见缓存提供商:

Cache

Provider class

Type

Cluster Safe

Query Cache Supported

Hashtable (not intended for production use)

org.hibernate.cache.HashtableCacheProvider

memory

yes

EHCache

org.hibernate.cache.EhCacheProvider

memory, disk

yes

OSCache

org.hibernate.cache.OSCacheProvider

memory, disk

yes

SwarmCache

org.hibernate.cache.SwarmCacheProvider

clustered (ip multicast)

yes (clustered invalidation)

JBoss TreeCache

org.hibernate.cache.TreeCacheProvider

clustered (ip multicast), transactional

yes (replication)

yes (clock sync req.)

4、   使用二级缓存

a)       xml方式:指定哪些实体类使用二级缓存:

方法一:在实体类映射文件中,使用<cache>来指定那个实体类使用二级缓存,如下:

<cache 
    usage="transactional|read-write|nonstrict-read-write|read-only"  (1)
    region="RegionName"                                              (2)
    include="all|non-lazy"                                           (3)
/>

(1)   usage(必须)说明了缓存的策略: transactional、 read-writenonstrict-read-write或 read-only

(2)   region (可选, 默认为类或者集合的名字(class orcollection role name)) 指定第二级缓存的区域名(name of the secondlevel cache region)

(3)   include (可选,默认为 allnon-lazy 当属性级延迟抓取打开时, 标记为lazy="true"的实体的属性可能无法被缓存

另外(首选?), 你可以在hibernate.cfg.xml中指定<class-cache>和 <collection-cache> 元素。

这里的usage 属性指明了缓存并发策略(cache concurrency strategy)

策略:只读缓存(Strategy:read only)

如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。甚至在集群中,它也能完美地运作。

<class name="eg.Immutable" mutable="false">
    <cache usage="read-only"/>
    ....
</class>

策略:读/写缓存(Strategy:read/write)

如果应用程序需要更新数据,那么使用/写缓存 比较合适。 如果应用程序要求“序列化事务”的隔离级别(serializable transaction isolation level),那么就决不能使用这种缓存策略。 如果在JTA环境中使用缓存,你必须指定hibernate.transaction.manager_lookup_class属性的值, 通过它,Hibernate才能知道该应用程序中JTA的TransactionManager的具体策略。 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。 如果你想在集群环境中使用此策略,你必须保证底层的缓存实现支持锁定(locking)。Hibernate内置的缓存策略并不支持锁定功能。

<class name="eg.Cat" .... >
    <cache usage="read-write"/>
    ....
    <set name="kittens" ... >
        <cache usage="read-write"/>
        ....
    </set>
</class>

策略:非严格读/写缓存(Strategy: nonstrictread/write)

如果应用程序只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离,那么比较适合使用非严格读/写缓存策略。如果在JTA环境中使用该策略,你必须为其指定hibernate.transaction.manager_lookup_class属性的值, 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。

策略:事务缓存(transactional)

Hibernate的事务缓存策略提供了全事务的缓存支持, 例如对JBoss TreeCache的支持。这样的缓存只能用于JTA环境中,你必须指定为其hibernate.transaction.manager_lookup_class属性。

没有一种缓存提供商能够支持上列的所有缓存并发策略。下表中列出了各种提供器、及其各自适用的并发策略。

表 19.2.  各种缓存提供商对缓存并发策略的支持情况(Cache Concurrency Strategy Support)

Cache

read-only

nonstrict-read-write

read-write

transactional

Hashtable (not intended for production use)

yes

yes

yes

EHCache

yes

yes

yes

OSCache

yes

yes

yes

SwarmCache

yes

yes

JBoss TreeCache

yes

yes

注:此方法要求:必须要标签<cache>放在<id>标签之前

 

<class name="com.wjt276.hibernate.Student"table="t_student">

<!--  指定实体类使用二级缓存 -->

<cache usage="read-only"/>//***********

<id name="id"column="id">

<generator class="native"/>

</id>

<property name="name"column="name"/>

<!--

使用多对一标签映射 一对多双向,下列的column值必需与多的一端的key字段值一样。

-->

<many-to-one name="classes"column="classesid"/>

</class>

方法二:在hibernate配置文件(hibernate.cfg.xml)使用<class-cache>标签中指定

要求:<class-cache>标签必须放在<maping>标签之后。

<hibernate-configuration>

<session-factory>

…………

<mapping resource="com/wjt276/hibernate/Classes.hbm.xml"/>

<mapping resource="com/wjt276/hibernate/Student.hbm.xml"/>

<class-cache class="com.wjt276.hibernate.Student"usage="read-only"/>

</session-factory>

</hibernate-configuration>

一般推荐使用方法一。

b)       annotation注解

为了优化数据库访问,你可以激活所谓的Hibernate二级缓存.该缓存是可以按每个实体和集合进行配置的.

@org.hibernate.annotations.Cache定义了缓存策略及给定的二级缓存的范围. 此注解适用于根实体(非子实体),还有集合.

@Entity

@Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

publicclass Forest { ... }

@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)

@JoinColumn(name="CUST_ID")

@Cache(usage =CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

public SortedSet<Ticket> getTickets(){

return tickets;

}

@Cache(

CacheConcurrencyStrategy usage();                 (1)

String region() default "";                       (2)

String include() default"all";                   (3)

)

(1)

usage: 给定缓存的并发策略(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)

(2)

region (可选的):缓存范围(默认为类的全限定类名或是集合的全限定角色名)

(3)

include (可选的):值为all时包括了所有的属性(proterty), 为non-lazy时仅含非延迟属性(默认值为all)

5、   应用范围

没有变化,近似于静态的数据。

6、   二级缓存的管理:

1、   清除指定实体类的所有数据

SessionFactory.evict(Student.class);

2、   清除指定实体类的指定对象

SessionFactory.evict(Student.class, 1);//第二个参数是指定对象的ID,就可以清除指定ID的对象

使用SessionFactory清除二级缓存

Sessionsession = null;

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.load(Student.class, 1);

System.out.println("student.name=" +student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

//管理二级缓存

SessionFactory factory = HibernateUtils.getSessionFactory();

//factory.evict(Student.class);

factory.evict(Student.class, 1);

try {

session = HibernateUtils.getSession();

session.beginTransaction();

//会发出查询sql,因为二级缓存中的数据被清除了

Student student = (Student)session.load(Student.class, 1);

System.out.println("student.name=" +student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

7、   二级缓存的交互

Sessionsession = null;

try {

session = HibernateUtils.getSession();

session.beginTransaction();

//仅向二级缓存读数据,而不向二级缓存写数据

session.setCacheMode(CacheMode.GET);

Student student =(Student)session.load(Student.class, 1);

System.out.println("student.name=" +student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

try {

session = HibernateUtils.getSession();

session.beginTransaction();

//发出sql语句,因为session设置了CacheMode为GET,所以二级缓存中没有数据

Student student =(Student)session.load(Student.class, 1);

System.out.println("student.name=" +student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

try {

session = HibernateUtils.getSession();

session.beginTransaction();

//只向二级缓存写数据,而不从二级缓存读数据

session.setCacheMode(CacheMode.PUT);

//会发出查询sql,因为session将CacheMode设置成了PUT

Student student =(Student)session.load(Student.class, 1);

System.out.println("student.name=" +student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

CacheMode参数用于控制具体的Session如何与二级缓存进行交互。

·                                CacheMode.NORMAL - 从二级缓存中读、写数据。

·                                CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。

·                                CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。

·                                CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过hibernate.cache.use_minimal_puts的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。

如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics API。

Map cacheEntries = sessionFactory.getStatistics()
        .getSecondLevelCacheStatistics(regionName)
        .getEntries();

此时,你必须手工打开统计选项。可选的,你可以让Hibernate更人工可读的方式维护缓存内容。

hibernate.generate_statistics true
hibernate.cache.use_structured_entries true

8、   总结

load默认使用二级缓存,iterate默认使用二级缓存

list默认向二级缓存中加数据,但是查询时候不使用

三、 查询缓存

查询缓存,是用于缓存普通属性查询的,当查询实体时缓存实体ID。
默认情况下关闭,需要打开。查询缓存,对list/iterator这样的操作会起作用。
可以使用<property name=”hibernate.cache.use_query_cache”>true</property>来打开查询缓存,默认为关闭。
所谓查询缓存:即让hibernate缓存list、iterator、createQuery等方法的查询结果集。如果没有打开查询缓存,hibernate将只缓存load方法获得的单个持久化对象。
在打开了查询缓存之后,需要注意,调用query.list()操作之前,必须显式调用query.setCachable(true)来标识某个查询使用缓存。
查询缓存的生命周期:当前关联的表发生修改,那么查询缓存生命周期结束
注意查询缓存依赖于二级缓存,因为使用查询缓存需要打开二级缓存

查询缓存的配置和使用:

* 在hibernate.cfg.xml文件中启用查询缓存,如:

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

* 在程序中必须手动启用查询缓存,如:

         query.setCacheable(true);
 
例如:

session= HibernateUtils.getSession();

session.beginTransaction();

Query query = session.createQuery("selects.name from Student s");

//启用查询查询缓存

query.setCacheable(true);

List names = query.list();

for (Iterator iter=names.iterator();iter.hasNext();) {

String name =(String)iter.next();

System.out.println(name);

}

System.out.println("-------------------------------------");

query = session.createQuery("selects.name from Student s");

//启用查询查询缓存

query.setCacheable(true);

//没有发出查询sql,因为启用了查询缓存

names = query.list();

for (Iteratoriter=names.iterator();iter.hasNext(); ) {

String name =(String)iter.next();

System.out.println(name);

}

         session.getTransaction().commit();
 

Session session = sf.openSession();

session.beginTransaction();

List<Category>categories = (List<Category>)session.createQuery("from Category")

.setCacheable(true).list();

session.getTransaction().commit();

session.close();

Session session2 = sf.openSession();

session2.beginTransaction();

List<Category>categories2 = (List<Category>)session2.createQuery("from Category")

.setCacheable(true).list();

session2.getTransaction().commit();

         session2.close();
 
注:查询缓存的生命周期与session无关。
查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用

四、 缓存算法

1、  LRU、LFU、FIFO