缓存方案

Redis和Memcached中选择Redis,因为Redis可通过一些匹配的原则找到对应的Key,而Memcached需要在上层应用自己设计匹配规则。

缓存对象(CacheObject )

public abstract class CacheObject implements Serializable {

	private static final long serialVersionUID = 3752520699855146119L;

	@Getter @Setter private Object id;

}

选择一:缓存操作者(CacheOperator)

基类CacheOperator

对于每一种缓存的类,都需要实现其子类。

获取Jedis

private Jedis getJedis() {
		if (null == jedis) {
			try {
				ResourceBundle bundle = ResourceBundle.getBundle("cache");
				JedisPoolConfig config = new JedisPoolConfig();
				JedisPool pool = new JedisPool(config, bundle.getString("ip"), Integer.parseInt(bundle.getString("port")), 1000);
				jedis = pool.getResource();
			} catch (Exception e) {
				return null;
			}
		}
		return jedis;
	}

这里采用Jedis作为Redis的连接器,ResourceBundle.getBundle是常用的读取properties配置的方式。

// cache.properties
ip=127.0.0.1
port=6379

缓存的增删查改

/**
	 * 向缓存中增加实体
	 * @param key
	 * @param entity
	 */
	protected <T extends BaseEntity> void putEntity(String key, T entity) {
		if (null != this.getJedis()) {
			this.getJedis().set(key.getBytes(), JSON.toJSONBytes(entity));
		} else {
			logger.info("缓存服务连接失败,不能放入数据(" + entity.toString() + ")");
		}
	}

	/**
	 * 向缓存中增加实体集合
	 * @param key
	 * @param entities
	 */
	protected <T extends BaseEntity> void putEntities(String key, List<T> entities) {
		if (null != this.getJedis()) {
			this.getJedis().set(key.getBytes(), JSON.toJSONBytes(entities));
		}
	}
	
	/**
	 * 向缓存中增加缓存对象
	 * @param key
	 * @param cacheObject
	 */
	protected <T extends CacheObject> void putCacheObject(String key, T cacheObject) {
		if (null != this.getJedis()) {
			this.getJedis().set(key.getBytes(), JSON.toJSONBytes(cacheObject));
		}
	}
	
	/**
	 * 从缓存中获取实体
	 * @param key
	 * @return
	 */
	protected <T extends BaseEntity> T getEntity(String key, Class<T> clazz) {
		if (null != this.getJedis()) {
			byte[] bytes = this.getJedis().get(key.getBytes());
			if (null == bytes) {
				return null;
			}
			return JSON.parseObject(bytes, clazz);
		}
		return null;
	}

	/**
	 * 从缓存中获取缓存对象
	 * @param key
	 * @param clazz
	 * @return
	 */
	protected <T extends CacheObject> T getCacheObject(String key, Class<T> clazz) {
		if (null != this.getJedis()) {
			byte[] bytes = this.getJedis().get(key.getBytes());
			if (null == bytes) {
				return null;
			}
			return JSON.parseObject(bytes, clazz);
		}
		return null;
	}
	
	/**
	 * 从缓存中获取实体集合
	 * @param key
	 * @param clazz
	 * @return
	 */
	protected <T extends BaseEntity> List<T> getEntities(String key, Class<T> clazz) {
		try {
			if (null != this.getJedis()) {
				byte[] bytes = this.getJedis().get(key.getBytes());
				if (null == bytes) {
					return null;
				}
				return JSON.parseArray(new String(bytes, "UTF-8"), clazz);
			}
		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 从缓存中删除实体/实体集合,缓存对象
	 * @param key
	 */
	protected void delete(String key) {
		if (null != this.getJedis()) {
			this.getJedis().del(key.getBytes());
		}
	}

	/**
	 * 根据Key前缀获取对应的所有实体
	 * @param keyPrefix
	 * @param clazz
	 * @return
	 */
	protected <T extends BaseEntity> List<T> getEntitiesByKeyPrefix(String keyPrefix, Class<T> clazz) {
		if (null != this.getJedis()) {
			Set<byte[]> keySet = this.getJedis().keys((keyPrefix + "*").getBytes());
			if (keySet.size() == 0) {
				return null;
			}
			T t;
			List<T> result = new ArrayList<>();
			List<byte[]> list = this.getJedis().mget(keySet.toArray(new byte[1][keySet.size()]));
			for (byte[] bytes : list) {
				t = JSON.parseObject(bytes, clazz);
				result.add(t);
			}
			return result;
		}
		return null;
	}

	/**
	 * 根据Key前缀获取对应的所有缓存对象
	 * @param keyPrefix
	 * @param clazz
	 * @return
	 */
	protected <T extends CacheObject> List<T> getCacheObjectsByKeyPrefix(String keyPrefix, Class<T> clazz) {
		if (null != this.getJedis()) {
			Set<byte[]> keySet = this.getJedis().keys(
					(keyPrefix + "*").getBytes());
			List<byte[]> list = this.getJedis().mget(
					keySet.toArray(new byte[1][keySet.size()]));
			List<T> result = new ArrayList<T>();
			T t;
			for (byte[] bytes : list) {
				t = JSON.parseObject(bytes, clazz);
				result.add(t);
			}
			return result;
		}
		return null;
	}

注:
(1)所有的put和get操作,都分为CacheObjectBaseEntity两种,一种是拿到缓存对象,一种是拿到Entity对象,基本上一致。

缓存操作者的子类(需要缓存的类对应)

/**
     * 向缓存中增加DealCategory
     * @param dealCategory
     */
    public void putDealCategory(DealCategory dealCategory) {
        super.putEntity("DealCategory." + dealCategory.getId(), dealCategory);

        // 维护其父类别的子类别缓存
        List<DealCategory> subCategories = getSubCategories(dealCategory.getParentId());
        if (subCategories != null && subCategories.size() > 0) {
            if (subCategories.contains(dealCategory)) {
                int index = 0;
                for (int i = 0; i < subCategories.size(); i++) {
                    if (subCategories.get(i).getId() == dealCategory.getId()) {
                        index = i;
                    }
                }
                subCategories.remove(index);
            }
            subCategories.add(dealCategory);

            putSubCategories(dealCategory.getParentId(), subCategories);
        }
    }

注:
(1)put的时候,参数只有DealCategory,根据类名.+具体的ID(一定的规则)当Key,把dealCategory放入缓存中。
(2)put和delete时,需要判断缓存对象的父节点的子节点们缓存,需要更新就更新,需要删除就删除。

选择二:缓存工具(CacheUtil)

使用Util的静态方法进行缓存的操作,相比上面的Operator类来说,少了子类的重写和类初始化的操作(采用静态方法)。

获取Jedis对象

同上

生成Key

private static String generateKey(Class clazz, Object id) {
		return clazz.getName() + "." + id;
	}

根据类名+id生成Key。

存入缓存

/**
	 * 向缓存中增加实体
	 * @param key
	 * @param entity
	 */
	public static <T extends BaseEntity> void putEntity(String key, T entity) {
		if (null != getJedis()) {
			getJedis().set(key.getBytes(), JSON.toJSONBytes(entity));
		} else {
			logger.info("缓存服务连接失败,不能放入数据(" + entity.toString() + ")");
		}
	}
	public static <T extends BaseEntity> void putEntity(T entity) {
		if (null != getJedis()) {
			String key = generateKey(entity.getClass(), entity.getId());
			getJedis().set(key.getBytes(), JSON.toJSONBytes(entity));
		} else {
			logger.info("缓存服务连接失败,不能放入数据(" + entity.toString() + ")");
		}
	}

在外部可以直接调用该静态方法,两种参数模式,一种是自带Key,一种不自带Key只有entity,那么需要调用上面的生成Key的方法,但要注意在整个工程中,需要保持key的规则一致。