1、在实际项目开发中,会使用到很多缓存技术,而且数据库的设计一般也会依赖于有缓存的情况下设计。

  • 常用的缓存分两种:本地缓存和分布式缓存。
  • 常用的本地缓存是guava cache,本章主要介绍guava cache在项目中的使用。

关于常用缓存以及每种缓存常用场景的介绍,之后可以去查看我记录的"Java缓存相关"系列博客。链接如下:

第一章 常用的缓存技术

2、实际使用

本项目的代码基于第六章的代码进行构建,这里只列出修改过的代码:

2.1、ssmm0-data

pom.xml:

  1. <!-- guava cache -->
  2. <dependency>
  3. <groupId>com.google.guava</groupId>
  4. <artifactId>guava</artifactId>
  5. <version>14.0.1</version>
  6. </dependency>

在pom.xml中引入了guava cache14.0.1的依赖包。

AdminMapper:

  1. package com.xxx.mapper.userManagement;
  2.  
  3. import java.util.List;
  4.  
  5. import org.apache.ibatis.annotations.Insert;
  6. import org.apache.ibatis.annotations.Param;
  7. import org.apache.ibatis.annotations.Result;
  8. import org.apache.ibatis.annotations.Results;
  9. import org.apache.ibatis.annotations.Select;
  10.  
  11. import com.xxx.model.userManagement.Admin;
  12.  
  13. /**
  14. * 管理员Mapper
  15. */
  16. public interface AdminMapper {
  17.  
  18. /**************注解**************/
  19. @Insert("INSERT INTO userinfo(username, password) VALUES(#{username},#{password})")
  20. public int insertAdmin(Admin admin);
  21.  
  22. @Select("SELECT * FROM userinfo WHERE username = #{username} AND password = #{password}")
  23. @Results(value = {
  24. @Result(id = true, column = "id", property = "id"),
  25. @Result(column = "username", property = "username"),
  26. @Result(column = "password", property = "password") })
  27. public Admin selectAdmin(@Param("username") String username,
  28. @Param("password") String password);
  29.  
  30. /***************xml**************/
  31. /**
  32. * 条件不定式查询
  33. * 我们这里使用@Param指定参数,这样的话,在AdminMapper.xml中就不用再使用parameterType属性了;否则得写parameterType属性
  34. */
  35. public List<Admin> getAdminByConditions(@Param("username")String username,
  36. @Param("password")String password,
  37. @Param("start")int start,
  38. @Param("limit")int limit);
  39.  
  40. /**
  41. * 返回主键
  42. */
  43. public int insertAdminWithBackId(Admin admin);
  44.  
  45. /****************guava cache*****************/
  46. @Select("SELECT * FROM userinfo WHERE username = #{username}")
  47. @Results(value = {
  48. @Result(id = true, column = "id", property = "id"),
  49. @Result(column = "username", property = "username"),
  50. @Result(column = "password", property = "password") })
  51. public List<Admin> getUserByName(@Param("username") String username);
  52. }

将使用到的两个方法:

  • public List<Admin> getUserByName(String username)
  • public List<Admin> getAdminByConditions(String username, String password, int start, int limit)

AdminDao:

  1. package com.xxx.dao.userManagement;
  2.  
  3. import java.util.List;
  4.  
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Repository;
  7.  
  8. import com.xxx.mapper.userManagement.AdminMapper;
  9. import com.xxx.model.userManagement.Admin;
  10.  
  11. /**
  12. * 管理员DAO
  13. */
  14. @Repository
  15. public class AdminDao {
  16. @Autowired
  17. private AdminMapper adminMapper;
  18. /***************注解*****************/
  19. public boolean register(Admin admin){
  20. return adminMapper.insertAdmin(admin)==1?true:false;
  21. }
  22.  
  23. public Admin login(String username ,String password){
  24. return adminMapper.selectAdmin(username, password);
  25. }
  26. /****************xml******************/
  27. public List<Admin> findAdmin(String username, String password, int start, int limit){
  28. return adminMapper.getAdminByConditions(username, password, start, limit);
  29. }
  30.  
  31. public int insertAdminWithBackId(Admin admin){
  32. return adminMapper.insertAdminWithBackId(admin);
  33. }
  34. /******************guava cache********************/
  35. public List<Admin> getUserByName(String username){
  36. return adminMapper.getUserByName(username);
  37. }
  38. }

将使用到的两个方法:

  • public List<Admin> getUserByName(String username)
  • public List<Admin> findAdmin(String username, String password, int start, int limit)

AdminService:

  1. package com.xxx.service.userManagement;
  2.  
  3. import java.util.List;
  4. import java.util.concurrent.ExecutionException;
  5. import java.util.concurrent.TimeUnit;
  6.  
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9.  
  10. import com.google.common.cache.CacheBuilder;
  11. import com.google.common.cache.CacheLoader;
  12. import com.google.common.cache.LoadingCache;
  13. import com.xxx.dao.userManagement.AdminDao;
  14. import com.xxx.model.userManagement.Admin;
  15. import com.xxx.vo.userManagement.AdminCacheKey;
  16.  
  17. /**
  18. * 管理员service
  19. */
  20. @Service
  21. public class AdminService {
  22. @Autowired
  23. private AdminDao adminDao;
  24.  
  25. public boolean register(Admin admin) {
  26. return adminDao.register(admin);
  27. }
  28.  
  29. public Admin login(String username, String password) {
  30. return adminDao.login(username, password);
  31. }
  32.  
  33. /*********** 以下方法是为了测试mybatis中使用xml **********/
  34. public List<Admin> findAdmin(String username,
  35. String password,
  36. int start,
  37. int limit) {
  38. return adminDao.findAdmin(username, password, start, limit);
  39. }
  40.  
  41. public Admin insertAdminWithBackId(Admin admin) {
  42. int record = adminDao.insertAdminWithBackId(admin);
  43. if (record == 1) {
  44. return admin;// 这时的admin已经被赋予主键了
  45. }
  46. return null;
  47. }
  48.  
  49. /************************ guava cache *************************/
  50. /************单条件的查询,key为String***********/
  51. public List<Admin> findByUsername(String username) {
  52. List<Admin> adminList = null;
  53. try {
  54. adminList = adminListCache.get(username);
  55. } catch (ExecutionException e) {
  56. e.printStackTrace();
  57. }
  58. return adminList;
  59. }
  60.  
  61. LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder()
  62. .expireAfterWrite(20, TimeUnit.MINUTES)// 缓存20分钟
  63. .maximumSize(1000)// 最多缓存1000个对象
  64. .build(new CacheLoader<String, List<Admin>>() {
  65. public List<Admin> load(String username) throws Exception {
  66. return adminDao.getUserByName(username);
  67. }
  68. });
  69.  
  70. /************多条件的查询,key为Object(封装了多个条件的VO类)***********/
  71. public List<Admin> findAdminList(String username,
  72. String password,
  73. int start,
  74. int limit) {
  75. /*
  76. * 注意:
  77. * 如果以一个新建的对象做为key的话,因为每次都是新建一个对象,所以这样的话,实际上每次访问key都是不同的,即每次访问都是重新进行缓存;
  78. * 但是实际上,我们想要根据对象的属性来判断对象是否相等,只需要根据这些属性重写对象的hashCode与equals方法即可,
  79. * 所以重写了AdminCacheKey类的hashCode和equals方法,这样,每次访问的话,就会以每个条件是否相等来判断对象(即key)是否相等了,这一块儿的缓存就会起作用了
  80. */
  81. AdminCacheKey cacheKey = new AdminCacheKey(username,
  82. password,
  83. start,
  84. limit);
  85. List<Admin> adminList = null;
  86. try {
  87. System.out.println(cacheKey);
  88. adminList = adminsCache.get(cacheKey);
  89. } catch (ExecutionException e) {
  90. e.printStackTrace();
  91. }
  92. return adminList;
  93. }
  94.  
  95. LoadingCache<AdminCacheKey, List<Admin>> adminsCache = CacheBuilder.newBuilder()
  96. .expireAfterWrite(60, TimeUnit.MINUTES) // 缓存项在给定时间内(60min)没有被写访问(创建或覆盖),则回收
  97. .maximumSize(100) // 最多缓存100项
  98. .build(new CacheLoader<AdminCacheKey, List<Admin>>() {
  99. public List<Admin> load(AdminCacheKey key) throws Exception {
  100. return adminDao.findAdmin(key.getUsername(),
  101. key.getPassword(),
  102. key.getStart(),
  103. key.getLimit());
  104. }
  105. });
  106.  
  107. }

将使用到的两个方法:

  • public List<Admin> findByUsername(String username)
  • public List<Admin> findAdminList(String username, String password, int start, int limit)

这一块儿是整个guava cache使用的部分。这里边写出了两种guava cache使用的方式:

  • 单查询条件:key为String或Object都可以
  • 多查询条件:key为Object,该Object封装了多个查询条件,并通过这些查询条件重写了该Object的hashcode()和equals()

这一部分中guava cache的使用方式,就是实际开发中最常用的方法。

AdminCacheKey:

  1. package com.xxx.vo.userManagement;
  2.  
  3. /**
  4. * guava cache的key
  5. */
  6. public class AdminCacheKey {
  7. private String username;
  8. private String password;
  9. private int start;
  10. private int limit;
  11.  
  12. public AdminCacheKey() {
  13. }
  14.  
  15. public AdminCacheKey(String username, String password, int start, int limit) {
  16. this.username = username;
  17. this.password = password;
  18. this.start = start;
  19. this.limit = limit;
  20. }
  21.  
  22. public String getUsername() {
  23. return username;
  24. }
  25.  
  26. public void setUsername(String username) {
  27. this.username = username;
  28. }
  29.  
  30. public String getPassword() {
  31. return password;
  32. }
  33.  
  34. public void setPassword(String password) {
  35. this.password = password;
  36. }
  37.  
  38. public int getStart() {
  39. return start;
  40. }
  41.  
  42. public void setStart(int start) {
  43. this.start = start;
  44. }
  45.  
  46. public int getLimit() {
  47. return limit;
  48. }
  49.  
  50. public void setLimit(int limit) {
  51. this.limit = limit;
  52. }
  53.  
  54. @Override
  55. public int hashCode() {
  56. final int prime = 31;
  57. int result = 1;
  58. result = prime * result + limit;
  59. result = prime * result
  60. + ((password == null) ? 0 : password.hashCode());
  61. result = prime * result + start;
  62. result = prime * result
  63. + ((username == null) ? 0 : username.hashCode());
  64. return result;
  65. }
  66.  
  67. @Override
  68. public boolean equals(Object obj) {
  69. if (this == obj)
  70. return true;
  71. if (obj == null)
  72. return false;
  73. if (getClass() != obj.getClass())
  74. return false;
  75. AdminCacheKey other = (AdminCacheKey) obj;
  76. if (limit != other.limit)
  77. return false;
  78. if (password == null) {
  79. if (other.password != null)
  80. return false;
  81. } else if (!password.equals(other.password))
  82. return false;
  83. if (start != other.start)
  84. return false;
  85. if (username == null) {
  86. if (other.username != null)
  87. return false;
  88. } else if (!username.equals(other.username))
  89. return false;
  90. return true;
  91. }
  92. }

该类是封装了多个查询条件的一个VO类。

2.2、ssmm0-userManagement

AdminController:

  1. package com.xxx.web.admin;
  2.  
  3. import java.util.List;
  4.  
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import javax.servlet.http.HttpSession;
  8.  
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.stereotype.Controller;
  11. import org.springframework.web.bind.annotation.RequestMapping;
  12. import org.springframework.web.bind.annotation.RequestParam;
  13. import org.springframework.web.bind.annotation.ResponseBody;
  14. import org.springframework.web.servlet.ModelAndView;
  15.  
  16. import com.xxx.model.userManagement.Admin;
  17. import com.xxx.service.userManagement.AdminService;
  18. import com.xxx.util.admin.AdminCookieUtil;
  19.  
  20. /**
  21. * adminController
  22. */
  23. @Controller
  24. @RequestMapping("/admin")
  25. public class AdminController {
  26.  
  27. @Autowired
  28. private AdminService adminService;
  29.  
  30. /**
  31. * 管理员注册
  32. */
  33. @ResponseBody
  34. @RequestMapping("/register")
  35. public boolean register(@RequestParam("username") String username,
  36. @RequestParam("password") String password){
  37. Admin admin = new Admin();
  38. admin.setUsername(username);
  39. admin.setPassword(password);
  40.  
  41. boolean isRegisterSuccess = adminService.register(admin);
  42.  
  43. return isRegisterSuccess;
  44. }
  45.  
  46. /**
  47. * 管理员登录
  48. */
  49. @RequestMapping("/login")
  50. public ModelAndView login(@RequestParam("username") String username,
  51. @RequestParam("password") String password,
  52. HttpServletResponse response,
  53. HttpSession session){
  54.  
  55. Admin admin = adminService.login(username, password);
  56.  
  57. ModelAndView modelAndView = new ModelAndView();
  58. if(admin == null){
  59. modelAndView.addObject("message", "用户不存在或者密码错误!请重新输入");
  60. modelAndView.setViewName("error");
  61. }else{
  62. modelAndView.addObject("admin", admin);
  63. modelAndView.setViewName("userinfo");
  64. /*
  65. * 这为什么不直接传一个username,而传了一个admin,
  66. * 是因为在实际开发中,你传过去的信息可能不只是username,还有用户手机号、地址等等
  67. */
  68. //使用cookie
  69. AdminCookieUtil.addLoginCookie(admin, response);
  70. //使用session
  71. //session.setAttribute("adminSession", admin);
  72. }
  73.  
  74. return modelAndView;
  75. }
  76.  
  77. /*****************************mybatis xml方式解决的问题*******************************/
  78. /**
  79. * 根据username或password查找List<Admin>
  80. */
  81. @ResponseBody
  82. @RequestMapping("/findAdmin")
  83. public List<Admin> findAdmin(@RequestParam(value="username",required=false) String username,
  84. @RequestParam(value="password",required=false) String password,
  85. @RequestParam("start") int start,
  86. @RequestParam("limit") int limit,
  87. HttpServletRequest request,
  88. HttpSession session){
  89. Admin admin = AdminCookieUtil.getLoginCookie(request);
  90. //Admin admin = (Admin) session.getAttribute("adminSession");
  91.  
  92. if(admin == null){//未登录
  93. return null;
  94. }
  95. System.out.println(admin.toJson());
  96. List<Admin> adminList = adminService.findAdmin(username, password, start, limit);
  97. return adminList;
  98. }
  99.  
  100. /**
  101. * 插入一个用户并返回主键
  102. * 注意:get请求也会自动装配(即将前台传入的username和password传入admin)
  103. */
  104. @ResponseBody
  105. @RequestMapping("/insert")
  106. public Admin insertAdminWithBackId(Admin admin){
  107. return adminService.insertAdminWithBackId(admin);
  108. }
  109. /*************************guava cache******************************/
  110. /**
  111. * 根据username查找List<Admin>
  112. */
  113. @ResponseBody
  114. @RequestMapping("/findAdminByUsername")
  115. public List<Admin> findAdminByUserName(@RequestParam(value="username") String username){
  116.  
  117. List<Admin> adminList = adminService.findByUsername(username);
  118. return adminList;
  119. }
  120.  
  121. @ResponseBody
  122. @RequestMapping("/findAdminList")
  123. public List<Admin> findAdminList(@RequestParam(value="username") String username,
  124. @RequestParam(value="password",required=false) String password,
  125. @RequestParam("start") int start,
  126. @RequestParam("limit") int limit){
  127.  
  128. List<Admin> adminList = adminService.findAdminList(username, password, start, limit);
  129. return adminList;
  130. }
  131. }

将使用到的两个方法:

  • public List<Admin> findAdminByUserName(String username)
  • public List<Admin> findAdminList(String username, String password, int start, int limit)

3、测试

  • 单元测试:使用springJunit去测就行
  • 整体测试:代码写好之后,注意对代码去做测试的方法,先运行相应的controller的方法,然后对查询出来的部分数据在数据库中直接进行修改,再运行之前的controller对应的方法。出现两种结果:
    • 第二次运行与第一次结果相同:缓存成功
    • 第二次运行与第一次结果不同:缓存不成功

4、总结:

  • 常用的几个API:

    • get(Object key):首先获取value-->若获取不到,先缓存-->再从缓存中去取(以上三步是原子操作),使用该方法优先于使用put
    • getIfPresent(Object key):获取value,若获取不到,返回null;若获取的到,返回value
    • put(Object key, Object value):显示的添加缓存key-value
  • guava cache的get(Object key)的value不能为null(这个可以去看源代码的注释),看下边的代码例子:
    1. LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder()
    2. .expireAfterWrite(20, TimeUnit.MINUTES)// 缓存20分钟
    3. .maximumSize(1000)// 最多缓存1000个对象
    4. .build(new CacheLoader<String, List<Admin>>() {
    5. public List<Admin> load(String username) throws Exception {
    6. //1、下边这样null的话,不抛异常
    7. /*List<Admin> admins = adminDao.getUserByName(username);
    8. if(admins==null){
    9. return null;
    10. }
    11. return admins;*/
    12. //2、但是如果这里查询出来的结果为null的话,也没关系
    13. //return adminDao.getUserByName(username);
    14. //3、如果这里直接返回null,就会出现com.google.common.cache.CacheLoader$InvalidCacheLoadException
    15. return null;
    16. }
    17. });

    注意:该代码中的三种null情况,只有第三种会抛出异常。前两种不为空的原因是因为,即使admins没有元素,admins也不会是null,而是[],这应该是mybatis的功劳?!这个是个问题,以后在读mybatis源码的时候,会仔细研究!!!但是实际使用中,我们判断一个list是否为空,会使用CollectionUtil.isNotEmpty(list)类似于下边这样,就会抛出异常了。

    1. LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder()
    2. .expireAfterWrite(20, TimeUnit.MINUTES)// 缓存20分钟
    3. .maximumSize(1000)// 最多缓存1000个对象
    4. .build(new CacheLoader<String, List<Admin>>() {
    5. public List<Admin> load(String username) throws Exception {
    6. //1、下边这样null的话,不抛异常
    7. List<Admin> admins = adminDao.getUserByName(username);
    8. //System.out.println(admins);//如果admins为空,不会返回null,而是返回[]
    9. if(CollectionUtils.isEmpty(admins)){
    10. System.out.println(admins+"-->");
    11. return null;
    12. }
    13. return admins;
    14. //2、但是如果这里查询出来的结果为null的话,也没关系
    15. //return adminDao.getUserByName(username);
    16. //3、如果这里直接返回null,就会出现com.google.common.cache.CacheLoader$InvalidCacheLoadException
    17. //return null;
    18. }
    19. });

    但是,为了在guava cache的使用中不抛出异常,我们这里直接使用下边这句就好了,由mybatis将[]返回就好了。

    1. return adminDao.getUserByName(username);

第七章 企业项目开发--本地缓存guava cache的更多相关文章

  1. 企业项目开发--本地缓存guava cache(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.在实际项目开发中,会使用到很多缓存技术,而且数据库的设计一般也会依赖于有缓存的情况下设计. 常用的缓存分 ...

  2. 企业项目开发--本地缓存guava cache(2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. AdminCacheKey: package com.xxx.vo.userManagement; /** ...

  3. 第九章 企业项目开发--分布式缓存Redis(1)

    注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis 1.1.为什么用分布式缓存(或者说本地缓存存在的问题 ...

  4. 第八章 企业项目开发--分布式缓存memcached

    注意:本节代码基于<第七章 企业项目开发--本地缓存guava cache> 1.本地缓存的问题 本地缓存速度一开始高于分布式缓存,但是随着其缓存数量的增加,所占内存越来越大,系统运行内存 ...

  5. 企业项目开发--分布式缓存memcached(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3.3.ssmm0-data 结构: 3.3.1.pom.xml  1 <?xml version=& ...

  6. 企业项目开发--分布式缓存Redis

    第九章 企业项目开发--分布式缓存Redis(1) 注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis ...

  7. 第十章 企业项目开发--分布式缓存Redis(2)

    注意:本章代码是在上一章的基础上进行添加修改,上一章链接<第九章 企业项目开发--分布式缓存Redis(1)> 上一章说了ShardedJedisPool的创建过程,以及redis五种数据 ...

  8. 第十一章 企业项目开发--消息队列activemq

    注意:本章代码基于 第十章 企业项目开发--分布式缓存Redis(2) 代码的github地址:https://github.com/zhaojigang/ssmm0 消息队列是分布式系统中实现RPC ...

  9. 第六章 企业项目开发--cookie

    注:本章代码基于<第五章 企业项目开发--mybatis注解与xml并用>的代码,链接如下: http://www.cnblogs.com/java-zhao/p/5120792.html ...

随机推荐

  1. sqlserver2008创建数据库 报 Cannot read property is filestream 此属性不可用于sql server 7.0 解决

    在创建数据库的时候,报整个错误 Cannot read property is filestream 此属性不可用于sql server 7.0 按照网上的方法  (http://blog.csdn. ...

  2. Scrum Meeting---Eleven(2015-11-6)

    今日已完成任务和明日要做的任务 姓名 今日已完成任务 今日时间 明日计划完成任务 估计用时 董元财 倒计时设计 3h 商品发布页设计 4h 胡亚坤 低栏设计 2h UI风格 2h 刘猛 通讯录设计 2 ...

  3. iOS - UIRefreshControl 刷新数据

    前言 NS_CLASS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED @interface UIRefreshControl : UIControl 1.UIRefresh ...

  4. hibernate对象关系实现(一)一对多

    hibernate是对jdk一个封装工具,实现对象和数据库之间数据映射.使用时涉及到四个问题:a.对象之间的关系在类中的体现:b,对象关系对应的数据库中表之间体现:c.实现a,b在hibernate的 ...

  5. Linux_常用命令_03_磁盘/挂载_信息查看

    1. 1.1. mount 不带参数的话,显示的是 当前已经挂载的情况 1.2. df 不带参数的话,硬盘分区状况查询 2. 2.1. cat /proc/partitions 2.2. fdisk ...

  6. java的重修之路

    一.内存管理 java里的声明分引用与基本数据类型. 数组: java里new一个对象数组为  person[] A; A = new person[4];  person[0] = new pers ...

  7. mysql 中文字段排序( 按拼音首字母排序) 的查询语句

    在处理使用Mysql时,数据表采用utf8字符集,使用中发现中文不能直接按照拼音排序 如果数据表tbl的某字段name的字符编码是latin1_swedish_ci select * from `tb ...

  8. Python学习笔记17—Tornado

    实例 #!/usr/bin/env Python #coding:utf-8 import tornado.httpserver import tornado.ioloop import tornad ...

  9. css技术和实例

    今天,我为大家收集精选了30个使用纯CSS完成的强大实践的优秀CSS技术和实例,您将在这里发现很多与众不同的技术,比如:图片集.阴影效果.可扩展按钮.菜单等-这些实例都是使用纯CSS和HTML实现的. ...

  10. CodeForces 429 B Working out(递推dp)

    题目连接:B. Working out 我想了很久都没有想到怎么递推,看了题解后试着自己写,结果第二组数据就 wa 了,后来才知道自己没有判选择的两条路径是否只是一个交点. 大概思路是:先预处理出每个 ...