使用redis和fastjson做应用和mysql之间的缓存
第一次做這種javaweb的項(xiàng)目,難免還是要犯很多錯(cuò)誤。
大概也知道,redis常常被用來做應(yīng)用和mysql之間的緩存。模型大概是這樣子的。
為了讓redis能夠緩存mysql數(shù)據(jù)庫中的數(shù)據(jù),我寫了很多這樣類似的代碼:
原來的查詢商品
public Product selectProductById(int id) {Product product = productMapper.selectByPrimaryKey(id);if (product != null) {String detail = product.getDetail();if (detail != null) {product.setDetail(HtmlUtils.string2Html(detail));// 進(jìn)行html轉(zhuǎn)義,替換html轉(zhuǎn)義符}}return product; }用redis緩存之后的查詢商品
public Product selectProductById(int id) {Product product = JSONObject.parseObject(redisCli.get(PRODUCT_KEY + id), Product.class);if (product != null) {product = productMapper.selectByPrimaryKey(id);String detail = product.getDetail();if (detail != null) {product.setDetail(HtmlUtils.string2Html(detail));// 進(jìn)行html轉(zhuǎn)義,替換html轉(zhuǎn)義符}redisCli.set(PRODUCT_KEY + product.getId(), JSONObject.toJSON(product).toString(),30);}return product; }老板說,不行啊,網(wǎng)站首頁太慢了!于是我們又開始在ModelAndView上做文章。
原來首頁的代碼
于是我們又加了這樣的代碼:
@RequestMapping("/wxIndex/{id}") public ModelAndView goWxIndex(HttpServletRequest request, HttpServletResponse response,@PathVariable(value = "id") Integer id) {ModelAndView mv = JSONObject.parseObject(redisCli.get("index"),ModelAndView.class);if(mv != null){return mv;}mv = new ModelAndView();mv.setViewName(ViewNameConstant.WXINDEX); //一些邏輯代碼redisCli.put("index",JSONObject.toString(mv),30);return mv; }于是代碼越來越亂。
慢慢學(xué)習(xí)和適應(yīng)spring的思想中,明白,我們可以使用攔截的方式去做mysql的緩存。我們攔截到一個(gè)sql語句,于是把這條sql語句作為key,把返回的結(jié)果作為value保存到redis里面去,失效時(shí)間為30秒鐘;
期間如果發(fā)現(xiàn)一個(gè)有insert或者update就把對(duì)應(yīng)表的所有的緩存給清理掉。
有了思想就下手去做好了。不曾想發(fā)現(xiàn)mybatis已經(jīng)提供了對(duì)應(yīng)好的緩存的接口Cache,思想和上述完全一致。
那么我們也就是用他的接口好了。
mybatis默認(rèn)緩存是PerpetualCache,可以查看一下它的源碼,發(fā)現(xiàn)其是Cache接口的實(shí)現(xiàn);那么我們的緩存只要實(shí)現(xiàn)該接口即可。
該接口有以下方法需要實(shí)現(xiàn):
public abstract interface CacheString getId();int getSize();void putObject(Object key, Object value); Object getObject(Object key); Object removeObject(Object key);void clear();ReadWriteLock getReadWriteLock(); }最重要的兩個(gè)接口是putObject和getObject;任何select語句都會(huì)首先請(qǐng)求getObject函數(shù),如果返回為null,那么再去請(qǐng)求mysql數(shù)據(jù)庫;我們?cè)趍ysql中取到數(shù)據(jù)之后,調(diào)用putObject函數(shù),進(jìn)行緩存數(shù)據(jù)的保存。
序列圖為:
網(wǎng)上提供的案例,大部分是這樣子:
public class MybatisRedisCache implements Cache {private RedisCli redisCli;@Override??public?void?putObject(Object?key,?Object?value)?{??logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>putObject:"+key+"="+value);??redisCli.set(SerializeUtil.serialize(key.toString()),?SerializeUtil.serialize(value));??}??@Override??public?Object?getObject(Object?key)?{??Object?value?=?SerializeUtil.unserialize(redisCli.get(SerializeUtil.serialize(key.toString())));??logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>getObject:"+key+"="+value);??return?value;??}?? }public?class?SerializeUtil?{??public?static?byte[]?serialize(Object?object)?{??ObjectOutputStream?oos?=?null;??ByteArrayOutputStream?baos?=?null;??try?{??//序列化??baos?=?new?ByteArrayOutputStream();??oos?=?new?ObjectOutputStream(baos);??oos.writeObject(object);??byte[]?bytes?=?baos.toByteArray();??return?bytes;??}?catch?(Exception?e)?{??e.printStackTrace();??}??return?null;??}??public?static?Object?unserialize(byte[]?bytes)?{??ByteArrayInputStream?bais?=?null;??try?{??//反序列化??bais?=?new?ByteArrayInputStream(bytes);??ObjectInputStream?ois?=?new?ObjectInputStream(bais);??return?ois.readObject();??}?catch?(Exception?e)?{??}??return?null;??}? }?如果是通過java提供的序列化進(jìn)行實(shí)體類和String的轉(zhuǎn)換,那么我們要修改所有已經(jīng)存在的實(shí)體Bean類,工作量太大;而且java的序列化效率又低;我們還是考慮使用工程已經(jīng)引入的fastjson好;使用fastjson,就必須在緩存數(shù)據(jù)的時(shí)候,同時(shí)緩存數(shù)據(jù)的類型;我們使用redis的hash結(jié)構(gòu),就能解決這個(gè)問題
于是接口就成了下面這個(gè)樣子:
public class MybatisRedisCache implements Cache {private RedisCli redisCli;@Overridepublic void putObject(Object key, Object value) {String keyStr = getKey(key);Map<String,String> map = new HashMap<String,String>();//如果是多組數(shù)據(jù),那么保存的方式不同,多組的情況需要保存子實(shí)體類型if(value.getClass().equals(ArrayList.class)){@SuppressWarnings("unchecked")List<Object> list = (List<Object>)value;map.put("type", "java.util.ArrayList");if(list.size() > 0){map.put("subType", list.get(0).getClass().getCanonicalName());}else{map.put("subType",Object.class.getCanonicalName());}map.put("value", JSONObject.toJSONString(value));}else{map.put("type", value.getClass().getCanonicalName());map.put("value", JSONObject.toJSONString(value));}this.redisCli.hAllSet(keyStr, map,30);this.cacheKeys.add(keyStr);}@Overridepublic Object getObject(Object key) {try{String keyStr = getKey(key);Map<Object,Object> map = this.redisCli.hAllGet(keyStr);String type = (String)map.get("type");String value = (String)map.get("value");if(type == null || value == null){return null;}if("java.util.ArrayList".equals(type)){String subType = (String)map.get("subType");return JSONObject.parseArray(value, Class.forName(subType));}else{return JSONObject.parseObject(value, Class.forName(type));}}catch (Exception e){e.printStackTrace();return null;}}@Overridepublic void clear() {if(this.cacheKeys.isEmpty()){return ;} for(String key : this.cacheKeys){this.redisCli.del(key);}this.cacheKeys.clear();} }ps: 我們這里還是把key直接保存在了內(nèi)存里面,這樣存在的問題就是,如果服務(wù)器重啟,那么需要清理所有的緩存;不然一定會(huì)造成臟數(shù)據(jù)。
或者,我們?cè)诒4婢彺鏀?shù)據(jù)的時(shí)候,設(shè)置緩存數(shù)據(jù)的生命時(shí)間是30秒即可,希望對(duì)大家有所幫助。
轉(zhuǎn)載于:https://www.cnblogs.com/archy_yu/p/5276153.html
總結(jié)
以上是生活随笔為你收集整理的使用redis和fastjson做应用和mysql之间的缓存的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Angularjs与weui的握手
- 下一篇: 新手安装linux的磁盘划分