论坛首页 Java企业应用论坛

用Spring Cache 实现Hibernate的二级缓存机制

浏览 16296 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-05-14   最后修改:2012-05-14
   因为系统中同时采用Hibernate和Jdbc两种技术,为了提高性能我们一般打开Hibernate的二级缓存和查询缓存功能,但是无法兼顾Jdbc的变动,而且Hibernate的缓存我们无法进行微调,是位于Dao层,也影响了缓存的性能。
   因为我们有必要做出一种灵活性和性能更好的缓存来适应这种情况。spring cache已经提供了一个良好的框架和cache annotation给我们使用,但是我们需要那他来开刀,改造为我们想要的效果。由于目前对spring cache的应用介绍都是点到为止,因此我们可以直接从spring 的代码和它的文档入手。

目标
提高系统性能,在有限的资源下支持更多的用户并发量
取消并模拟Hibernate的二级缓存机制,整合Hibernate和JDBC的缓存
更高的灵活性和实时性

原则
1. 针对系统中不改动或者少量改动的数据对象,不适宜用在频繁改动的数据上
2. 缓存越靠近系统前端,表现越好

关键点
1. 设计缓存的关键字 "key"
2. 设计缓存的更新机制
3. 缓存以key - value 方式存在
..........
具体介绍已经写在ppt中,因为这个思路不一定成熟,欢迎讨论。代码如下:
spring cache 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">

	<cache:annotation-driven key-generator="stringKeyGenerator"/>
	
	<bean id="cacheManager" class="com.legendshop.business.cache.EhCacheCacheManager" 
		p:cache-manager-ref="ehcacheFactory" 
		p:supportQueryCache="true"/>
	
	<!-- Ehcache library setup -->
	<bean id="ehcacheFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" />
	
	<bean id="stringKeyGenerator" class="com.legendshop.core.cache.StringKeyGenerator" />


LegendCacheManager:继承与CacheManager,增加自己的属性:
package com.legendshop.business.cache;

import org.springframework.cache.CacheManager;

/**
 * The Interface LegendCacheManager.
 */
public interface LegendCacheManager extends CacheManager{
	
	/**
	 * 
	 * Checks if is support query cache.
	 * 是否支持查询缓存
	 * 
	 * @return true, if is support query cache
	 */
	public boolean isSupportQueryCache();
	
	/**
	 * Checks if is removes the all entries.
	 * 在不支持查询缓存的情况下起效,当Entity Cache更新时是否删除List Cache所有对象
	 * 
	 * @return true, if is removes the all entries
	 */
	public boolean isRemoveAllEntries();
	
	/**
	 * Gets the rel cache name.
	 * RelationShop 缓存名称
	 * 
	 * @return the rel cache name
	 */
	public String getRelCacheName();
}


EhCacheCacheManager: Ehcache Manager 的实现类
import java.util.Collection;
import java.util.LinkedHashSet;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Status;

import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.util.Assert;

/**
 * The Class EhCacheCacheManager.
 */
public class EhCacheCacheManager extends AbstractCacheManager implements LegendCacheManager {

	/** The cache manager. */
	private net.sf.ehcache.CacheManager cacheManager;
	
	/** The support query cache. */
	private boolean supportQueryCache;
	
	/** The remove all entries. */
	private boolean removeAllEntries;
	
	/** The rel cache name. */
	private  String relCacheName = "LEGENDSHOP_CACHE";


	/**
	 * Set the backing EhCache {@link net.sf.ehcache.CacheManager}.
	 * 
	 * @param cacheManager
	 *            the new cache manager
	 */
	public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}


	/* (non-Javadoc)
	 * @see org.springframework.cache.support.AbstractCacheManager#loadCaches()
	 */
	@Override
	protected Collection<Cache> loadCaches() {
		Assert.notNull(this.cacheManager, "A backing EhCache CacheManager is required");
		Status status = this.cacheManager.getStatus();
		Assert.isTrue(Status.STATUS_ALIVE.equals(status),
				"An 'alive' EhCache CacheManager is required - current cache is " + status.toString());

		String[] names = this.cacheManager.getCacheNames();
		Collection<Cache> caches = new LinkedHashSet<Cache>(names.length);
		for (String name : names) {
			caches.add(new LegendCache(this,this.cacheManager.getEhcache(name)));
		}
		return caches;
	}

	/* (non-Javadoc)
	 * @see org.springframework.cache.support.AbstractCacheManager#getCache(java.lang.String)
	 */
	@Override
	public Cache getCache(String name) {
		Cache cache = super.getCache(name);
		if (cache == null) {
			// check the EhCache cache again
			// (in case the cache was added at runtime)
			Ehcache ehcache = this.cacheManager.getEhcache(name);
			if (ehcache != null) {
				cache = new LegendCache(this,ehcache);
				addCache(cache);
			}
		}
		return cache;
	}


	/* (non-Javadoc)
	 * @see com.legendshop.business.cache.LegendCacheManager#isSupportQueryCache()
	 */
	@Override
	public boolean isSupportQueryCache() {
		return supportQueryCache;
	}


	/**
	 * Sets the support query cache.
	 * 
	 * @param supportQueryCache
	 *            the new support query cache
	 */
	public void setSupportQueryCache(boolean supportQueryCache) {
		this.supportQueryCache = supportQueryCache;
	}


	/* (non-Javadoc)
	 * @see com.legendshop.business.cache.LegendCacheManager#getRelCacheName()
	 */
	@Override
	public String getRelCacheName() {
		return relCacheName;
	}


	/**
	 * Sets the rel cache name.
	 * 
	 * @param relCacheName
	 *            the new rel cache name
	 */
	public void setRelCacheName(String relCacheName) {
		this.relCacheName = relCacheName;
	}


	/* (non-Javadoc)
	 * @see com.legendshop.business.cache.LegendCacheManager#isRemoveAllEntries()
	 */
	@Override
	public boolean isRemoveAllEntries() {
		return false;
	}


	/**
	 * Sets the removes the all entries.
	 * 
	 * @param removeAllEntries
	 *            the new removes the all entries
	 */
	public void setRemoveAllEntries(boolean removeAllEntries) {
		this.removeAllEntries = removeAllEntries;
	}

}


LegendCache:Ehcache的实现类
package com.legendshop.business.cache;

import java.util.Collection;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.ehcache.EhCacheCache;
import org.springframework.util.Assert;

import com.legendshop.model.entity.BaseEntity;

/**
 * The Class LegendCache.
 */
public class LegendCache implements Cache {

	/** The log. */
	private final Logger log = LoggerFactory.getLogger(LegendCache.class);

	/** The cache manager. */
	private final LegendCacheManager cacheManager;

	/** The cache. */
	private final Ehcache cache;

	/** The SUFFIX. */
	private final String SUFFIX = "List";

	/**
	 * Create an {@link EhCacheCache} instance.
	 * 
	 * @param cacheManager
	 *            the cache manager
	 * @param ehcache
	 *            backing Ehcache instance
	 */
	public LegendCache(LegendCacheManager cacheManager, Ehcache ehcache) {
		Assert.notNull(ehcache, "Ehcache must not be null");
		Status status = ehcache.getStatus();
		Assert.isTrue(Status.STATUS_ALIVE.equals(status),
				"An 'alive' Ehcache is required - current cache is " + status.toString());
		this.cacheManager = cacheManager;
		this.cache = ehcache;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#getName()
	 */
	@Override
	public String getName() {
		return this.cache.getName();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#getNativeCache()
	 */
	@Override
	public Ehcache getNativeCache() {
		return this.cache;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#clear()
	 */
	@Override
	public void clear() {
		this.cache.removeAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#get(java.lang.Object)
	 */
	@Override
	public ValueWrapper get(Object key) {
		Element element = this.cache.get(key);
		if (element != null) {
			//log.info("get from cache {} by key {}, result {}", new Object[] { getName(), key, element.getObjectValue() });
			return new LegendValueWrapper(element.getObjectValue());
		} else {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#put(java.lang.Object,
	 * java.lang.Object)
	 */
	@Override
	public void put(Object key, Object value) {
		//log.info("put into cache {} by key {}, value {}", new Object[] { getName(), key, value });
		this.cache.put(new Element(key, value));
		
		if(this.getName().endsWith(SUFFIX) && cacheManager.isSupportQueryCache() && Collection.class.isAssignableFrom(value.getClass())){
			//for list
				// 如果是列表,则保存以ID为主键的关系
					Collection<BaseEntity> coll = (Collection<BaseEntity>) value;
					for (BaseEntity entity : coll) {
						//put relevant into relcache  format: entity name + id:{[cacheName, key]}
						Cache relCache = cacheManager.getCache(cacheManager.getRelCacheName());
						String relCacheKey = this.getName().substring(0, this.getName().length() - 4) + entity.getId();
						LegendValueWrapper valueWrapper = (LegendValueWrapper) relCache.get(relCacheKey);
						if (valueWrapper == null) {
							valueWrapper = new LegendValueWrapper(entity.getId());
						}
						if (valueWrapper.addRelObject(this.getName(),key)) {
							//log.info("put into rel cache {} by key {}, value {}", new Object[] {relCacheKey, relCacheKey, valueWrapper });
							relCache.put(relCacheKey, valueWrapper);
						}
			}

		}


	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#evict(java.lang.Object)
	 */
	@Override
	public void evict(Object key) {
		//evict effect only in entity cache
		//log.info("evict from cache {} by key {}", getName(), key);
		this.cache.remove(key);
		
		//for list
		if (cacheManager.isSupportQueryCache() && !this.getName().endsWith(SUFFIX)) {
				// clean entity
				Cache relCache = cacheManager.getCache(cacheManager.getRelCacheName());
				if (relCache != null) {
					String relCacheKey = this.getName() + key;
					LegendValueWrapper valueWrapper = (LegendValueWrapper)relCache.get(relCacheKey);
					if(valueWrapper != null){
						for (CacheNameAndItemWrapper warpper : valueWrapper.getRelObject()) {
							//log.info("evict from cache {} by key {}", warpper.getCacheName(), warpper.getKey());
							cacheManager.getCache(warpper.getCacheName()).evict(warpper.getKey());
						}
				}
					//remove relcache
					relCache.evict(relCacheKey);
			}
		}else if(cacheManager.isRemoveAllEntries()){
			Cache listCache = cacheManager.getCache(this.getName() + SUFFIX);
			if(listCache != null){
				listCache.clear();
			}
		}
	}
	

}



LegendQueryCache:实现类似Hibernate的二级缓存机制,将ID list保存在List Cache中,多次和Entity交互取得全部的Entity,显然会影响一点性能。
package com.legendshop.business.cache;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.ehcache.EhCacheCache;
import org.springframework.util.Assert;

import com.legendshop.model.entity.BaseEntity;

/**
 * The Class LegendCache.
 */
public class LegendQueryCache implements Cache {

	/** The log. */
	private final Logger log = LoggerFactory.getLogger(LegendQueryCache.class);

	/** The cache manager. */
	private final LegendCacheManager cacheManager;

	/** The cache. */
	private final Ehcache cache;

	/** The SUFFIX. */
	private final String SUFFIX = "List";

	/**
	 * Create an {@link EhCacheCache} instance.
	 * 
	 * @param cacheManager
	 *            the cache manager
	 * @param ehcache
	 *            backing Ehcache instance
	 */
	public LegendQueryCache(LegendCacheManager cacheManager, Ehcache ehcache) {
		Assert.notNull(ehcache, "Ehcache must not be null");
		Status status = ehcache.getStatus();
		Assert.isTrue(Status.STATUS_ALIVE.equals(status),
				"An 'alive' Ehcache is required - current cache is " + status.toString());
		this.cacheManager = cacheManager;
		this.cache = ehcache;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#getName()
	 */
	@Override
	public String getName() {
		return this.cache.getName();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#getNativeCache()
	 */
	@Override
	public Ehcache getNativeCache() {
		return this.cache;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#clear()
	 */
	@Override
	public void clear() {
		this.cache.removeAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#get(java.lang.Object)
	 */
	@Override
	public ValueWrapper get(Object key) {
		Element element = this.cache.get(key);
		if (element != null) {
			Object value = element.getObjectValue();
			if (cacheManager.isSupportQueryCache() && this.getName().endsWith(SUFFIX)&& Collection.class.isAssignableFrom(value.getClass())) {
				//get cache from entity cache by id list
				String entityCacheName = this.getName().substring(0, this.getName().length() - 4);
				Collection<Serializable> ids =  (Collection<Serializable>)value;
				Collection<Object> result = new ArrayList<Object>();
				if(ids == null){
					return null;
				}else{
					Cache entityCache = cacheManager.getCache(entityCacheName);
					for (Serializable id : ids) {
						ValueWrapper warpper = entityCache.get(id);
						if(warpper == null){
							//if one of result is null then retrieve data again
							return null;
						}
						result.add(warpper.get());
					}
					return new LegendValueWrapper(result);
				}
				
			}else{
				log.info("get from cache {} by key {}, result {}", new Object[] { getName(), key, value });
				return new LegendValueWrapper(value);
			}

		} else {
			return null;
		}
		
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#put(java.lang.Object,
	 * java.lang.Object)
	 */
	@Override
	public void put(Object key, Object value) {
		if(this.getName().endsWith(SUFFIX) && cacheManager.isSupportQueryCache() && Collection.class.isAssignableFrom(value.getClass())){
			//for list
				// 如果是列表,则保存以ID为主键的关系
					Collection coll = (Collection) value;
					Collection<Serializable> entityId = new ArrayList<Serializable>();
					for (Object entityObj : coll) {
						//put into entity cache for each row, format: entity id: entity
						String entityCacheName = this.getName().substring(0, this.getName().length() - 4);
						Cache entityCache = cacheManager.getCache(entityCacheName);
						BaseEntity entity = (BaseEntity)entityObj;
						entityId.add(entity.getId());
						entityCache.put(entity.getId(), entityObj);
						
						//put relevant into relcache  format: entity name + id:{[cacheName, key]}
						Cache relCache = cacheManager.getCache(cacheManager.getRelCacheName());
						String relCacheKey = entityCacheName + entity.getId();
						LegendValueWrapper valueWrapper = (LegendValueWrapper) relCache.get(relCacheKey);
						if (valueWrapper == null) {
							valueWrapper = new LegendValueWrapper(entity.getId());
						}
						if (valueWrapper.addRelObject(this.getName(),key)) {
							log.info("put into cache {} by key {}, value {}", new Object[] {relCacheKey, entity.getId(), valueWrapper });
							relCache.put(relCacheKey, valueWrapper);
						}
			}
					//put entity id list into cache
					cache.put(new Element(key, entityId));
		}else{
			//only one entity
			log.info("put into cache {} by key {}, value {}", new Object[] { getName(), key, value });
			this.cache.put(new Element(key, value));
		}
		
		

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.cache.Cache#evict(java.lang.Object)
	 */
	@Override
	public void evict(Object key) {
		//evict effect only in entity cache
		log.info("evict from cache {} by key {}", getName(), key);
		this.cache.remove(key);
		
		//for list
		if (cacheManager.isSupportQueryCache() && !this.getName().endsWith(SUFFIX)) {
				// clean entity
				Cache relCache = cacheManager.getCache(cacheManager.getRelCacheName());
				if (relCache != null) {
					String relCacheKey = this.getName() + key;
					LegendValueWrapper valueWrapper = (LegendValueWrapper)relCache.get(relCacheKey);
					if(valueWrapper != null){
						for (CacheNameAndItemWrapper warpper : valueWrapper.getRelObject()) {
							log.info("evict from cache {} by key {}", warpper.getCacheName(), warpper.getKey());
							cacheManager.getCache(warpper.getCacheName()).evict(warpper.getKey());
						}
				}
					//remove relcache
					relCache.evict(relCacheKey);
			}
		}
	}
	

}


StringKeyGenerator:需要改写key的生成策略
package com.legendshop.core.cache;

import java.lang.reflect.Method;

import org.springframework.cache.interceptor.KeyGenerator;

/**
 * 实现新的key generator基于两点原因: 
 * 1.memcached的键都是string; 
 * 2.分布式环境不能用hashcode.
 * 
 */
public class StringKeyGenerator implements KeyGenerator {

	/* (non-Javadoc)
	 * @see org.springframework.cache.interceptor.KeyGenerator#generate(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object generate(Object target, Method method, Object... params) {
		StringBuilder sb = new StringBuilder(50);
		sb.append(target.getClass().getName());
		sb.append('.');
		sb.append(method.getName());
		sb.append('.');
		for (Object o : params) {
			if(o != null){
				sb.append(o.toString());
				sb.append('.');
			}
		}
		return sb.toString();
	}

}


另外需要在ehcache.xml中把Cache name定义好,否则CacheManager会找不到Cache。

使用查询缓存需要遵循Hibernate类似的规则,详细见ppt。

由于这些代码不是一个简单的Demo,各个功能分布于系统各个部分,如果需要看效果,则需要从svn把真个系统拿下来再运行,目前已经在Ehcache环境下测试通过。

   发表时间:2012-05-14  
关键是怎么拓展spring现有的几个cache annotation,难道你要全部重写?
0 请登录后投票
   发表时间:2012-05-14   最后修改:2012-05-14
希望能快点上传~
能实现联动的无非就是 entity cache 和list cache了
0 请登录后投票
   发表时间:2012-05-14  
2. 缓存越靠近系统前端,表现越好  : 即数据越接近于用户越好
0 请登录后投票
   发表时间:2012-05-14  
aa87963014 写道
希望能快点上传~
能实现联动的无非就是 entity cache 和list cache了

annotation是重用spring的,@caching已经满足我们大部分情况了,内部cache如何划分就要看自己的设计了。 我们在cache的get和set入口开始做文章,当然用ehcache的cache event linstner也是可以实现的,但是就是不通用了。我们还要支持memcached的.我们还要支持memcached等其他的cache,因此跟逻辑相关的都要统一处理.
0 请登录后投票
   发表时间:2012-05-14  
1、Entity更新时从RelationShop找到对应的List并更新
  这个不需要吧 因为你的List存的只是ID啊

2、RelationShop cache 以Entity ID 作为key, 记录引用到这个Entity的List的所在的cache名称和key,用于当Entity更新时找到他所影响的List一起做更新操作
    建议上一个时间戳缓存,类似于Hibernate,我觉得要比RelationShop好。

3、当Entity Create Delete时应该直接删除List/修改时间戳吧?

4、你们现在 List如何保存呢? 如何得到Entity类型? 如果List为空呢?

几点疑问和见解
0 请登录后投票
   发表时间:2012-05-14  
想了下,由entity的改变更新List 要加上很多限制,例如:list 保存的是 num>0的entity
现在其中一个entity 的num变为了0,那么list就是错误的结果。

这个需要使用者自己掂量怎么去维护关系
0 请登录后投票
   发表时间:2012-05-14  
jinnianshilongnian 写道
1、Entity更新时从RelationShop找到对应的List并更新
  这个不需要吧 因为你的List存的只是ID啊

2、RelationShop cache 以Entity ID 作为key, 记录引用到这个Entity的List的所在的cache名称和key,用于当Entity更新时找到他所影响的List一起做更新操作
    建议上一个时间戳缓存,类似于Hibernate,我觉得要比RelationShop好。

3、当Entity Create Delete时应该直接删除List/修改时间戳吧?

4、你们现在 List如何保存呢? 如何得到Entity类型? 如果List为空呢?

几点疑问和见解


1。刚才提到了List cache有2种方案,1.放ID List, 2. 放整个List。比如这个List下的某个Entity更新或者删除了,那这个List就要作废了,需要重新从数据库拿记录。这2种方案在其关联的Entity改变之后,都要干掉List Cache
2。RelationShop cache 的数据结构是这样的, Entity ID:{[cachename1, key1],[cachename2, key2]...}, 不知道你说的时间戳是否ehcache的create time?
3. Entity Cache 更新时通过RelationShop cache找到他所影响的List Cache 并删除,跟着删除对应的RelationShop cache
4. Cache name就是一个Entity name, 每个Entity实现一个BadeEntity的接口,里面有一个getId()的方法需要实现,对Cache而言,我不需要知道Entity的类型,只要知道他的Id作为Key就好,value的类型是Object. List为空也是一种结果缓存下来的,只是他不受任何Entity的影响。
0 请登录后投票
   发表时间:2012-05-14  
onecan 写道
jinnianshilongnian 写道
1、Entity更新时从RelationShop找到对应的List并更新
  这个不需要吧 因为你的List存的只是ID啊

2、RelationShop cache 以Entity ID 作为key, 记录引用到这个Entity的List的所在的cache名称和key,用于当Entity更新时找到他所影响的List一起做更新操作
    建议上一个时间戳缓存,类似于Hibernate,我觉得要比RelationShop好。

3、当Entity Create Delete时应该直接删除List/修改时间戳吧?

4、你们现在 List如何保存呢? 如何得到Entity类型? 如果List为空呢?

几点疑问和见解


1。刚才提到了List cache有2种方案,1.放ID List, 2. 放整个List。比如这个List下的某个Entity更新或者删除了,那这个List就要作废了,需要重新从数据库拿记录。这2种方案在其关联的Entity改变之后,都要干掉List Cache
2。RelationShop cache 的数据结构是这样的, Entity ID:{[cachename1, key1],[cachename2, key2]...}, 不知道你说的时间戳是否ehcache的create time?
3. Entity Cache 更新时通过RelationShop cache找到他所影响的List Cache 并删除,跟着删除对应的RelationShop cache
4. Cache name就是一个Entity name, 每个Entity实现一个BadeEntity的接口,里面有一个getId()的方法需要实现,对Cache而言,我不需要知道Entity的类型,只要知道他的Id作为Key就好,value的类型是Object. List为空也是一种结果缓存下来的,只是他不受任何Entity的影响。

2。RelationShop cache 的数据结构是这样的, Entity ID:{[cachename1, key1],[cachename2, key2]...}, 不知道你说的时间戳是否ehcache的create time?

不是create time  是修改的时间。 hibernate也是采用这种时间戳方式

比如你有个Entity 有一个 如 s属性 原来为1  后来修改成 2    但你有一个查询 条件为s>1  这样你的List可能有几个失效的。 这个你的场景会遇到吗?
0 请登录后投票
   发表时间:2012-05-14  
aa87963014 写道
想了下,由entity的改变更新List 要加上很多限制,例如:list 保存的是 num>0的entity
现在其中一个entity 的num变为了0,那么list就是错误的结果。

这个需要使用者自己掂量怎么去维护关系


那这个List就等于失效了,你可以重新填充Entity Cache并找到他对应的List Cache也把新的数据填充在里面,不过我建议是直接remove掉更方便了。这种方案只是影响部分List Cache,提高了实时性,要比一有改动就把List Cache整个干掉来的划算。而且在我们程序中,我们可以配置成任何一个Entity改动,其对应的List Cache全部干掉,这个Spring cache默认就支持了,另外我们也可以不开启这种模式,那等于普通的Spring cache了,所以灵活性是掌握在我们手里的.
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics