hibernate.cache.use_structured_entries
Hibernate文档上介绍,该属性是用于把对象以一种更易读的方式放到二级缓存中,这样,在对二级缓存进行监控的时候就更容易理解放到二级缓存中对象的内容。只需要简单的设置
hibernate.cache.use_structured_entries true
即可。
那这个属性到底有什么作用呢?先简单看一下效果:准备数据代码和映射文件在前面一篇帖子中有:
http://blog.csdn.net/stefwu/article/details/10349407
下面是测试代码:
@Test public void testStatistics4() { Session session = HibernateUtil.getInstance().getSession(); session.get(Department.class, 1l); Statistics st = HibernateUtil.getInstance().getSf().getStatistics(); //得到二级缓存区域统计信息 SecondLevelCacheStatistics scs = st .getSecondLevelCacheStatistics("itcast.DEPT"); //得到该二级缓存区域中缓存的实体对象,是一个Map,遍历这个map for(Object o:scs.getEntries().entrySet()){ AbstractReadWriteEhcacheItemReader.readItem(((Entry)o).getValue()); } session.close(); }
该代码中有一些东西先解释一下,首先,通过SecondLevelCacheStatistics的getEntries()方法得到存放在该二级缓存区域中的实体集,接着遍历这个实体集,每一个Entry的value就是存放的实体,但是,该实体是一个org.hibernate.cache.ehcache.internal.strategy.AbstractReadWriteEhcacheAccessStrategy.Item或者org.hibernate.cache.ehcache.internal.strategy.AbstractReadWriteEhcacheAccessStrategy.Lock(锁定缓存对象,准备操作的时候)对象。其中,Item对象是用于包装真正缓存对象的东西,在其上有一个getValue()方法,该方法返回的就是保存的结构化(注意这个结构化,就是由hibernate.cache.use_structured_entries控制)的缓存对象;Lock对象是用于在锁定一个对象(只会锁定其ID,即Class#id)时生成的临时的锁对象,所以,真正我们需要观察的其实是Item对象。但是,这两个对象都是protected,所以都无法直接访问,所以,我们得写一个类专门用于访问这两个对象:
package org.hibernate.cache.ehcache.internal.strategy; import org.hibernate.cache.ehcache.internal.strategy.AbstractReadWriteEhcacheAccessStrategy.Item; import org.hibernate.cache.ehcache.internal.strategy.AbstractReadWriteEhcacheAccessStrategy.Lock; public class AbstractReadWriteEhcacheItemReader { public static void readItem(Object item){ if(item instanceof Item){ System.out.println(((Item)item).getValue()); }else if(item instanceof Lock){ System.out.println(item); } } }
注意,包一定是org.hibernate.cache.ehcache.internal.strategy。
那么我们运行上面的测试代码就会看到输出:
{emps=1, _version=null, _lazyPropertiesUnfetched=false, name=d1, _subclass=cd.itcast.hibernate.day2.many2one.Department}
很明显,hibernate将缓存的对象变成了一个更容易理解的Map对象,在对象上有三个额外的列:_version,_subclass和_lazyPropertiesUnfetched;其中_version代表该对象的版本;_subclass代表缓存对象的真实类型(因为在从缓存把这个对象拿出去的时候,还需要靠这个标记来还原对象类型);_lazyPropertiesUnfetched代表该对象上是否还有延迟加载的属性没有获取;
而假设我们把hibernate配置还原:
hibernate.cache.use_structured_entries false
再次运行测试,控制台输出:
CacheEntry(cd.itcast.hibernate.day2.many2one.Department)[d1,1]
可以看到,如果没有格式化,那么hibernate直接保存到缓存中的是一个CacheEntry对象。
了解了对象是怎么存放在二级缓存中的,我们来看看更深一点点的东西,到底hibernate.cache.use_structured_entries这个配置控制了什么?
其实这个问题只需要简单的DEBUG一下hibernate,就能很清楚的看到整个过程,简单叙述如下:
1,在hibernate中,缓存的读取是靠EntityRegionAccessStrategy这个接口控制的,简单说,这个类控制了二级缓存的操作策略,怎么添加数据,怎么得到数据,怎么修改数据,怎么删除数据;而数据以什么样的格式存放到二级缓存中,又怎么读取成对象,这个过程由CacheEntryStructure这个接口控制;如果hibernate.cache.use_structured_entries设置为false(默认),则hibernate选择UnstructuredCacheEntry来作为存储结构,如果设置为true,则hibernate选择StructuredCacheEntry来作为存储结构。
2,在CacheEntryStructure中有两个非常重要的方法:
public interface CacheEntryStructure { //怎么把CacheEntry对象变成要保存到二级缓存中的数据结构 public Object structure(Object item); //怎么把二级缓存中得到的数据结构重新变成CacheEntry对象 public Object destructure(Object map, SessionFactoryImplementor factory); }
这两个方法很好理解,简单来说就是CacheEntry对象到二级缓存中数据结构的Mapping方法。
3,在把对象存放到二级缓存中的时候,先通过CacheEntryStructure把要存放的value变成对应的存放结构:
cacheEntry = persister.getCacheEntryStructure().structure(ce);
然后调用RegionAccessStrategy上的putFromLoad(Object key,Object value,long txTimestamp,Object version)方法,把格式化之后的数据存放:
region.put( key, new Item( value, version, region.nextTimestamp() ) );
4,在从二级缓存中得到对象的时候,首先通过RegionAccessStrategy上的get(Object key,long txTimestamp)得到二级缓存中存放的对应的数据:
Object ce = persister.getCacheAccessStrategy().get( ck, source.getTimestamp() );
然后再使用CacheEntryStructure的destructure方法把对象重新变成CacheEntry给hibernate:
CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, factory );
如果是设置hibernate.cache.use_structured_entries=true的时候,在读取的时候都会有个格式的转换过程,而hibernate.cache.use_structured_entries=false的时候,直接存放的就是CacheEntry,所以,建议在生产环境中还是不要设置这个属性,在对二级缓存进行调试的时候,再配置这个属性。
从配置文件入手去阅读hibernate的源代码,可以看到很多有趣的东西,而且代码也很集中。大家不妨试试。