jeesite缓存问题

 jeesite,其框架主要为:

后端

  • 核心框架:Spring Framework 4.0

  • 安全框架:Apache Shiro 1.2

  • 视图框架:Spring MVC 4.0

  • 服务端验证:Hibernate Validator 5.1

  • 布局框架:SiteMesh 2.4

  • 工作流引擎:Activiti 5.15、FoxBPM 6

  • 任务调度:Spring Task 4.0

  • 持久层框架:MyBatis 3.2

  • 数据库连接池:Alibaba Druid 1.0

  • 缓存框架:Ehcache 2.6、Redis

  • 日志管理:SLF4J 1.7、Log4j

  • 工具类:Apache Commons、Jackson 2.2、Xstream 1.4、Dozer 5.3、POI 3.9

2、前端

  • JS框架:jQuery 1.9。

  • CSS框架:Twitter Bootstrap 2.3.1。

  • 客户端验证:JQuery Validation Plugin 1.11。

  • 富文本:CKEcitor

  • 文件管理:CKFinder

  • 动态页签:Jerichotab

  • 手机端框架:Jingle

  • 数据表格:jqGrid

  • 对话框:jQuery jBox

  • 下拉选择框:jQuery Select2

  • 树结构控件:jQuery zTree

  • 日期控件: My97DatePicker



这里对于jeesite,感觉其功能还是挺强大的,但是有一点致命缺点,就是其缓存机制,本来缓存是为了提速,但是,当这里的缓存加上了MVC,并且在前端进行请求后,不适时宜地将请求的相关类对象进行缓存,这就导致了单例化和伪持久化。怎么说来?就是说,当前端修改Person对象实例,并提交到服务端试图保存时,由于某些原因,如权限不足导致保存失败,这本来应该是很正常的,但是,偏偏由于在这之前,缓存将Person对象实例更新了,从而缓存中的该实例是修改后的,这样,后来再次获取该对象,由于缓存存在,优先取缓存而不是从DB里获取,导致,后来获取的对象的数据都是错误的(修改但保存失败的),这就变相单例化,而且是无法获得正确数据了。


例如如下的接口

[java] view plain copy
  1. @RequiresPermissions("sys:user:edit")  
  2.     @RequestMapping(value = "save")  
  3.     public String save(User user, HttpServletRequest request, Model model, RedirectAttributes redirectAttributes) {  
  4.   
  5.           
  6.         //判断是否有权限修改用户信息  
  7.           
  8.         //先清缓存:因为框架原因,只要更新了该用户,则会同步更新该用户缓存,从而无法获得真正的该用户信息,所以需要清除掉该缓存,这里先注释掉,看问题  
  9.         //UserUtils.clearCache(user);  
  10.         User oldUser = systemService.getUser(user.getId());  
  11.         List<String>roleIdListOld = oldUser.getRoleIdList();  
  12.         User operator = UserUtils.getUser();  
  13.         List<String>roleIdListOperator = operator.getRoleIdList();  
  14.         //自己不能修改自己的权限  
  15. //      if(user.getId().equals(operator.getId())){  
  16. //          addMessage(model, "修改用户信息失败, 不能修改自己的权限");  
  17. //          UserUtils.clearCache();  
  18. //          return form(oldUser, model);  
  19. //          }  
  20.         if(!roleIdListOperator.containsAll(roleIdListOld)){  
  21.             addMessage(model, "修改用户信息失败, 您的权限不足");  
  22.             UserUtils.clearCache();  
  23.             return form(oldUser, model);  
  24.         }  
  25.         user.setRoleList(roleList);  
  26.         // 保存用户信息  
  27.         systemService.saveUser(user);  
  28.         // 清除当前用户缓存  
  29.         if (user.getPhone().equals(UserUtils.getUser().getPhone())){  
  30.             UserUtils.clearCache();  
  31.             //UserUtils.getCacheMap().clear();  
  32.         }  
  33.         addMessage(redirectAttributes, "保存用户'" + user.getPhone() + "'成功");  
  34.         return "redirect:" + adminPath + "/sys/user/list?repage";  
  35.     }  

再看下getUser:

[java] view plain copy
  1. public static User getUser(String id){  
  2.     User user = (User)CacheUtils.get(USER_CACHE, USER_CACHE_ID_ + id);  
  3.     if (user ==  null){  
  4.         user = userDao.get(id);  
  5.         if (user == null){  
  6.             return null;  
  7.         }  
  8.         user.setRoleList(roleDao.findList(new Role(user)));  
  9.         CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);  
  10.         CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getPhone(), user);  
  11.     }  
  12.     return user;  
  13. }  


这里的

[java] view plain copy
  1. systemService.getUser(user.getId());  
会一直拿到该对象实例的缓存值,而该值,在修改提交到服务端时,框架已经更新了,再进到controller中。


所以,即使在

[java] view plain copy
  1. if(!roleIdListOperator.containsAll(roleIdListOld)){  
  2.             addMessage(model, "修改用户信息失败, 您的权限不足");  
  3.             UserUtils.clearCache();  
  4.             return form(oldUser, model);  
  5.         }  
这里返回了,其他地方获取该user的值
[java] view plain copy
  1. getUser(user.getId());  
还是会是缓存的值。

也相当于单例的、全局的实例值

解决方法:

在关系到修改等的地方,每次都需要对该实例进行缓存的清空。同时,在修改时,修改对象最好就是拿出db的该记录,逐个参数进行修改替换:



[java] view plain copy
  1. @RequiresPermissions("user:list:edit")  
  2.     @RequestMapping(value = "editUserInfoSave")  
  3.     public String editUserInfoSave(User user,Model model, RedirectAttributes redirectAttributes) {  
  4.           
  5.         //先清除该user的缓存,防止干扰到其他地方的引用。其实还是会有并发问题,会在清除之前被引用到  
  6.         UserUtils.clearCache(user);  
  7.                 //从db中获取user,注意这个userSave 是修改前的,与user的值不一样,注意一点:如果直接从getUser(user.getId());中获取,同时并没有清缓存的前提下  
  8.                 //UserUtils.clearCache(user);则会导致拿到的user并非DB里的user,而是缓存前端提交的                  
  9.                 User userSave = systemService.getUserFromDB(user.getId());  
  10.         /** 
  11.          * 替换更新修改信息 
  12.          */  
  13.         userSave.setName(user.getName());  
  14.         userSave.setFirstnameStr(user.getFirstnameStr());  
  15.         userSave.setLastnameStr(user.getLastnameStr());  
  16.         userSave.setIdStr(user.getIdStr());  
  17.         userSave.setUsername(user.getUsername());  
  18.         userSave.setBirthdateStr(user.getBirthdateStr());  
  19.         userSave.setEmail(user.getEmail());  
  20.         userSave.setUserType(user.getUserType());  
  21.         userSave.setGenderStr(user.getGenderStr());  
  22.         // 保存用户信息  
  23.         systemService.saveUser(userSave);  
  24.         addMessage(redirectAttributes, "保存用户'" + user.getPhone() + "'成功");  
  25.         return "redirect:" + adminPath + "/user/user/list?repage";  
  26.     }  

这里的getUserFromDB:

[java] view plain copy
  1. /** 
  2.  * 根据ID获取用户——通过DB 
  3.  * @param id 
  4.  * @return 取不到返回null 
  5.  */  
  6. public static User getUserFromDB(String id){  
  7.   
  8.     User user = userDao.get(id);  
  9.     user.setRoleList(roleDao.findList(new Role(user)));  
  10.     return user;  
  11. }  


因此特别需要注意缓存的使用,不是任何地方都适合使用缓存。

0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388023.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高级Python:定义类时要应用的9种最佳做法

重点 (Top highlight)At its core, Python is an object-oriented programming (OOP) language. Being an OOP language, Python handles data and functionalities by supporting various features centered around objects. For instance, data structures are all objects, …

Java 注解 拦截器

场景描述&#xff1a;现在需要对部分Controller或者Controller里面的服务方法进行权限拦截。如果存在我们自定义的注解&#xff0c;通过自定义注解提取所需的权限值&#xff0c;然后对比session中的权限判断当前用户是否具有对该控制器或控制器方法的访问权限。如果没有相关权限…

医疗大数据处理流程_我们需要数据来大规模改善医疗流程

医疗大数据处理流程Note: the fictitious examples and diagrams are for illustrative purposes ONLY. They are mainly simplifications of real phenomena. Please consult with your physician if you have any questions.注意&#xff1a;虚拟示例和图表仅用于说明目的。 …

What's the difference between markForCheck() and detectChanges()

https://stackoverflow.com/questions/41364386/whats-the-difference-between-markforcheck-and-detectchanges转载于:https://www.cnblogs.com/chen8840/p/10573295.html

ASP.NET Core中使用GraphQL - 第七章 Mutation

ASP.NET Core中使用GraphQL - 目录 ASP.NET Core中使用GraphQL - 第一章 Hello WorldASP.NET Core中使用GraphQL - 第二章 中间件ASP.NET Core中使用GraphQL - 第三章 依赖注入ASP.NET Core中使用GraphQL - 第四章 GrahpiQLASP.NET Core中使用GraphQL - 第五章 字段, 参数, 变量…

POM.xml红叉解决方法

方法/步骤 1用Eclipse创建一个maven工程&#xff0c;网上有很多资料&#xff0c;这里不再啰嗦。 2右键maven工程&#xff0c;进行更新 3在弹出的对话框中勾选强制更新&#xff0c;如图所示 4稍等片刻&#xff0c;pom.xml的红叉消失了。。。

JS前台页面验证文本框非空

效果图&#xff1a; 代码&#xff1a; 源代码&#xff1a; <script type"text/javascript"> function check(){ var xm document.getElementById("xm").value; if(xm null || xm ){ alert("用户名不能为空"); return false; } return …

python对象引用计数器_在Python中借助计数器对象对项目进行计数

python对象引用计数器前提 (The Premise) When we deal with data containers, such as tuples and lists, in Python we often need to count particular elements. One common way to do this is to use the count() function — you specify the element you want to count …

套接字设置为(非)阻塞模式

当socket 进行TCP 连接的时候&#xff08;也就是调用connect 时&#xff09;&#xff0c;一旦网络不通&#xff0c;或者是ip 地址无效&#xff0c;就可能使整个线程阻塞。一般为30 秒&#xff08;我测的是20 秒&#xff09;。如果设置为非阻塞模式&#xff0c;能很好的解决这个…

经典问题之「分支预测」

问题 来源 &#xff1a;stackoverflow 为什么下面代码排序后累加比不排序快&#xff1f; public static void main(String[] args) {// Generate dataint arraySize 32768;int data[] new int[arraySize];Random rnd new Random(0);for (int c 0; c < arraySize; c)data…

vi

vi filename :打开或新建文件&#xff0c;并将光标置于第一行首 vi n filename &#xff1a;打开文件&#xff0c;并将光标置于第n行首 vi filename &#xff1a;打开文件&#xff0c;并将光标置于最后一行首 vi /pattern filename&#xff1a;打开文件&#xff0c;并将光标置…

数字图像处理 python_5使用Python处理数字的高级操作

数字图像处理 pythonNumbers are everywhere in our daily life — there are phone numbers, dates of birth, ages, and other various identifiers (driver’s license and social security numbers, for example).电话号码在我们的日常生活中无处不在-电话号码&#xff0c;…

05精益敏捷项目管理——超越Scrum

00.我们不是不知道它会给我们带来麻烦&#xff0c;只是没想到麻烦会有这么多。——威尔.罗杰斯 01.知识点&#xff1a; a.Scrum是一个强大、特意设计的轻量级框架&#xff0c;器特性就是将软件开发中在制品的数量限制在团队层级&#xff0c;使团队有能力与业务落班一起有效地开…

带标题的图片轮询展示

为什么80%的码农都做不了架构师&#xff1f;>>> <div> <table width"671" cellpadding"0" cellspacing"0"> <tr height"5"> <td style"back…

linux java 查找进程中的线程

这里对linux下、sun(oracle) JDK的线程资源占用问题的查找步骤做一个小结&#xff1b;linux环境下&#xff0c;当发现java进程占用CPU资源很高&#xff0c;且又要想更进一步查出哪一个java线程占用了CPU资源时&#xff0c;按照以下步骤进行查找&#xff1a;(一)&#xff1a;通过…

定位匹配 模板匹配 地图_什么是地图匹配?

定位匹配 模板匹配 地图By Marie Douriez, James Murphy, Kerrick Staley玛丽杜里兹(Marie Douriez)&#xff0c;詹姆斯墨菲(James Murphy)&#xff0c;凯里克史塔利(Kerrick Staley) When you request a ride, Lyft tries to match you with the driver most suited for your…

Sprint计划列表

转载于:https://www.cnblogs.com/zhs20160715/p/9953586.html

MySQL学习【第十二篇事务中的锁与隔离级别】

一.事务中的锁 1.啥是锁&#xff1f; 顾名思义&#xff0c;锁就是锁定的意思 2.锁的作用是什么&#xff1f; 在事务ACID的过程中&#xff0c;‘锁’和‘隔离级别’一起来实现‘I’隔离性的作用 3.锁的种类 共享锁&#xff1a;保证在多事务工作期间&#xff0c;数据查询不会被阻…

Android WebKit

这段时间基于项目需要 在开发中与WebView的接触比较多&#xff0c;前段时间关于HTML5规范尘埃落定的消息出现在各大IT社区头版上&#xff0c;更有人说&#xff1a;HTML5将颠覆原生App开发 虽然我不太认同这一点 但是关于HTML5JSCSSNative的跨平台开发模式还是为很多企业节省了开…

jQuery的事件绑定和解绑

1、绑定事件 语法&#xff1a; bind(type,data,fn) 描述&#xff1a;为每一个匹配元素的特定事件&#xff08;像click&#xff09;绑定一个事件处理器函数。 参数解释&#xff1a; type (String) : 事件类型 data (Object) : (可选) 作为event.data属性值传递给事件对象的额外数…