(转)淘淘商城系列——在业务逻辑中添加缓存

http://blog.csdn.net/yerenyuan_pku/article/details/72871268

上文我们一起学习了如何使用Spring容器来管理Redis单机版和集群版实现,本文我们将一起学习如何在业务逻辑中添加缓存。 
我们首先应该明了一个道理,在业务逻辑中添加缓存的一个指导思想就是添加缓存不能影响正常业务逻辑。那么应该怎么添加缓存呢?我们可看看首页大广告的展示流程,如下图所示。 

从上面的流程图可知,我们应在调用服务层的服务查询首页内容时添加缓存,其实在首页中我们只实现了首页大广告的展示,说得更具体点应是在查询内容列表时添加缓存,添加缓存的步骤如下:

  1. 查询数据库之前先查询缓存。
  2. 查询到结果,直接响应结果。
  3. 查询不到结果,即缓存中没有,那么需要查询数据库。
  4. 把查询结果添加到缓存中。
  5. 返回结果。

我们理清了这个流程,那么接下来就考虑一个问题,具体地我们要向Redis里面保存什么东西,Redis里面存放的都是key-value形式的数据,但是里面的内容全部都是字符串,我们不可能把一个java对象放到Redis数据库里面,但有时又有可能要存一个java对象进去,那么这时候该怎么存呢?我们应该把java对象转换成json字符串,这时候就可以存进去了。OK,key应该用内容分类id作为key,我们当时查询内容信息的时候也是根据内容分类id去查的,那么这时候我们要把缓存放到Redis数据库中去,所以这个key也应该是内容分类id吧!value呢?我们之前是根据内容分类id查询到一个内容列表,很显然这个内容列表就是一个java对象,我们可以把它转成json,然后把它存到Redis里面去就可以了。 
key和value想明白了,那还有一个问题,我们这个缓存不可能只是为内容作缓存,有可能还会对商品、用户、…作缓存,这个时候key就会很多,用内容分类id作为key的话,有可能会重复,如果要是重复了,就会跟别的key冲突,要是发生冲突了,那么这个缓存数据就不对了。那么这时候怎么办?我们要防止key冲突,我们得对这个key进行归类,可以使用hash来对其进行归类。 
我们是以首页大广告位的展示为例,我们便给hkey(分类)起个名字,叫”CONTENT_KEY”,为了不把代码写死,我们把它放到配置文件当中,如下图所示。 

我们在taotao-content-service工程中添加了一个resource.properties文件,所以需要在Spring容器中扫描进去,我们把applicationContext-dao.xml文件中加载配置文件这一行做下修改,将db.properties修改为*.properties。这样properties目录下所有的以properties结尾的文件都会被加载进来。 

下面我们修改taotao-content-service工程下的ContentServiceImpl类中的getContentList方法,如下所示。

@Override
public List<TbContent> getContentList(long cid) {
    // 查询数据库之前,先查询缓存,并且添加缓存不能影响正常业务逻辑
    try {
        String json = jedisClient.hget(CONTENT_KEY, cid + "");
        // 判断是否命中缓存,判断json字符串是否为null或""
        if (StringUtils.isNotBlank(json)) {
            // 把这个json转换成List集合
            List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
            return list;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 根据内容分类id查询内容列表
    TbContentExample example = new TbContentExample();
    // 设置查询条件
    Criteria criteria = example.createCriteria();
    criteria.andCategoryIdEqualTo(cid);
    // 执行查询
    List<TbContent> list = contentMapper.selectByExample(example);
    // 向缓存中保存结果,并且添加缓存不能影响正常业务逻辑
    try {
        jedisClient.hset(CONTENT_KEY, cid + "", JsonUtils.objectToJson(list));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return list;
}

我们可以在以下代码处

String json = jedisClient.hget(CONTENT_KEY, cid + "");

打断点查看一下第一次访问首页大广告位和第二次访问首页大广告位的情况。 
缓存有个问题就是如果数据库表中的数据做了修改,缓存是需要同步的,否则查询的还是老数据,因此凡是涉及增、删、改的操作都需要同步缓存。我们以添加内容为例,应将ContentServiceImpl类中的insertContent方法修改为:

@Override
public TaotaoResult insertContent(TbContent content) {
    // 补全pojo的属性
    content.setCreated(new Date());
    content.setUpdated(new Date());
    // 向内容表中插入数据
    contentMapper.insert(content);

    // 做缓存同步,清除redis中内容分类id对应的缓存信息
    jedisClient.hdel(CONTENT_KEY, content.getCategoryId().toString());

    return TaotaoResult.ok();
}

为方便大家复制,现将ContentServiceImpl类的代码贴出。

/**
 * 内容管理Service
 * <p>Title: ContentServiceImpl</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version 1.0
 */
@Service
public class ContentServiceImpl implements ContentService {

    @Autowired
    private TbContentMapper contentMapper;

    @Autowired
    private JedisClient jedisClient;

    @Value("${CONTENT_KEY}")
    private String CONTENT_KEY; 

    @Override
    public TaotaoResult insertContent(TbContent content) {
        // 补全pojo的属性
        content.setCreated(new Date());
        content.setUpdated(new Date());
        // 向内容表中插入数据
        contentMapper.insert(content);

        // 做缓存同步,清除redis中内容分类id对应的缓存信息
        jedisClient.hdel(CONTENT_KEY, content.getCategoryId().toString());

        return TaotaoResult.ok();
    }

    @Override
    public List<TbContent> getContentList(long cid) {
        // 查询数据库之前,先查询缓存,并且添加缓存不能影响正常业务逻辑
        try {
            String json = jedisClient.hget(CONTENT_KEY, cid + "");
            // 判断是否命中缓存,判断json字符串是否为null或""
            if (StringUtils.isNotBlank(json)) {
                // 把这个json转换成List集合
                List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
                return list;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 根据内容分类id查询内容列表
        TbContentExample example = new TbContentExample();
        // 设置查询条件
        Criteria criteria = example.createCriteria();
        criteria.andCategoryIdEqualTo(cid);
        // 执行查询
        List<TbContent> list = contentMapper.selectByExample(example);
        // 向缓存中保存结果,并且添加缓存不能影响正常业务逻辑
        try {
            jedisClient.hset(CONTENT_KEY, cid + "", JsonUtils.objectToJson(list));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

}
  • 1

下面我们便来添加一条广告,那么会先清除缓存然后再去查询数据库。 

 
原文地址:https://www.cnblogs.com/telwanggs/p/6961643.html