Hibernate缓存与集合性能分析

Hibernate做为一个应用级的数据访问层封装,只能在其做用范围内保持cache中数据的有效性,若是系统与第三方系统共享数据库的状况下,Hibernate的Cache机制可能失效。java

Hibernate在本地JVM中维护了一个缓存池,并将从数据库得到的数据保存到池中以供下次重用。数据库

外部系统的定义,并不是限于本系统以外的第三方系统,即便在本系统中,若是出现了绕过Hibernate数据存储机制的其余数据存取手段,那么Cache的有效性也必须细加考量。数组

1. Cache分类缓存

Hibernate中的Cache大体分为两层:session

(1) Session级别缓存:在Session实现,属于事务级数据缓冲;一旦事务结束,这个Cache也就失效。此层为内置实现,无需进行干涉。性能

(2) 二级缓存:Hibernate中对其实力范围内的数据进行缓存的管理容器。fetch

2. 缓存映射hibernate

配置缓存映射时经过设置类或集合映射的<cache>元素来设定的。如:设计

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

usage:必须。说明了缓存的策略;、代理

  ----read-only:若是只须要读取一个持久化类的实例,而无需修改,能够进行制度缓存,最简单也是最实用的方法,甚至在集群中,也能完美地运做;

  ----read-write:若是应用程序须要更新数据,实用读/写策略比较合适。但若要求“序列化事务”的隔离级别,则毫不能使用这种缓存策略;

                           若是想在集群环境中使用此策略,必须保证底层的缓存实现支持锁定(Locking),Hibernate内置的缓存策略并不支持锁定。

                           若是在JTA环境中使用缓存,那么必须指定hibernate.transaction.manager_lookup_class属性的值。

  ----nonstrict-read-write:非严格读/写缓存策略。若是只是偶尔须要更新数据,不多出现两个事务同时更新同一记录的状况,则可用此项。

                           若是在JTA环境中使用缓存,那么必须指定hibernate.transaction.manager_lookup_class属性的值。

  ----transactional:Hibernate的事务缓存策略提供了全事务的缓存支持。这样的缓存只能用于JTA环境中,需指定hibernate.transaction.manager_lookup_class属性。

region:可选。默认为类或集合的名字,指定第二级缓存的区域名;

include:可选。默认为all。当属性级延迟读取打开时,标记为lazy=”true”的实体的属性可能没法被缓存。

3. 管理缓存

一级缓存

在应用程序中,当给save()、update()、saveOrUpdate()方法传递一个对象或使用load()、get()、list()、iterate()或scroll()方法得到一个对象时,该对象都将被加入到Session的内部缓存中。

当flush()方法被调用时,对象的状态会和数据库取得同步。若是不但愿此同步操做发生或者正处理大量对象、须要有效管理内存时,就能够调用evict()方法,从一级缓存中移除这些对象及其集合。

Session还提供了一个contains()方法,用来判断某个实例是否处于当前session的缓存中。若是要把全部的对象从session的缓存中完全清除,则须要调用Session.clear()方法。

ScrollableResult cats = session.createQuery(“FROM Cat as cat”).scroll(); // 大量数据集 
while (cats.next()){ 
  Cat cat = (Cat)cats.get( 0 ); 
  ... 
  session.evict(cat); // 移除cat缓存 
}

 

二级缓存

对于二级缓存来讲,在SessionFactory中定义了许多方法,用于清除缓存中的实例、整个类、集合实例或者整个集合。

如:

sessionFactory.evict(Cat.class, catId); // 清除某个Cat 
sessionFactory.evict(Cat.class); // 清除全部Cats 
sessionFactory.evictCollection("Cat.kittens", catId); // 清除某个kittens对象的集合 
sessionFactory.evictCollection("Cat.kittens"); // 清除全部kittens对象的集合

 

4. 查询缓存

在Hibernate中查询的结果集也能够被缓存。只有当常用一样的参数进行查询时,才会有些用处。

要使用查询缓存,须要在配置文件中设置:

hibernate.cache.use_query_cache=true

该设置将会建立两个缓存区域:

① org.hibernate.cache.StandardQueryCache:用于保存查询结果集;

② org.hibernate.cache.UpdateTimestampsCache:用于保存最近查询的一系列表的时间戳;

须要注意的是在查询缓存中,它并不缓存结果集中所包含的实体的确切状态,只缓存这些实体的标识符属性的值以及各值类型的结果,因此查询缓存一般会和二级缓存一块儿使用。

绝大多数的查询并不能从查询缓存中受益,因此Hibernate默认是不进行查询缓存的。

若是须要进行查询缓存,须要调用Query.setCacheable(true)方法。这个调用会让查询在执行过程当中先从缓存中查找结果,并将本身的结果集放到缓存中去。

若是要对查询缓存的失效政策进行精确地控制,必须调用Query.setCacheRegion()方法,为每一个查询指定其命名的缓存区域。

如:

List cats = session.createQuery(“FROM Cat as cat WHERE cat.kittens = :kit”)
                   .setEntity(“kittens”, kittens)
                   .setMaxResults(20)
                   .setCacheable(true)
                   .setCacheRegion("pages")
                   .list();

 

若查询须要强行刷新其查询缓存区域,那么应该调用Query.setCacheMode(CacheMode.REFRESH)。

这对在其余进程中修改底层数据或对那些须要选择性更新特定查询结果集的状况特别有用。这是对SessionFactory.evictQueries()更为有效的替代方案,一样能够清除查询缓存区域。

CacheMode参数用于控制具体的Session如何与二级缓存进行交互,主要属性以下:

· CacheMode.NORMAL:从二级缓存中读、写数据;

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

· CacheMode.PUT:仅向二级缓存写数据,但不从二级缓存中读取数据;

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

5. 提高集合性能

在Hibernate中定义了三种基本类型的集合,分别是值数据集合、一对多关联和多对多关联。

这个分类区分了不一样的表和外键关系类型,若是要了解关系模型的全部内容,必须同时考虑”用于Hibernate更新或删除集合行数据的主键结构”,所以获得如下分类:

· 有序集合类

· 集合(sets)

· 包(bags)

全部的有序集合类(maps/lists/arrays)都拥有一个由<key>和<index>组成的主键。

这种状况下集合类的更新时很是高效的——主键已经被有效地索引,所以当Hibernate试图更新或删除一行时,能够迅速找到该行数据。

集合(sets)的主键由<key>和其余元素字段构成。对于有些元素类型来讲,效率很低,特别是组合元素或大文本、大二进制字段,数据库可能没法有效地对复杂的主键进行索引。

另外一方面,对于一对多、多对多关联,特别是合成的标识符来讲,集合也能够达到一样的高效性能。

<idbag>映射定义了代理键,所以它老是能够很高效地被更新。事实上,<idbag>拥有着最好的性能表现,Bag的效率是最差的,由于bag容许重复的元素值,也没有索引字段,所以不可能定义主键。

Hibernate没法判断重复的行,当这种集合被更改时,Hibernate将会先完整地移除整个集合,而后再从新建立整个集合,所以Bag是很是低效的。

须要注意的是,对于一对多关联来讲,主键极可能并非数据库表的物理主键,但在此状况下,上面的分类仍然有用。

6. Lists、Maps、Sets更新性能分析

对于多对多关联、值数据集合而言,有序集合类比集合(set)有一个好处,由于Set的内在结构,若是改变了一个元素,Hibernate并不会更新这一行。

对于Set来讲,只有在插入和删除操做时“改变”才有效。

须要注意的是,数组没法延迟载入。

大概结论:list、map和idbags市最高效的(非反向)集合类型,set则紧随其后。

在Hibernate中由于set的语义在关系模型中是最天然的,因此set是最通用的集合类型。

在设计良好的Hibernate领域模型中,一般能够看到更多的集合,事实上是带有inverse="true”的一对多的关联。对于这些关联,更新操做会在多对一的这一端进行处理,所以对于此类状况,无需考虑其集合的更新性能。

7. bag和list在反向集合类中的性能分析

对于指明了inverse="true”的集合类(如标准的双向的一对多关联),能够在未初始化(fetch)包元素的状况下直接向bag或list添加新元素。

这是由于Collection.add或者addAll方法对bag或list老是返回true(这点与Set不一样)。

如:

Parent p = (Parent)session.load(Parent. class , id);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c); // 这里不会返回整个集合
session.flush();
相关文章
相关标签/搜索