前言:strawberry:
在访问量大的时候,为了提高查询效率,我们会将数据先缓存到redis中。先查询redis,查询不到再去查询数据库,实现这个逻辑也不复杂,写一个 if(...)else{...}
也就行了。这种做法也不是不行,就是看起来有点儿粗糙,所以我想换一种更优雅的写法。
举个栗子:chestnut:
现有一个使用商品名称查询商品的需求,要求先查询缓存,查不到则去数据库查询;从数据库查询到之后加入缓存,再查询时继续先查询缓存。
思路分析
可以写一个条件判断,伪代码如下:
//先从缓存中查询 String goodsInfoStr = redis.get(goodsName); if(StringUtils.isBlank(goodsInfoStr)){ //如果缓存中查询为空,则去数据库中查询 Goods goods = goodsMapper.queryByName(goodsName); //将查询到的数据存入缓存 goodsName.set(goodsName,JSONObject.toJSONString(goods)); //返回商品数据 return goods; }else{ //将查询到的str转换为对象并返回 return JSON.parseObject(goodsInfoStr, Goods.class); } 复制代码
上面这串代码也可以实现查询效果,看起来也不是很复杂,但是这串代码是 不可复用的
,只能用在这个场景。假设在我们的系统中还有很多类似上面商品查询的需求,那么我们需要到处写这样的 if(...)else{...}
。作为一个程序员,不能把类似的或者重复的代码统一起来是一件很难受的事情,所以需要对这种场景的代码进行优化。
上面这串代码的问题在于: 入参不固定、返回值也不固定,如果仅仅是参数不固定,使用泛型即可。但最关键的是查询方法也是不固定的,比如查询商品和查询用户肯定不是一个查询方法吧。
所以如果我们可以把一个方法(即上面的各种查询方法)也能当做一个参数传入一个统一的判断方法就好了,类似于:
/** * 这个方法的作用是:先执行method1方法,如果method1查询或执行不成功,再执行method2方法 */ public static<T> T selectCacheByTemplate(method1,method2) 复制代码
想要实现上面的这种效果,就不得不提到Java8的新特性: 函数式编程
原理介绍
在Java中有一个package: java.util.function
,里面全部是接口,并且都被 @FunctionalInterface
注解所修饰。
Function分类
- Consumer(消费):接受参数,无返回值
- Function(函数):接受参数,有返回值
- Operator(操作):接受参数,返回与参数同类型的值
- Predicate(断言):接受参数,返回boolean类型
- Supplier(供应):无参数,有返回值
具体我就不在赘述了,可以参考: blog.youkuaiyun.com/hua226/arti…
代码实现:cake:
那么接下来就来使用Java优雅的实现先查询缓存再查询数据库吧!