# WebTech **Repository Path**: xiaolongxibao/web-tech ## Basic Information - **Project Name**: WebTech - **Description**: SSM+LayUI的后台管理小demo - **Primary Language**: Java - **License**: LGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2023-05-09 - **Last Updated**: 2024-05-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SSM+LayUI的后台管理小demo #### 介绍 SSM+LayUI的后台管理小demo **一、** **任务概述** 采用 SSM 框架来实现一个基于 Web 的用户管理系统,要求具有以下功能: (1)提供用户注册,修改个人资料,修改密码,以及登陆,注销等功能, 具体的界面设计,参考图 1-图 5, 其中图 3 要实现响应式布局,当屏幕的分辨 率小于 800px 时,左侧的 left 菜单自动隐藏 ![image.png](README.assets/clip_image004.gif)![IMG_256](README.assets/clip_image006.gif)![IMG_256](README.assets/clip_image008.gif)![IMG_256](README.assets/clip_image010.gif)![IMG_256](README.assets/clip_image012.gif) (2) (必做)实现用户的信息管理,包括所有用户的信息列表展示,查询,编辑和 删除,具体的界面设计,参考图 6-图 9 ![IMG_256](README.assets/clip_image014.gif)![IMG_256](README.assets/clip_image016.gif)![IMG_256](README.assets/clip_image018.gif)![IMG_256](README.assets/clip_image020.gif) (3) (选做)实现文章的信息管理,一个用户可以发表多篇文章。文章管理的首页 展示一个统计信息,既有列表统计,也有图表显示。点击 进入文章管理进去, 可以对所选用户的文章进行管理,包括对文章信息的 CRUD 操作,具体的界面 设计,参考图 10-图 15 ![IMG_256](README.assets/clip_image022.gif)![IMG_256](README.assets/clip_image024.gif) ![IMG_256](README.assets/clip_image026.gif)![IMG_256](README.assets/clip_image028.gif) ![IMG_256](README.assets/clip_image029.gif)![IMG_256](README.assets/clip_image031.gif) **二、** **系统分析与设计** **2.1** **需求分析** **2.1.1****用户需求分析**: 用户文章管理系统需要提供管理用户的信息以及用户所发布的文章信息。该系统应该提供多种功能来满足用户的需求,在最基本的业务需求中需要包括用户信息管理、文章信息管理的业务功能。 **2.1.2****功能需求分析** 本用户文章管理系统必须满足以下功能需求: **1)** **用户管理子系统:** a) 登录/注册功能:作为游客可以通过便捷的注册获取平台登录账号,已经注册且账号状态正常(的用户可以通过账号密码直接登录。 b) 个人信息管理功能:作为用户可以通过个人信息管理页面进行个人信息的完善与修改,包括用户昵称、性别、联系方式、邮箱等信息的完善修改以及密码修改、账号注销。 c) 用户管理功能:作为管理员可以对已经注册的用户进行一定限度内的管理,包括用户权限变更、用户密码重置、非活跃账号的删除清理、新用户的添加。 ![图示 描述已自动生成](README.assets/clip_image033.jpg) **2)** **文章管理子系统:** a) 文章浏览功能:用户可以获取用户文章列表并提供详情查看。 b) 文章管理功能:用户可以进行文章的上架、下架和信息修改。 c) 文章数量图表显示分析功能:要求用户列表下有文章数量图像化显示。 **2.1.3****性能需求分析**: 了保证系统能够长期、安全、稳定、可靠、高效的运行,系统应该满足以下的性能需求: **1)** **系统的准确性和及时性** 系统处理的准确性和及时性是系统的必要性能。在系统设计和开发过程中,要充分考虑系统当前和将来可能承受的工作量,使系统的处理能力和响应时间能够满足用户对信息的处理。由于系统的查询功能对于整个系统的功能和性能完成很重要。从系统的多个数据来源来看,文章信息查询、用户信息查询,其准确性很大程度上决定了系统的成败。因此,在系统开发过程中,系统采用优化的SQL语句及安全扩展存储过程来保证系统的准确性和及时性。 **2)** **系统的安全性要求** 对于此类涉及文章管理以及需要个人隐私信息的平台系统需要有一定的数据加密以及信息保护措施来保障用户信息安全以及系统的安全维护。 **3)** **系统的易用性要求** 需要具有简单明了的操作流程,便捷的信息浏览、注册登录以及简洁易用的人机交互界面,对用户应尽量友好并且只需要较小的学习成本便可以熟练使用该管理系统。 **4)** **响应速度、用户容量要求** 作为提供便捷交易的系统平台,应向着轻便快速的方向靠拢,对于用户执行操作的响应不能超过一秒。对于庞大的用户群体,鉴于存在非活跃用户的删除机制,本系统持久层的用户信息存储至少需要能存储一万名用户的物理容量;同时对于在线用户至少要保证一百人的同时在线。 **5)** **系统的易维护性与可拓展性** 系统在开发过程中,应该充分考虑以后的可扩充性。例如系统权限和文章信息设置等模块也会不断的更新和完善。所有这些都要求系统提供足够的手段进行功能的调整和扩充。而要实现这一点,应通过系统的开放性来完成,既系统应是一个开放系统,只要符合一定的规范,可以简单的加入和减少系统的模块,配置系统的硬件。要符合开发规范,除了基础的代码规范之外要配备精炼统一的注释说明,同时遵循模块化设计原则,提供友好的模块接口,以在未来可以进行系统功能的增减和维护迭代。 **2.1.4****数据需求分析**: 经分析,用户信息是用户管理子系统中的基本信息,所建立的用户实体的基本属性包括:账户id、账户密码、联系方式、邮箱、昵称、生日、余额;而文章信息则是文章管理子系统中的基本信息,所建立的文章实体的基本信息包括:文章id、文章简介、文章名、文章内容, 最终所呈现的实体属性联系图如下所示: ![img](README.assets/clip_image035.jpg) **2.2** **可行性分析** **2.2.1** **经济可行性** 经济可行性是指该系统的开发和维护成本是否合理。需要评估系统的开发和维护成本是否可以在预算范围内,并且评估该系统是否能够为公司带来足够的利润。本系统开发经费对于本单位在经济上是可以接受的,并且本系统实施后可以显著提高某个领域的管理效率。 **2.2.2** **技术可行性** 本管理系统拟采用Browser/Server模式进行开发。 软件方面可以使用Java等编程语言进行开发,使用MySQL等关系型数据库进行数据存储,同时还可以利用现有的Web开发框架,如Spring MVC等,快速构建出一个稳定可靠的管理系统。 硬件方面,硬件更新的速度越来越快、容量越来越大、可靠性越来越高、价格越来越低、其硬件平台完全能满足此系统的需要。 **2.2** **总体设计** **2.2.1** **系统体系结构设计** 本系统采用B/S体系架构,即浏览器/服务器体系结构,用户工作界面是通过WWW浏览器来实现,极少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现 浏览器端:即用户使用的浏览器,是用户操作系统的接口,用户通过浏览器界面向服务器端提出请求,并对服务器端返回的结果进行处理并展示,通过界面可以将系统的逻辑功能更好的表现出来。 服务器端:提供数据服务,操作数据,然后把结果返回前端,结果显示在系统界面上。 ![img](README.assets/clip_image037.jpg) **2.2.2****软件结构设计** ​ 本系统采用模块化的结构设计方式,设计过程中采用三层架构的思想将软件结构分为表现层,业务逻辑层以及持久层,在简化软件设计过程的同时兼具良好的拓展性以及层次性,以实现功能模块之间的相互独立,基本做到了软件设计中高内聚低耦合的目标。经过对变换型数据流图的分析得本Web管理系统软件体系架构图如下所示: ![图示, 示意图 描述已自动生成](README.assets/clip_image039.jpg) 依照自顶向下逐步求精的程序设计方法,本次系统设计参考三层架构进行实现。 在表现层中的Controller用于接收客户端请求并依据请求内容调用UserService中的业务逻辑处理方法(如UerInfoChange用户信息修改)进行处理,处理完http请求后根据处理结果进行对应的Response返回,前端的View页面视图根据返回的内容进行相应的渲染。 而业务逻辑层则主要负责负责业务逻辑处理,其实现不依赖于表现层,只是可能会调用数据持久层的方法来实现业务需求,要求保证数据一致性。 持久层则负责数据的持久化,针对数据库进行访问,提供业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。 ![图形用户界面 描述已自动生成](README.assets/clip_image041.jpg) MVC模式是软件工程中常见的一种软件架构模式,该模式把软件系统(项目)分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),并赋予各个部分不同的功能,方便开发人员进行分组。 (1)视图(View):负责界面的显示,以及与用户的交互功能,例如表单、网页等。 (2)控制器(Controller):可以理解为一个分发器,用来决定对于视图发来的请求,需要用哪一个模型来处理,以及处理完后需要跳回到哪一个视图。即用来连接视图和模型。 (3)模型(Model):模型持有所有的数据、状态和程序逻辑。模型接受视图数据的请求,并返回最终的处理结果。 MVC模式的流程如下:浏览器通过视图向控制器发出请求,控制器接收到请求之后通过选择模型进行处理,处理完请求以后再转发到视图,进行视图界面的渲染并做出最终响应,如图所示。 在MVC模式中,视图View可以用JSP/HTML/CSS实现,模型Model可以用JavaBean实现,而控制器Control就可以用Servlet来实现。 **2.2.3****数据库设计** 创建名为WebDB的数据库,经对数据进行需求分析以及抽象化后,所得到的实体关系模式为: 用户(用户Id,密码,昵称,性别,联系方式,邮箱,生日,余额) 文章(文章Id,文章名,文章简介,文章内容) 定义基本表如下: 表2.1 tb_user(用户表) | 属性列名 | 属性说明 | 数据类型 | 码 | 外码 | 备注 | | ---------- | -------- | ----------- | ---- | ---- | ------------------------- | | u_id | 用户编号 | Char(12) | 主码 | | (U+日期+id) UyyyymmddXXX | | u_nickname | 用户姓名 | Varchar(9) | | | Not null | | u_password | 用户密码 | Varchar(20) | | | Not null | | u_gender | 性别 | Tinyint | | | 0-girl 1-boy | | u_phone | 联系方式 | Char(11) | | | Unique | | u_email | 电子邮件 | Varchar(30) | | | | | u_birthday | 生日 | Timestamp | | | Default current_stamp | | u_money | 余额 | Double | | | >= 0, default 0 | 表2.2 tb_article | 属性列名 | 属性说明 | 数据类型 | 码 | 外码 | 备注 | | --------- | -------- | ------------- | ---- | ---- | -------------- | | a_id | 文章编号 | Int | 主码 | | Auto_increment | | a_name | 文章名 | Varchar(20) | | | Not null | | author_id | 作者编号 | Char(12) | | 外码 | Not null | | a_desc | 文章描述 | Varchar(100) | | | Deflaut “lazy” | | a_body | 文章内容 | Varchar(1000) | | | Not null | **2.3** **详细设计** 对于Controller中RequestMapping映射的设置,对于页面请求则全部配置在RouterController中,而对于用户数据以及文章数据的请求则分别配置在UserController和ArticleController中,请求URL分别以“/user”以及“/article”开头。 **2.3.1****用户登录****/****注册****/****密码重置模块** 功能简介:模块功能为验证用户身份,需要输入用户的账号id和用户密码,系统会根据输入的账号、密码进行判断,如果输入无误且合法,则会依据用户的身份类别提供相应操作界面(/MainPage),而成功登录的用户将信息存入session当中作为所登录用户的标识,以此用户信息来初始化部分页面。 a) 登录页面:LoginPage.jsp b) 页面组成:用户名的textField和密码的passwordField以及一个用户“登录”button、一个用户“注册”button和“忘记密码”的button c) 相关路由: /login —— 登录页面访问请求 /user/loginDo —— 用户登录请求 /user/logonDo —— 用户注册请求 /user/updateUser —— 用户信息更新请求 /mainPage —— 主界面请求 d) 请求参数及响应:(若为页面请求则返回ModelAndView,可携带参数) (其中Response包装在Result当中,带有状态码Code:200 — OK;500 — false;406 — err ,信息msg以及携带数据的Data) | 接口名称 | 接口类型 | 接口功能 | Request参数 | Response参数 | | ---------------- | -------- | ---------------- | ---------------------------------------- | -------------------------------------------------------- | | /Login | Get | 返回用户登录页面 | Null | Null | | /User/loginDo | post | 用户登录请求 | userId、password | “LoginUser”(true) Or Null(false) | | /User/LogonDo | post | 用户注册请求 | userId password birthday phone | 用户注册, Return true or false, Msg中为响应信息 | | /User/UpdateUser | post | 用户信息更新 | userId(must) 及其他需要更新的用户信息 | 用户更新, Return true of false, Msg中为响应信息 | | /MianPage | Get | 返回主页面 | Null | 获取Session中的登录信息返回带有LoginUser的ModelAndView。 | e)用户登录模块盒图:(以登录举例、其余模块请求类似) ![img](README.assets/clip_image043.jpg)![img](README.assets/clip_image045.jpg) **2.3.2****用户管理模块** 功能简介:模块功能为管理员对列表选中用户进行用户管理,包括信息修改、非活账号删除、昵称模糊查询、添加用户。 a) 操作页面:UserTablePage.jsp b) 页面组成:采用layui框架的表格,要求有搜索框、添加、删除、以及修改的button c) 相关路由: /userTablePage —— 用户列表页面访问请求 /user/userEditPage —— 用户编辑页面请求 /user/userAddPage —— 用户添加页面请求 /user/getUserList —— 用户列表获取请求 /user/addUser —— 用户新增请求 /user/updateUser —— 用户信息更新请求 /user/deleteUser —— 用户删除请求 e) 请求参数及响应:(若为页面请求则返回ModelAndView,可携带参数) (其中Response包装在Result当中,带有状态码Code:200 — OK;500 — false;406 — err ,信息msg以及携带数据的Data) | 接口名称 | 接口类型 | 接口功能 | Request参数 | Response参数 | | ------------------ | -------- | -------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------- | | /userTablePage | Get | 返回用户信息列表页面 | Null | Null | | /user/userEditPage | Get | 用户信息编辑页面请求 | userId | “editUser”(true) Or Null(false) | | /user/userAddPage | Get | 用户添加页面请求 | Null | Null | | /user/getUserList | Get | 获取用户列表(分页),如果携带有search参数则进行模糊查询 | Page、limit (search) | Page{ Page、size、List、 total } | | /user/addUser | POST | 添加用户 | User | True or false With msg | | /user/updateUser | POST | 用户信息更新 | userId(must) 及其他需要更新的用户信息 | 用户更新, Return true of false, Msg中为响应信息 | | /user/deleteUser | POST | 用户删除申请 | userId、ask | True or false With msg | **2.3.1****文章管理模块** 功能简介:模块功能为管理员对用户以及个人的文章进行管理,包括信息修改、文章删除、文章查询、添加文章以及图表显示。 a) 操作页面:ArticleTablePage.jsp b) 页面组成:采用layui框架的表格,要求有搜索框、添加、删除、以及修改的button c) 相关路由: /articleTablePage —— 文章列表页面访问请求 /article/articleEditPage —— 文章编辑页面请求 /article/article AddPage —— 文章添加页面请求 /article/getArticleList —— 文章列表获取请求 /article/addArticle —— 文章新增请求 /article/updateArticle —— 文章信息更新请求 /article/deleteArticle —— 文章删除请求 d) 请求参数及响应:(若为页面请求则返回ModelAndView,可携带参数) (其中Response包装在Result当中,带有状态码Code:200 — OK;500 — false;406 — err ,信息msg以及携带数据的Data) | 接口名称 | 接口类型 | 接口功能 | Request参数 | Response参数 | | ------------------------ | -------- | -------------------------------------------------------- | ------------------------------------------- | --------------------------------------------------- | | /articleTablePage | Get | 返回文章列表页面 | Null | Null | | /article/articleEditPage | Get | 文章信息编辑页面请求 | articleId | “editArticle”(true) Or Null(false) | | /article/article AddPage | Get | 文章添加页面请求 | Null | Null | | /article/getArticleList | Get | 获取文章列表(分页),如果携带有search参数则进行模糊查询 | Page、limit、 (authorId)、(search) | Page{ Page、size、 List
、total } | | /article/addArticle | POST | 添加文章 | authorId、 article | True or false With msg | | /article/updateArticle | POST | 文章信息更新 | articleId(must) 及其他需要更新的文章信息 | 文章更新, Return true of false, Msg中为响应信息 | | /article/deleteArticle | POST | 文章删除申请 | articleId、ask | True or false With msg | **三、** **系统开发与实现** **3.1****编程语言及开发环境介绍** **3.1.1****编程语言****:****Java****、****HTML****、****CSS****、****JavaScript****、****SQL** 依据需求分析以及经过相关技术可行性分析后,结合所拟定的系统规模大小以及软件的拓展性要求,综合考量过后选定使用java作为系统的程序设计语言。同时为了合作编写的项目结构统一,选择使用maven进行项目构建和依赖管理;而对于持久层的数据存储则借助MyBatis这一款优秀的持久层框架,而Web开发前端框架是在原生的HTML+CSS+JavaScript的基础上借助Layui进行页面的编写。后端则采用SpringMVC+Spring+MyBatis的集成方案进行开发。 **3.1.2****开发****环境** 针对所选取的主要程序语言以及队友双方的开发经验和IDE熟练程度,本次系统的开发所拟定的开发环境如下: a) 集成开发环境: Visual Studio Code(前端实现) IntelliJ IDEA 2022.1.4(后端项目实现) MySQL80(数据库) b) 主要开发依赖: Lay-ui V2.5.5 java Jdk 16.0.2 mybatis 3.5.11 maven 3.8.6 Spring相关 5.3.1 c) 版本控制: Git+Gitee **3.2** **前端实现部分** **3.2.1****项目结构** ![img](README.assets/clip_image047.jpg) **3.2.2 HTML****页面实现(部分)** 1)Login.html: 以最外部的外部容器容纳三个登录、注册和密码重置的容器,其中分别包含了各自的表单和表单中对应的input、submit以及转跳连接,通过noDisplay样式以设置不可见,以JavaScript来进行不同功能容器的切换。 ![电脑萤幕的截图 描述已自动生成](README.assets/clip_image049.jpg)![img](README.assets/clip_image051.jpg) 2)articleTable.html: 参照layui的表格实现,然后为在articleTable下方实现用户文章属的可视化图表,为echarts一个合适大小的DOM节点然后后续由Script脚本实现动态渲染。 ![img](README.assets/clip_image053.jpg) **3.2.3 CSS****样式实现(部分)** ​ Login.css: ![文本 描述已自动生成](README.assets/clip_image055.jpg)![img](README.assets/clip_image057.jpg) **3.2.4 JavaScript****实现(部分)** 1)密码重置按钮响应事件: // 密码重置 $('#resetPwd_button').click(function () { ​ var pwd1_in = document.getElementById("password1_re"); ​ var pwd2_in = document.getElementById("password2_re"); ​ var username = document.getElementById("username_re").value; ​ var phone = document.getElementById("telephone_re").value; ​ var pwd1 = pwd1_in.value; ​ var pwd2 = pwd2_in.value; ​ if (pwd1 !== pwd2 || pwd1 == null || pwd2 == null) { ​ pwd2_in.setCustomValidity("确认密码和新密码不一致!!"); ​ return; ​ } else { ​ pwd2_in.setCustomValidity(""); ​ } ​ if (!checkPwd_inElem("password2_re")) { ​ return; ​ } ​ $.ajax({ ​ url:baseUrl+"/user/updateDo", ​ data: JSON.stringify({ ​ userId: username, ​ password: pwd2, ​ phone: phone ​ }), ​ type: "post", ​ contentType: "application/json;charset=UTF-8", ​ dataType: "json", ​ success:function (result) { ​ if(result!=null){ ​ alert(result.message); ​ if(result.status===200){ ​ setTimeout(function (){ ​ window.location.href=baseUrl+"/login"; ​ },1000); ​ } ​ } ​ else{ ​ alert("修改失败,请稍后重试"); ​ } ​ }, ​ error:function(e){ ​ console.log(e,e.message); ​ alert('error'); ​ } ​ }) }); 2)articleTable渲染请求 ​ table.render({ ​ elem: '#articleMainTable', ​ url: 'user/getUserList', ​ method:'post', ​ toolbar: '#toolbarDemo', ​ defaultToolbar: ['filter', 'exports', 'print', { ​ title: '提示', ​ layEvent: 'LAYTABLE_TIPS', ​ icon: 'layui-icon-tips' ​ }], ​ cols: [[ ​ {type: 'numbers', title: '序号', width: 50, fixed: 'left' }, ​ {field: 'userId',align:'center', title: 'ID',sort: true}, ​ {field: 'nickName', title: '用户名'}, ​ {field: 'articleNum', title: '文章数量'}, ​ {title: '操作', minWidth: 130, toolbar: '#currentTableBar', align: "center"} ​ ]],done: function (res, curr, count) { ​ console.log(res); ​ var dataName = [], dataList = []; ​ for (var i = 0; i < res.data.length; i++) { ​ dataName.push(res.data[i].userId); ​ dataList.push(res.data[i].articleNum); ​ } ​ // 渲染图表; ​ echart1Str(dataName, dataList); ​ }, ​ limits: [8, 12, 16, 20, 35, 50], ​ limit: 8, ​ page: true, ​ skin: 'line' ​ }); 3)样式切换 function goToLogin(){ ​ $('.containar').css('transform', 'translateX(80%)'); ​ $('.logon').addClass('nodisplay'); ​ $('.resetPwd').addClass('nodisplay'); ​ $('.login').find('input[name!=sex]').val(''); ​ $('.login').removeClass('nodisplay'); } function goToLogon(){ ​ $('.containar').css('transform', 'translateX(80%)'); ​ $('.login').addClass('nodisplay'); ​ $('.resetPwd').addClass('nodisplay'); ​ $('.logon').find('input[name!=sex]').val(''); ​ $('.logon').removeClass('nodisplay'); } function goToRestPwd(){ ​ $('.containar').css('transform', 'translateX(80%)'); ​ $('.login').addClass('nodisplay'); ​ $('.logon').addClass('nodisplay'); ​ $('.resetPwd').find('input').val(''); ​ $('.resetPwd').removeClass('nodisplay'); } **3.3** **后端****SSM****框架实现部分** **3.3.1****项目结构** ![img](README.assets/clip_image059.jpg) **3.3.2 Controller**层(以*UserController*为例) // 获取用户列表: @RequestMapping(value = "getUserList",method = RequestMethod.POST) @ResponseBody public Object getUserList(int page,int limit,String search){ ​ System.out.println(search); ​ Page userPage; ​ if(StringUtils.isNoneBlank(search)){ ​ userPage = userService.getUserListByNameLike(search,page,limit); ​ }else{ ​ userPage = userService.getUserList(page,limit); ​ System.out.println(userPage); ​ } ​ return PageManage.buildPage(userPage); } **3.3.3 Service**层 // 按照名字模糊查询用户 获取分页列表 public Page getUserListByNameLike(String name, int page, int size) { ​ Page userPage = new Page<>(page, size); ​ userPage.setRows(userMapper.getUserListByNameLike(name,(page - 1)*size, size)); ​ userPage.setTotal(userMapper.getCount(name)); ​ return userPage; } **3.3.4 Dao**层(**Mapper**) // 根据用户名模糊查询 **3.4** **页面展示** ![img](README.assets/clip_image061.jpg) 图3.1 登录页面 ![图形用户界面, 应用程序 描述已自动生成](README.assets/clip_image063.jpg)![img](README.assets/clip_image065.jpg) 图3.2 登录界面自适应以及注册界面 ![img](README.assets/clip_image067.jpg) 图3.3 首页 ![img](README.assets/clip_image069.jpg)、 图3.4 个人资料修改 ![图形用户界面, 表格 描述已自动生成](README.assets/clip_image071.jpg) 图3.5 用户列表 ![图形用户界面, 应用程序 描述已自动生成](README.assets/clip_image073.jpg) 图3.6 文章列表及数据可视化图表 ![img](README.assets/clip_image075.jpg) 图3.7 用户文章管理及添加操作 **3.5** **测试** 问题记录:在对用户文章进行添加或删除后返回带有ArticleMainPage,用户的文章数量不会立即更新用户的文章数量,经问体分析后发现是由于用户文章和用户两个Mapper有级联查询的关系,如果开启了MyBatis的二级缓存功能,其在执行select时若进行过增删改则会进行缓存更新,单在单表(UserMapper)进行查询(用户列表带文章数量的级联查询)的时候,由于添加的是另一个Mapper(Article),User这个Mapper并没有信息更新,所以会读取缓存中的文章数量,由此产生数据不一致的问题。可通过关闭二级缓存或者自定义缓存注解来实现缓存的级联更新 。 经白盒、黑盒测试,本系统最终运行无误。 #### 特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)