首页 > 代码库 > 028hibernate缓存(性能优化策略)
028hibernate缓存(性能优化策略)
一级缓存
二级缓存
查询缓存
缓存是为了提高性能
变化不是很大,相对静态的对象放入缓存
对象的创建比较耗时
028--01 hibernate一级缓存
一级缓存很短和session的生命周期一致,因此也叫session级缓存或事务级缓存
hibernate一级缓存
那些方法支持一级缓存:
* get()
* load()
* iterate(查询实体对象)
如何管理一级缓存:
* session.clear(),session.evict()
如何避免一次性大量的实体数据入库导致内存溢出
* 先flush,再clear
如果数据量特别大,考虑采用jdbc实现,如果jdbc也不能满足要求可以考虑采用数据本身的特定导入工具
028--02 hibernate二级缓存
Hibernate默认的二级缓存是开启的。
二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存),它与session级缓存不一样,一级缓存只要session关闭缓存就不存在了。而二级缓存则只要进程在二级缓存就可用。
二级缓存可以被所有的session共享
二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存
二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存
二级缓存一般使用第三方的产品,如EHCache
1.二级缓存的配置和使用:
配置二级缓存的配置文件:模板文件位于hibernate\etc目录下(如ehcache.xml),将模板存放在ClassPath目录中,一般放在根目录下(src目录下)
<ehcache> <!-- 设置当缓存对象益出时,对象保存到磁盘时的保存路径。 如 d:\xxxx The following properties are translated: user.home - User‘s home directory user.dir - User‘s current working directory 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.指定哪些实体类使用二级缓存:
方法一:在实体类映射文件中,使用<cache>来指定那个实体类使用二级缓存,如下:
<cache usage="transactional|read-write|nonstrict-read-write|read-only" (1) region="RegionName" (2) include="all|non-lazy" (3) /> |
(1) usage(必须)说明了缓存的策略: transactional、 read-write、 nonstrict-read-write或 read-only。
(2) region (可选, 默认为类或者集合的名字(class or collection role name)) 指定第二级缓存的区域名(name of the second level cache region)
(3) include (可选,默认为 all) non-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: nonstrict read/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> |
一般推荐使用方法一。
5.应用范围
没有变化,近似于静态的数据。
6.二级缓存的管理:
1、 清除指定实体类的所有数据
SessionFactory.evict(Student.class);
2、 清除指定实体类的指定对象
SessionFactory.evict(Student.class, 1);//第二个参数是指定对象的ID,就可以清除指定ID的对象
使用SessionFactory清除二级缓存 Session session = 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.二级缓存的交互
Session session = 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.hibernate查询缓存
查询缓存,是用于缓存普通属性查询的,当查询实体时缓存实体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文件中启用查询缓存,如:
<property name="hibernate.cache.use_query_cache">true</property>
* 在程序中必须手动启用查询缓存,如:
query.setCacheable(true);
例如:
session = HibernateUtils.getSession(); session.beginTransaction();
Query query = session.createQuery("select s.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("select s.name from Student s"); //启用查询查询缓存 query.setCacheable(true);
//没有发出查询sql,因为启用了查询缓存 names = query.list(); for (Iterator iter=names.iterator();iter.hasNext(); ) { String name = (String)iter.next(); System.out.println(name); }
session.getTransaction().commit(); |
注:查询缓存的生命周期与session无关。
查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
028hibernate缓存(性能优化策略)