hibernate的缓存

hibernate缓存按照大类分包括事物级别的缓存(session),应用级的缓存(sessionFactory),分布式的缓存
事物级别的缓存是基于session的声明周期实现的。session内部会存在这样的一个缓存。
应用级别的缓存:实际上是基于插件实现的,就是sessionFactory上的缓存。也是我们熟称的二级缓存。
分布式的缓存就是多个应用实例,多个JVM之间共享的缓存策略,通过远程机制实现各个缓存实例之间的数据同步。
hibernate的缓存细分分为对象缓存和查询缓存,查询缓存必须以对象缓存作为基础才能起作用否则效率会很低。
对象缓存是缓存查询的实体对象到内存。这样下次查找的时候就会直接去缓存查找对象从而避免了去数据库获取。
查询缓存实际上是缓存了关联对象的id,而后会根据这个id去对象缓存中查找实体对象,若是查不到再去查询数据库。
hibernate中二级缓存是以插件的方式实现的。
hibernate3默认的二级缓存使用的是EhCache。
hibernate3版本中的二级缓存配置如下
Hibernate.cfg.xml
启用class缓存:
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
启用查询缓存:
<property name="hibernate.cache.use_query_cache">true</property>
如果spring集成hibernate时,配置二级缓存的方式如下
启用class缓存:
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
</prop>
启用查询缓存:
<prop key="hibernate.cache.use_query_cache">true</prop>
配置ehcache,将ehcache.xml放到classpath下面
配置样式如下:
<diskStore path="java.io.tmpdir" />

<!--默认的缓存配置  -->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" />
    <!-- 如果需要对某个类特殊配置按照如下的方式,sampleCache1代表实体类的全路径 -->
<cache name="sampleCache1" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" />
<!-- 如果需要为查询缓存单独配置按照下民的方式,否则默认会让查询缓存使用默认的缓存策略 -->
<cache name="org.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" timeToIdleSeconds="1800"
timeToLiveSeconds="0" overflowToDisk="true" />
<cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="1800"
timeToLiveSeconds="0" overflowToDisk="true" />

<!-- 注意事项
“当hibernate更新数据库的时候,它怎么知道更新哪些查询缓存呢? hibernate在一个地方维护每个表的最后更新时间,其实也就是放在上面UpdateTimestampsCache所指定的缓存配置里面。
当通过hibernate更新的时候,hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表,当hibernate查询一个缓存是否存在的时候,如果缓存存在,它还要取出缓存的生成时间和这个缓存所查询的表,然后去查找这些表的最后更新时间,如果有一个表在生成时间后更新过了,那么这个缓存是无效的。
可以看出,只要更新过一个表,那么凡是涉及到这个表的查询缓存就失效了,因此查询缓存的命中率可能会比较低。”
-->
对对象使用二级缓存,两种方式
即在映射文件中或是使用注解
映射文件中:
<class name="Product" table="product" >
<cache usage="read-write"/> <id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="increment"/>
</id>
<property name="name" type="java.lang.String"/>
<property name="pDesc" type="java.lang.String"/>
<property name="orderId" type="java.lang.Integer"/>
</class>
注解的方式:
@Entity
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Forest { ... }

对于session级别的缓存如果使用get方式会只执行一条,如果关闭了session会执行多条

对对象的关联结合进行二级缓存,需要在配置文件中进行。且只对onetomany有效,只会缓存关联集合的对象ID,如果要完全缓存关联对象需要对关联对象也进行二级缓存
配置如下:
<set name="children" lazy="true" order-by="treeid asc">
<cache usage="read-write"/>
<key column="PARENTID"/>
<one-to-many class="SysCodelist"/>
</set>
集合缓存是独立的,不受关联对象的删除、修改的影响。
无论是list,load,iterator查询出的对象都会放入class缓存
当修改某个对象,hibernate会根据id从缓存中移除该对象。
ehcache缓存的策略有:
read-only
read-write
nonstrict-read-write 不严格的读写,即任务两个事物更新同一个记录可能性比较小,性能会比read-write好一些
事务缓存(transactional):当发生异常时可以回滚,只用于jta的方式。
读写缓存会读要操作的缓存数据进行加锁,若是其他事物要操作该数据时发现加锁了就要去查询数据库。不严格读写缓存不锁住缓存中的数据。

查询缓存:查询缓存的目的就是为了通过list()方法的查询结果放入缓存中,并实现对语句的缓存,如果下次用户使用同样的查询语句的时,会直接从查询结果中获取对象的数据。
可见查询缓存实际上是缓存了以sql语句为key,查询结果为value的。
当第一次启用查询缓存的session进行语句查询的时候,系统只执行一次将全部的查询对象取出,然后放入class缓存。语句和id集合存入查询缓存。而当第二次执行相同语句的查询时,先会去查询缓存中查找,如果有,就取出每个对象id去class缓存中查找,若是此时对象的缓存没有启动或是过期,那么session活根据id一个个的load,就会出现select N+1的问题。

实际开发中并不是对所有的对象进行二级缓存的。但是spring中提供的hibernate方法虽然有getHibernateTemplate().setCacheQueries(),但是影响是全局的。一旦启用将会对不需要缓存的查询照成不良的影响。
因此可以自己写一个方法
public List findByCachedQuery(final String hql)
    {
        return (List) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException {
                Query queryObject = session.createQuery(hql);
                queryObject.setCacheable(true);
                if (getHibernateTemplate().getQueryCacheRegion() != null) {
                    queryObject.setCacheRegion(getHibernateTemplate().getQueryCacheRegion());
                }
                return queryObject.list();
            }
        }, true);
    }
将只在session范围内启用查询缓存。
一旦session结束那么查询缓存也恢复到默认配置。

注意如果使用二级缓存,那么所有对数据库的修改都要走hibernate,如果使用sql将会对二级缓存没有影响,会让二级缓存与数据数据不一致。

如果调用存储过程,hibernate也是不会知道的
在配置文件中打开查询二级缓存的方式:
hibernate.cache.use_query_cache true
显示编程设置二级缓存
对query的的方法
List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .setEntity("blogger",blogger) .
setMaxResults(15) .
setCacheable(true) .
setCacheRegion("frontpages") .
list();
注意session.find()方法永远去查询数据库,不会去二级缓存中查找数据的

使用hibernate二级缓存的主要原因是因为hibernate的session缓存声明周期非常短,
session缓存主要的作用是为了保存session中的数据与数据库的同步。
因此对系统性能的提升非常少。所以为了提升系统的性能常常使用了
延迟加载、模切外链接、查询过滤。还需要配置二级缓存。

hibernate的iterator有著名的N+1问题,但是通常只有第一次加载的时候有这样的问题。
后面的性能会得到极大的提升。该方法适用于查询数据量较大的情况。

但是对数据量特别大的数据,如流水数据需要指定特殊的缓存策略。以防止大量数据载入内存导致的内存溢出。

使用hibernate的hql语句和save等方法更新数据的同时也会更新缓存中的数据。

你可能感兴趣的:(hibernate的缓存)