一、在實際項目開發中,會使用到不少緩存技術,並且數據庫的設計通常也會依賴於有緩存的狀況下設計。html
關於經常使用緩存以及每種緩存經常使用場景的介紹,以後能夠去查看我記錄的"Java緩存相關"系列博客。連接以下:java
《第一章 經常使用的緩存技術》web
二、實際使用spring
本項目的代碼基於第六章的代碼進行構建,這裏只列出修改過的代碼:數據庫
2.一、ssmm0-dataapache
pom.xml:緩存
<!-- guava cache --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>14.0.1</version> </dependency>
在pom.xml中引入了guava cache14.0.1的依賴包。cookie
AdminMapper:session
package com.xxx.mapper.userManagement; import java.util.List; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import com.xxx.model.userManagement.Admin; /** * 管理員Mapper */ public interface AdminMapper { /**************註解**************/ @Insert("INSERT INTO userinfo(username, password) VALUES(#{username},#{password})") public int insertAdmin(Admin admin); @Select("SELECT * FROM userinfo WHERE username = #{username} AND password = #{password}") @Results(value = { @Result(id = true, column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "password", property = "password") }) public Admin selectAdmin(@Param("username") String username, @Param("password") String password); /***************xml**************/ /** * 條件不定式查詢 * 咱們這裏使用@Param指定參數,這樣的話,在AdminMapper.xml中就不用再使用parameterType屬性了;不然得寫parameterType屬性 */ public List<Admin> getAdminByConditions(@Param("username")String username, @Param("password")String password, @Param("start")int start, @Param("limit")int limit); /** * 返回主鍵 */ public int insertAdminWithBackId(Admin admin); /****************guava cache*****************/ @Select("SELECT * FROM userinfo WHERE username = #{username}") @Results(value = { @Result(id = true, column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "password", property = "password") }) public List<Admin> getUserByName(@Param("username") String username); }
將使用到的兩個方法:mybatis
AdminDao:
package com.xxx.dao.userManagement; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import com.xxx.mapper.userManagement.AdminMapper; import com.xxx.model.userManagement.Admin; /** * 管理員DAO */ @Repository public class AdminDao { @Autowired private AdminMapper adminMapper; /***************註解*****************/ public boolean register(Admin admin){ return adminMapper.insertAdmin(admin)==1?true:false; } public Admin login(String username ,String password){ return adminMapper.selectAdmin(username, password); } /****************xml******************/ public List<Admin> findAdmin(String username, String password, int start, int limit){ return adminMapper.getAdminByConditions(username, password, start, limit); } public int insertAdminWithBackId(Admin admin){ return adminMapper.insertAdminWithBackId(admin); } /******************guava cache********************/ public List<Admin> getUserByName(String username){ return adminMapper.getUserByName(username); } }
將使用到的兩個方法:
AdminService:
package com.xxx.service.userManagement; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.xxx.dao.userManagement.AdminDao; import com.xxx.model.userManagement.Admin; import com.xxx.vo.userManagement.AdminCacheKey; /** * 管理員service */ @Service public class AdminService { @Autowired private AdminDao adminDao; public boolean register(Admin admin) { return adminDao.register(admin); } public Admin login(String username, String password) { return adminDao.login(username, password); } /*********** 如下方法是爲了測試mybatis中使用xml **********/ public List<Admin> findAdmin(String username, String password, int start, int limit) { return adminDao.findAdmin(username, password, start, limit); } public Admin insertAdminWithBackId(Admin admin) { int record = adminDao.insertAdminWithBackId(admin); if (record == 1) { return admin;// 這時的admin已經被賦予主鍵了 } return null; } /************************ guava cache *************************/ /************單條件的查詢,key爲String***********/ public List<Admin> findByUsername(String username) { List<Admin> adminList = null; try { adminList = adminListCache.get(username); } catch (ExecutionException e) { e.printStackTrace(); } return adminList; } LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder() .expireAfterWrite(20, TimeUnit.MINUTES)// 緩存20分鐘 .maximumSize(1000)// 最多緩存1000個對象 .build(new CacheLoader<String, List<Admin>>() { public List<Admin> load(String username) throws Exception { return adminDao.getUserByName(username); } }); /************多條件的查詢,key爲Object(封裝了多個條件的VO類)***********/ public List<Admin> findAdminList(String username, String password, int start, int limit) { /* * 注意: * 若是以一個新建的對象作爲key的話,由於每次都是新建一個對象,因此這樣的話,實際上每次訪問key都是不一樣的,即每次訪問都是從新進行緩存; * 可是實際上,咱們想要根據對象的屬性來判斷對象是否相等,只須要根據這些屬性重寫對象的hashCode與equals方法便可, * 因此重寫了AdminCacheKey類的hashCode和equals方法,這樣,每次訪問的話,就會以每一個條件是否相等來判斷對象(即key)是否相等了,這一起的緩存就會起做用了 */ AdminCacheKey cacheKey = new AdminCacheKey(username, password, start, limit); List<Admin> adminList = null; try { System.out.println(cacheKey); adminList = adminsCache.get(cacheKey); } catch (ExecutionException e) { e.printStackTrace(); } return adminList; } LoadingCache<AdminCacheKey, List<Admin>> adminsCache = CacheBuilder.newBuilder() .expireAfterWrite(60, TimeUnit.MINUTES) // 緩存項在給定時間內(60min)沒有被寫訪問(建立或覆蓋),則回收 .maximumSize(100) // 最多緩存100項 .build(new CacheLoader<AdminCacheKey, List<Admin>>() { public List<Admin> load(AdminCacheKey key) throws Exception { return adminDao.findAdmin(key.getUsername(), key.getPassword(), key.getStart(), key.getLimit()); } }); }
將使用到的兩個方法:
這一起是整個guava cache使用的部分。這裏邊寫出了兩種guava cache使用的方式:
這一部分中guava cache的使用方式,就是實際開發中最經常使用的方法。
AdminCacheKey:
package com.xxx.vo.userManagement; /** * guava cache的key */ public class AdminCacheKey { private String username; private String password; private int start; private int limit; public AdminCacheKey() { } public AdminCacheKey(String username, String password, int start, int limit) { this.username = username; this.password = password; this.start = start; this.limit = limit; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getStart() { return start; } public void setStart(int start) { this.start = start; } public int getLimit() { return limit; } public void setLimit(int limit) { this.limit = limit; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + limit; result = prime * result + ((password == null) ? 0 : password.hashCode()); result = prime * result + start; result = prime * result + ((username == null) ? 0 : username.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AdminCacheKey other = (AdminCacheKey) obj; if (limit != other.limit) return false; if (password == null) { if (other.password != null) return false; } else if (!password.equals(other.password)) return false; if (start != other.start) return false; if (username == null) { if (other.username != null) return false; } else if (!username.equals(other.username)) return false; return true; } }
該類是封裝了多個查詢條件的一個VO類。
2.二、ssmm0-userManagement
AdminController:
package com.xxx.web.admin; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import com.xxx.model.userManagement.Admin; import com.xxx.service.userManagement.AdminService; import com.xxx.util.admin.AdminCookieUtil; /** * adminController */ @Controller @RequestMapping("/admin") public class AdminController { @Autowired private AdminService adminService; /** * 管理員註冊 */ @ResponseBody @RequestMapping("/register") public boolean register(@RequestParam("username") String username, @RequestParam("password") String password){ Admin admin = new Admin(); admin.setUsername(username); admin.setPassword(password); boolean isRegisterSuccess = adminService.register(admin); return isRegisterSuccess; } /** * 管理員登陸 */ @RequestMapping("/login") public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password, HttpServletResponse response, HttpSession session){ Admin admin = adminService.login(username, password); ModelAndView modelAndView = new ModelAndView(); if(admin == null){ modelAndView.addObject("message", "用戶不存在或者密碼錯誤!請從新輸入"); modelAndView.setViewName("error"); }else{ modelAndView.addObject("admin", admin); modelAndView.setViewName("userinfo"); /* * 這爲何不直接傳一個username,而傳了一個admin, * 是由於在實際開發中,你傳過去的信息可能不僅是username,還有用戶手機號、地址等等 */ //使用cookie AdminCookieUtil.addLoginCookie(admin, response); //使用session //session.setAttribute("adminSession", admin); } return modelAndView; } /*****************************mybatis xml方式解決的問題*******************************/ /** * 根據username或password查找List<Admin> */ @ResponseBody @RequestMapping("/findAdmin") public List<Admin> findAdmin(@RequestParam(value="username",required=false) String username, @RequestParam(value="password",required=false) String password, @RequestParam("start") int start, @RequestParam("limit") int limit, HttpServletRequest request, HttpSession session){ Admin admin = AdminCookieUtil.getLoginCookie(request); //Admin admin = (Admin) session.getAttribute("adminSession"); if(admin == null){//未登陸 return null; } System.out.println(admin.toJson()); List<Admin> adminList = adminService.findAdmin(username, password, start, limit); return adminList; } /** * 插入一個用戶並返回主鍵 * 注意:get請求也會自動裝配(即將前臺傳入的username和password傳入admin) */ @ResponseBody @RequestMapping("/insert") public Admin insertAdminWithBackId(Admin admin){ return adminService.insertAdminWithBackId(admin); } /*************************guava cache******************************/ /** * 根據username查找List<Admin> */ @ResponseBody @RequestMapping("/findAdminByUsername") public List<Admin> findAdminByUserName(@RequestParam(value="username") String username){ List<Admin> adminList = adminService.findByUsername(username); return adminList; } @ResponseBody @RequestMapping("/findAdminList") public List<Admin> findAdminList(@RequestParam(value="username") String username, @RequestParam(value="password",required=false) String password, @RequestParam("start") int start, @RequestParam("limit") int limit){ List<Admin> adminList = adminService.findAdminList(username, password, start, limit); return adminList; } }
將使用到的兩個方法:
三、測試
四、總結:
LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder() .expireAfterWrite(20, TimeUnit.MINUTES)// 緩存20分鐘 .maximumSize(1000)// 最多緩存1000個對象 .build(new CacheLoader<String, List<Admin>>() { public List<Admin> load(String username) throws Exception { //一、下邊這樣null的話,不拋異常 /*List<Admin> admins = adminDao.getUserByName(username); if(admins==null){ return null; } return admins;*/ //二、可是若是這裏查詢出來的結果爲null的話,也不要緊 //return adminDao.getUserByName(username); //三、若是這裏直接返回null,就會出現com.google.common.cache.CacheLoader$InvalidCacheLoadException return null; } });
注意:該代碼中的三種null狀況,只有第三種會拋出異常。前兩種不爲空的緣由是由於,即便admins沒有元素,admins也不會是null,而是[],這應該是mybatis的功勞?!這個是個問題,之後在讀mybatis源碼的時候,會仔細研究!!!可是實際使用中,咱們判斷一個list是否爲空,會使用CollectionUtil.isNotEmpty(list)相似於下邊這樣,就會拋出異常了。
LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder() .expireAfterWrite(20, TimeUnit.MINUTES)// 緩存20分鐘 .maximumSize(1000)// 最多緩存1000個對象 .build(new CacheLoader<String, List<Admin>>() { public List<Admin> load(String username) throws Exception { //一、下邊這樣null的話,不拋異常 List<Admin> admins = adminDao.getUserByName(username); //System.out.println(admins);//若是admins爲空,不會返回null,而是返回[] if(CollectionUtils.isEmpty(admins)){ System.out.println(admins+"-->"); return null; } return admins; //二、可是若是這裏查詢出來的結果爲null的話,也不要緊 //return adminDao.getUserByName(username); //三、若是這裏直接返回null,就會出現com.google.common.cache.CacheLoader$InvalidCacheLoadException //return null; } });
可是,爲了在guava cache的使用中不拋出異常,咱們這裏直接使用下邊這句就行了,由mybatis將[]返回就行了。
return adminDao.getUserByName(username);