基于SpringBoot+MyBatis实现的个人博客系统(一)

这篇主要讲解一下如何基于SpringBoot和MyBatis技术实现一个简易的博客系统(前端页面主要是利用CSS,HTML进行布局书写),前端的静态页面代码可以直接复制粘贴,后端的接口以及前端发送的Ajax请求需要自己书写.

博客系统需要完成的接口:

  • 注册
  • 登录
  • 博客列表页展示
  • 博客详情页展示
  • 发布博客
  • 修改博客
  • .......

 完整版代码详见Gitee:blogsystem · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)

项目亮点:

  1. 密码实现加盐处理,确保安全性;
  2. Session升级,由原来的内存存储改为通过Redis存储,不会丢失,并且支持分布式部署;
  3. 功能升级,对于博客列表的展示添加了分页功能;
  4. 登录验证升级,添加了拦截器的功能对用于的登录进行校验;
  5. ......

一, 项目的搭建

1,导入依赖坐标(创建SpringBoot项目)

在书写任何一个项目的同时,需要先将项目的基础给搭建好,搭建项目需要提前考虑好项目的一些功能需要哪些依赖,从而进行添加,如何创建SpringBoot项目可以看我的另一篇博客:SpringBoot项目的创建和使用_蜡笔小心眼子!的博客-CSDN博客

 博客系统需要添加的依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.16</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>blogsystem</artifactId><version>0.0.1-SNAPSHOT</version><name>blogsystem</name><description>blogsystem</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>2.3.1</version><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

2,书写配置文件

需要使用MyBatis技术的项目需要配置你的数据库相关的信息以及Mapper的xml文件存储的位置,同时也可以在配置文件中定义一下日志的打印级别,从而方便查看数据库操作的完整信息:

#配置数据库连接信息
spring:datasource:url: "你自己的数据库"username: rootpassword: "数据库对应的密码"driver-class-name: com.mysql.cj.jdbc.Driver#配置Mapper的xml文件存储信息
mybatis:
#  xml的存储位置mapper-locations: classpath:mapper/*Mapper.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#配置日志打印级别
logging:level:com:example:demo: debug

二, 将前端页面部署到项目中(resource文件下的static目录中)

对于后端程序员来说可以不用特别注重前端样式的书写,但是需要看得懂前端的代码以及和后端交互的请求即可(学有余力的情况下可以适当学习从而优化自己项目中的前端页面),这里的静态页面信息可以直接在我的码云中进行下载:SSM配置信息: 存放SSM项目中的一些配置信息 (gitee.com)博客系统(静态页面).zip · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)SSM配置信息: 存放SSM项目中的一些配置信息 (gitee.com)

将下载好的前端页面全选之后直接复制到static目录下即可:

三, 初始化数据库

在配置文件中我们已经配置了数据库的连接信息,但是此时在数据库中还没有初始化一些数据,所以我们需要初始化数据,方便写项目的时候进行测试,初始化数据库的SQL代码也可以在我们的码云中进行下载:博客系统初始化数据库-ssm.sql · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)

查看数据库中的表即相应表结构:

 

 四, 对项目的整体架构进行分层

一个企业级的SM项目都需要对其进行合理的分层,每一层处理每一层的业务逻辑,我在项目中的分层如下:

  • common:一些工具类
  • config:配置信息类
  • controller:处理前端请求的类
  • entity:实体类(也可以写成model)
  • mapper:用来和Mapper.xml文件交互的接口类
  • service:处于controller和mapper之间的类

五, 书写前后端交互的功能

从这里开始就是项目的核心了,这里开始可以对前后端的接口和相应功能进行书写了!

1, 统一返回对象的封装

为了给前端返回统一的对象,后端需要定义一个类对返回的数据进行封装,该类包含code,msg和data三个属性,该类定义在common包下:

package com.example.blogsystem.common;import lombok.Data;import java.io.Serializable;/*** 统一返回对象* 返回成功的话 code 设置成 200* 反悔失败的话 code 设置成本身的 code*/@Data
public class AjaxResult implements Serializable {private int code;private String msg;private Object data;/*** 返回成功** @param data* @return*/public static AjaxResult success(Object data) {AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(200);ajaxResult.setMsg("");ajaxResult.setData(data);return ajaxResult;}public static AjaxResult success(Object data, String msg) {AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(200);ajaxResult.setMsg(msg);ajaxResult.setData(data);return ajaxResult;}/*** 返回失败** @param code* @param msg* @return*/public static AjaxResult fail(Integer code, String msg) {AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(code);ajaxResult.setMsg(msg);ajaxResult.setData("");return ajaxResult;}public static AjaxResult fail(Integer code, String msg, String data) {AjaxResult ajaxResult = new AjaxResult();ajaxResult.setCode(code);ajaxResult.setMsg(msg);ajaxResult.setData(data);return ajaxResult;}
}

2, 注册功能

注册功能就是用户给后端发送一次请求之后,后端就会在数据中新增一条用户记录!

前端

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>注册页面</title><link rel="stylesheet" href="css/conmmon.css"><link rel="stylesheet" href="css/login.css"><!-- 引入jquery的js文件 --><script src="js/jquery.min.js"></script>
</head><body>
<!-- 导航栏 -->
<div class="nav"><img src="img/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用来占据中间位置 --><span class="spacer"></span><a href="blog_list.html">主页</a><a href="login.html">登陆</a><!-- <a href="#">注销</a> -->
</div>
<!-- 版心 -->
<div class="login-container"><!-- 中间的注册框 --><div class="login-dialog"><h3>注册</h3><div class="row"><span>用户名</span><input type="text" id="username"></div><div class="row"><span>密码</span><input type="password" id="password"></div><div class="row"><span>确认密码</span><input type="password" id="password2"></div><div class="row"><button id="submit" onclick="mysub()">提交</button></div></div>
</div><!-- 最好把js的代码写在这下面 因为js代码的代码是按照从上往下执行的 如果写在上面的话 可能js的代码获取的一些标签在下面还没有生效 会获取失败 -->
<script>function mysub(){// 1.非空判断// 1.1 先得到输入的组件var username = jQuery("#username");var password = jQuery("#password");var password2 = jQuery("#password2");// 1.2 判断输入组件是否为空if(username.val().trim()==""){alert("请先输入用户名!");username.focus(); // 聚焦光标return false;}if(password.val().trim()==""){alert("请先输入密码!");password.focus();return false;}if(password2.val().trim()==""){alert("请先输入确认密码!");password2.focus();return false;}if(password.val()!=password2.val()){alert("两次密码输入不一致性,请先检查!");password.focus();return false;}// 2.先把提交按钮设置成不可用(禁用)jQuery("#submit").attr("disabled","disabled");// 3.将当前页面的数据提交给后端jQuery.ajax({url:"/user/reg",type:"POST",data:{"username":username.val().trim(),"password":password.val().trim()},success:function(res){// 4.根据后端返回的结果(成功or失败)再处理后续流程if(res.code==200 && res.data==1){alert("注册成功!");location.href = "login.html"; // 调整到登录页面}else{alert("抱歉:操作失败!"+res.msg);// 取消禁用jQuery("#submit").removeAttr("disabled");}}});}
</script>
</body></html>

 前端给后端发送请求都是通过Ajax来实现的,所以对于任何需要发送Ajax请求的页面都需要导入js的依赖(后面就不再赘述)

后端(这里的后端只展示controller包中的代码)

@RequestMapping("/reg")public AjaxResult reg(UserInfo userInfo) {//1.对前端传递来的参数进行校验if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) || !StringUtils.hasLength(userInfo.getPassword())) {return AjaxResult.fail(-1, "参数有误!");}//2.与数据库进行交互实现注册的功能//将密码进行加盐加密userInfo.setPassword(PasswordTools.encrypt(userInfo.getPassword()));int result = userService.reg(userInfo);//3.对于查询结果给前端进行反馈return AjaxResult.success(result);}

3, 登录功能 

登录就是前端给后端传递用户名和密码,后端从数据库中查询是否存在这样的用户!

前端

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登陆页面</title><link rel="stylesheet" href="css/conmmon.css"><link rel="stylesheet" href="css/login.css"><script src="js/jquery.min.js"></script>
</head><body>
<!-- 导航栏 -->
<div class="nav"><img src="img/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用来占据中间位置 --><span class="spacer"></span><a href="blog_list.html">主页</a><a href="reg.html">注册</a>
</div>
<!-- 版心 -->
<div class="login-container"><!-- 中间的登陆框 --><div class="login-dialog"><h3>登陆</h3><div class="row"><span>用户名</span><input type="text" id="username"></div><div class="row"><span>密码</span><input type="password" id="password"></div><div class="row"><button id="submit" onclick="mysub()">提交</button></div></div>
</div><script>function mysub() {//1.对提交的数据进行判空操作var username = jQuery("#username");var password = jQuery("#password");if(username.val().trim() == "") {alert("请输入用户名!");username.focus();return false;}if(password.val().trim() == "") {alert("请输入密码!");password.focus();return false;}//2.发送数据给服务器jQuery.ajax({url:"user/login",type:"post",data:{"username":username.val().trim(),"password":password.val().trim()},success:function(res) {//这里针对服务器的响应数据 规定返回1是成功 返回0是失败if(res.code == 200 && res.data == 1) {alert("恭喜:登录成功!");location.href = "myblog_list.html";} else {alert("抱歉:登录失败!" + res.msg);return false;}}});}
</script>
</body></html>

登录功能的前端代码和注册功能前端代码几乎一模一样

后端

@RequestMapping("/login")public AjaxResult login(String username, String password, HttpServletRequest request) {//1.对前端传递来的参数进行校验if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return AjaxResult.fail(-1, "参数有误!");}//2.根据用户名去数据库中进行查询UserInfo userInfo = userService.login(username);if (userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-2, "用户名或者密码错误!");}//对数据库中查找的密码进行解密
//        if (!PasswordTools.check(password,userInfo.getPassword())) {
//            return AjaxResult.fail(-2, "用户名或者密码错误!");
//        }if (!userInfo.getPassword().equals(password)) {return AjaxResult.fail(-2, "用户名或者密码错误!");}//当前表示登陆成功 需要存储sessionHttpSession session = request.getSession();session.setAttribute(ApplicationVariable.USERINFO_SESSION_KEY, userInfo);return AjaxResult.success(1);}

登录的时候需要存储用户的session(会话)信息,因为session的Key需要在多个地方使用,我们将该属性抽象出来放在了common这个公共包下了:

package com.example.blogsystem.common;public class ApplicationVariable {public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}

 对于取出session()会话)中的的用户信息也可以将其封装公共的类,放在common包下:

package com.example.blogsystem.common;import com.example.blogsystem.entity.UserInfo;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;public class UserSessionTools {public static UserInfo getLoginUser(HttpServletRequest request) {HttpSession session = request.getSession(false);if (session != null && session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY) != null) {return (UserInfo) session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY);}return null;}
}

4, 实现拦截器

对于一些博客信息的操作需要用户进行登录,所以可以通过拦截器判断用户具有相应的权限,只有通过拦截器的用户才可以操作,可以将拦截的配置信息放在config包下.

实现HandlerInterceptor接口:

package com.example.blogsystem.config;import com.example.blogsystem.common.ApplicationVariable;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@Configuration
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if (session != null && session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY) != null) {return true;}response.sendRedirect("/login.html");//没有通过拦截器的请求需要跳转到登录页面先登录return false;}
}

实现WebMvcConfigurer进行配置接口:

package com.example.blogsystem.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
public class MyConfig implements WebMvcConfigurer {@Resourceprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**") //拦截所有的url.excludePathPatterns("/login.html").excludePathPatterns("/reg.html").excludePathPatterns("/blog_list.html").excludePathPatterns("/blog_content.html").excludePathPatterns("/css/**").excludePathPatterns("/editor.md/**").excludePathPatterns("/img/**").excludePathPatterns("/js/**").excludePathPatterns("/user/reg").excludePathPatterns("/user/login");}
}

这里需要先放开所有的静态页面,图片以及登录和注册接口.

5, 博客添加功能

博客添加功能就是前端向后端提交博客的一些信息,后端在文章表中插入一条文章记录即可!

前端

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文章添加</title><!-- 引入自己写的样式 --><link rel="stylesheet" href="css/conmmon.css"><link rel="stylesheet" href="css/blog_edit.css"><!-- 引入 editor.md 的依赖 --><link rel="stylesheet" href="editor.md/css/editormd.min.css" /><script src="js/jquery.min.js"></script><script src="editor.md/editormd.js"></script>
</head><body><!-- 导航栏 --><div class="nav"><img src="img/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用来占据中间位置 --><span class="spacer"></span><a href="blog_list.html">主页</a><a href="#">注销</a></div><!-- 编辑框容器 --><div class="blog-edit-container"><!-- 标题编辑区 --><div class="title"><input id="title" type="text" placeholder="在这里写下文章标题"><button onclick="mysub()">发布文章</button></div><!-- 创建编辑器标签 --><div id="editorDiv"><textarea id="editor-markdown" style="display:none;"></textarea></div></div><script>var editor;function initEdit(md){// 编辑器设置editor = editormd("editorDiv", {// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. width: "100%",// 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度height: "calc(100% - 50px)",// 编辑器中的初始内容markdown: md,// 指定 editor.md 依赖的插件路径path: "editor.md/lib/",saveHTMLToTextarea: true // });}initEdit("# 在这里写下一篇博客"); // 初始化编译器的值// 提交function mysub(){//1.对文章标题和内容进行判空操作var title = jQuery("#title");var content = editor.getValue();if(title.val().trim() == "") {alert("请输入文章标题!");title.focus();return false;}if(content == "") {alert("请输入正文!")return false;}//2.提交数据给后端jQuery.ajax({url:"/art/add",type:"post",data:{"title":title.val(),"content":content},success:function (res) {//假设文章添加成功后端给前端返回的data中的数据是1if(res.code == 200 && res.data == 1) {alert("恭喜:文章添加成功!");if (confirm("是否继续添加文章?")) {//如果继续继续添加文章的话 需要刷新此页面location.href = location.href;} else {//不继续添加文章需要跳转到文章列表页location.href = "myblog_list.html";}} else{alert("抱歉:文章添加失败!" + res.msg);}}})// alert(editor.getValue()); // 获取值// editor.setValue("#123") // 设置值}</script>
</body></html>

这里的前端页面引入了MarkDown编辑器,所以添加博客的时候相较于其他官方博客系统更加真实,而且该编辑器提供了一些Api让我们进行格式转换的时候更加方便.

//相关API
alert(editor.getValue()); // 获取值
editor.setValue("#123") // 设置值

后端

@RequestMapping("/add")public AjaxResult add(ArticleInfo articleInfo, HttpServletRequest request) {//1.对前端传递来的参数进行判空操作if (articleInfo == null || !StringUtils.hasLength(articleInfo.getTitle()) || !StringUtils.hasLength(articleInfo.getContent())) {return AjaxResult.fail(-1, "参数错误!");}//2.获取当前的uid进行校验UserInfo userInfo = UserSessionTools.getLoginUser(request);if (userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-1, "参数错误!");}//3.封装uid进行持久化articleInfo.setUid(userInfo.getId());int result = articleService.add(articleInfo);//4.给前端进行数据反馈return AjaxResult.success(result);}

这里数据库存储的是MarkDown格式的数据,是为了方便进行修改的时候直接进行修改省去了一次从Html转换成MarkDown格式的操作.

6, 博客编辑功能

博客编辑功能需要实现两个操作:

1.先去查询当前文章的信息进行展示(页面加载的时候进行调用)

2.提交修改操作(触发提交按钮的时候进行调用)

前端

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文章修改</title><!-- 引入自己写的样式 --><link rel="stylesheet" href="css/conmmon.css"><link rel="stylesheet" href="css/blog_edit.css"><!-- 引入 editor.md 的依赖 --><link rel="stylesheet" href="editor.md/css/editormd.min.css"/><script src="js/jquery.min.js"></script><script src="editor.md/editormd.js"></script><script src="js/urltools.js"></script><script src="js/logout.js"></script>
</head><body>
<!-- 导航栏 -->
<div class="nav"><img src="img/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用来占据中间位置 --><span class="spacer"></span><a href="blog_list.html">主页</a><a href="javascript:logout()">注销</a>
</div>
<!-- 编辑框容器 -->
<div class="blog-edit-container"><!-- 标题编辑区 --><div class="title"><input id="title" type="text" placeholder="在这里写下文章标题"><button onclick="mysub()">发布文章</button></div><!-- 创建编辑器标签 --><div id="editorDiv"><textarea id="editor-markdown" style="display:none;"></textarea></div>
</div><script>var isSubmit = 1;var id = 0;var editor;function initEdit(md) {// 编辑器设置editor = editormd("editorDiv", {// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.width: "100%",// 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度height: "calc(100% - 50px)",// 编辑器中的初始内容markdown: md,// 指定 editor.md 依赖的插件路径path: "editor.md/lib/",saveHTMLToTextarea: true //});}// initEdit("# 在这里写下一篇博客"); // 初始化编译器的值//接口1:先去查询当前文章的信息进行展示 页面加载的时候进行调用function initArt() {//1.通过queryString获取文章idid = getParamByKey("id");if (id == null || id <= 0) {isSubmit = 0;alert("抱歉:非法参数!");return false;}//2.向后端发送请求 获取对应id的文章并展现在前端页面jQuery.ajax({url: "/art/getdetailbyid",type: "post",data: {"id": id},success: function (res) {if (res.code == 200 && res.data != null && res.data.id > 0) {//文章查询成功jQuery("#title").val(res.data.title);initEdit(res.data.content);} else {//文章获取失败isSubmit = 0;alert("抱歉:非法参数!" + res.msg);}}});}initArt();//接口2:提交修改操作 触发提交按钮的时候进行调用// 提交function mysub() {if (isSubmit == 0) {alert("抱歉:非法操作,请刷新页面再试!");return false;}//1.非空判断var title = jQuery("#title");var content = editor.getValue();if (title.val().trim() == "") {alert("请输入文章标题!");title.focus();return false;}if (content == "") {alert("请输入正文!")return false;}//2.提交请求给后端jQuery.ajax({url: "/art/update",type: "post",data: {"id": id,"title": title.val(),"content": content},success: function (res) {//规定修改成功后端返回1if (res.code == 200 && res.data == 1) {alert("恭喜:修改成功!");location.href = "myblog_list.html";} else {alert("抱歉:非法参数!" + res.msg);}}});// alert(editor.getValue()); // 获取值// editor.setValue("#123") // 设置值}
</script>
</body></html>

后端

/*** 对文章进行修改的时候也需要对拿到文章进行权限验证 拿到的文章的uid必须和登录的用户的id一致* 防止登录的用户对其他人的文章进行篡改*/@RequestMapping("/getdetailbyid")public AjaxResult getdetailbyid(Integer id, HttpServletRequest request) {//1.对id进行判空操作if (id == null || id <= 0) {return AjaxResult.fail(-1, "参数错误!");}//2.获取到登录用户的idUserInfo userInfo = UserSessionTools.getLoginUser(request);if (userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-1, "参数错误!");}//3.封装id和uid进行持久化操作return AjaxResult.success(articleService.getDetailByIdAndUid(id, userInfo.getId()));}@RequestMapping("/update")public AjaxResult update(ArticleInfo articleInfo, HttpServletRequest request) {//1.对前端传递的参数进行判空if (articleInfo == null || articleInfo.getId() <= 0|| !StringUtils.hasLength(articleInfo.getTitle()) || !StringUtils.hasLength(articleInfo.getContent())) {return AjaxResult.fail(-1, "参数错误!");}//2.获取uid进行封装并进行持久化UserInfo userInfo = UserSessionTools.getLoginUser(request);if (userInfo == null || userInfo.getId() <= 0) {return AjaxResult.fail(-1, "参数错误!");}articleInfo.setUid(userInfo.getId());articleInfo.setUpdatetime(LocalDateTime.now());//3.给前端返回数据int result = articleService.update(articleInfo);return AjaxResult.success(result);}

根据文章id进行查找文章时,必须要进行校验,确保查询到的文章时该登录用户的文章,即文章表中的uid = 用户表中的id.

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

九、2023.10.3.Linux(end).9

文章目录 33、简述mmap的原理和使用场景&#xff1f;34、互斥量能不能在进程中使用&#xff1f;35、协程是轻量级线程&#xff0c;轻量级表现在哪里&#xff1f;36、说说常见信号有哪些&#xff0c;表示什么含义&#xff1f;37、说说线程间通信的方式有哪些&#xff1f;38、说说…

C# 自定义控件库之Lable组合控件

1、创建类库 2、在类库中添加用户控件&#xff08;Window窗体&#xff09; 3、控件视图 4、后台代码 namespace UILib {public partial class DeviceInfoV : UserControl{public DeviceInfoV(){InitializeComponent();ParameterInitialize();}#region 初始化private void Par…

初级篇—第二章SELECT查询语句

文章目录 什么是SQLSQL 分类SQL语言的规则与规范阿里巴巴MySQL命名规范数据导入指令 显示表结构 DESC基本的SELECT语句SELECTSELECT ... FROM列的别名 AS去除重复行 DISTINCT空值参与运算着重号查询常数过滤数据 WHERE练习 运算符算术运算符加减符号乘除符号取模符号 符号比较运…

DevSecOps 将会嵌入 DevOps

通常人们在一个项目行将结束时才会考虑到安全&#xff0c;这么做会导致很多问题&#xff1b;将安全融入到DevOps的工作流中已产生了积极结果。 DevSecOps&#xff1a;安全正当时 一直以来&#xff0c;开发人员在构建软件时认为功能需求优先于安全。虽然安全编码实践起着重要作…

python——Django框架

一、基本介绍 Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。 使用 Django&#xff0c;只要很少的代码&#xff0c;Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容&#xff0c;并进一步开发出全功能的 Web 服务 Django 本身基于 MVC …

踩坑笔记 MySQL分页排序查询(Order by limit)导致数据丢失和重复

文章目录 背景现象原因解决方案 背景 分页查询排序后的数据&#xff0c;是一个非常常见的业务场景&#xff1b;但当使用不唯一的字段排序时&#xff0c;分两页查询的数据可能出现数据重复和丢失的错觉。 在执行查询时&#xff0c;MySQL会根据查询优化器的决策来确定数据的检索…

R语言进行孟德尔随机化+meta分析(2)----基于R和stata

目前不少文章用到了孟德尔随机化meta分析&#xff0c;在上一章咱们简单介绍了一下meta分析的基础知识。咱们今天来介绍一篇11分文章&#xff0c;由文章看看孟德尔随机化meta分析如何进行&#xff0c;文章的题目是&#xff1a;Appraising the causal role of smoking in multipl…

【PostgreSQL】【存储管理】表和元组的组织方式

外存管理负责处理数据库与外存介质(PostgreSQL8.4.1版本中只支持磁盘的管理操作)的交互过程。在PostgreSQL中&#xff0c;外存管理由SMGR(主要代码在smgr.c中)提供了对外存的统一接口。SMGR负责统管各种介质管理器&#xff0c;会根据上层的请求选择一个具体的介质管理器进行操作…

动态链接那些事

1、为什么要动态链接 1.1 空间浪费 对于静态链接来说&#xff0c;在程序运行之前&#xff0c;会将程序所需的所有模块编译、链接成一个可执行文件。这种情况下&#xff0c;如果 Program1 和 Program2 都需要用到 Lib.o 模块&#xff0c;那么&#xff0c;内存中和磁盘中实际上就…

Guava限流器原理浅析

文章目录 基本知识限流器的类图使用示例 原理解析限流整体流程问题驱动1、限流器创建的时候会初始化令牌吗&#xff1f;2、令牌是如何放到桶里的&#xff1f;3、如果要获取的令牌数大于桶里的令牌数会怎么样4、令牌数量的更新会有并发问题吗 总结 实际工作中难免有限流的场景。…

外卖订餐系统:数字时代的美食点餐新体验

在数字时代&#xff0c;外卖订餐系统已经成为现代生活的一部分。它不仅改变了我们点餐的方式&#xff0c;还为餐饮业带来了巨大的变革。本文将深入探讨外卖订餐系统的崭新世界&#xff0c;探讨它的发展历程、优势和未来趋势。 从电话点餐到外卖订餐系统 许多人还记得过去打电…

Linux环境下gdb调试方法与演示

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 演示环境&#xff1…

初识jdbc

java中的数据存储技术 在Java中&#xff0c;数据库存取技术可分为如下几类&#xff1a; JDBC直接访问数据库 JDO (Java Data Object )技术 第三方O/R工具&#xff0c;如Hibernate, Mybatis 等 JDBC是java访问数据库的基石&#xff0c;JDO、Hibernate、MyBatis等只是更好的封…

华为云云耀云服务器L实例评测使用 | 通过程序实现直播流自动分段录制

华为云云耀云服务器L实例评测使用 | 通过程序实现直播流自动分段录制 1. 准备工作2. 环境搭建3. 心得总结 1. 准备工作 随着云计算时代的进一步深入&#xff0c;越来越多的中小企业企业与开发者需要一款简单易用、高能高效的云计算基础设施产品来支撑自身业务运营和创新开发。基…

基本的五大排序算法

目录&#xff1a; 一&#xff0c;直接插入算法 二&#xff0c;希尔排序算法 三&#xff0c;选择排序 四&#xff0c;堆排序 五&#xff0c;冒泡排序算法 简介&#xff1a; 排序算法目前是我们最常用的算法之一&#xff0c;据研究表明&#xff0c;目前排序占用计算机CPU的时…

TouchGFX之后端通信

在大多数应用中&#xff0c;UI需以某种方式连接到系统的其余部分&#xff0c;并发送和接收数据。 它可能会与硬件外设&#xff08;传感器数据、模数转换和串行通信等&#xff09;或其他软件模块进行交互通讯。 Model类​ 所有TouchGFX应用都有Model类&#xff0c;Model类除了存…

小白自己​制作一个苹果.ios安卓.apk文件app应用手机下载的代码合并文件一码双端的落地页面详细教程

小白自己制作一个苹果.ios安卓.apk文件app应用手机下载的代码落地页面详细教程 图片取自这里哈 我们在这篇文章中教你如何制作一个手机下载引导落地页。这个落地页将可以自动识别访问者使用的是安卓还是苹果设备&#xff0c;并引导下载相应的应用程序。让我们按照以下步骤一…

Python中aiohttp和aiofiles模块的安装

Python中aiohttp和aiofiles模块的安装 前言 在进行asyncio多任务爬取的时候&#xff0c;配合着aiohttp和aiofiles的使用是必不可少的&#xff0c;那么我们现在就安装这两个模块到pycharm上 安装 将下面两行代码放入到pycharm上的终端就会开始下载 pip install aiohttp pip in…

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app,因为无法验证其完整性解决方案

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app&#xff0c;因为无法验证其完整性解决方案 首先&#xff0c;确保您从可信任的来源下载并安装企业开发者签名过的应用程序。如果您不确定应用程序的来源&#xff0c;建议您联系应用程序提供者…

宠物医院必备,介绍一款宠物疫苗接种管理软件

在当今社会&#xff0c;养宠物已经成为越来越多人的生活方式&#xff0c;宠物疫苗接种已是宠物医院的重要工作&#xff0c;但是目前绝大多数的宠物医院对疫苗接种的管理&#xff0c;还是采取人工登记方式&#xff0c;不仅效率低下&#xff0c;而且无法做到疫苗接种到期自动提醒…