1.博客系统简要分析
一共有6个网页,分别是博客列表页面,博客详情页面,发布博客页面,博客登陆页面,博客更新页面,修改个人信息页面(暂未实现),我们要实现的功能有,实现博客列表的展示页面,博客详情页面的展示功能,用户登录功能,显示用户信息功能,编辑博客功能,发布博客功能,删除博客功能,退出登录功能
我们现在就开始写吧
2.创建springBoot项目
1.创建项目,勾选需要的依赖
2.删除无用的目录及文件
4.创建框架
3.配置文件(选择yml)
application.xml
spring:profiles:active: dev
logging:file:path: logs/
level:root: info
application-dev.xml
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8
username: root
password:111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:# 配置打印 MyBatis 执行的 SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case:true#自动驼峰转换mapper-locations: classpath:mapper/***Mapper.xml
application-prod.xml
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8
username: root
password:111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:#上线就不用打印mybatis执行日志configuration:map-underscore-to-camel-case:true#自动驼峰转换
3.数据库准备
这个项目的数据库表比较简单
只有两个表
1.设计表结构
2.建表sql
-- 建表SQLcreatedatabaseifnotexists`java_blog_spring1`charset utf8mb4;-- 用户表droptableifexists`java_blog_spring`.`user`;CREATETABLE`java_blog_spring`.`user`(`id`INTNOTNULLAUTO_INCREMENT,`user_name`VARCHAR(128)NOTNULL,`password`VARCHAR(128)NOTNULL,`photo`VARCHAR(128)NOTNULL,`github_url`VARCHAR(128)NULL,`delete_flag`TINYINT(4)NULLDEFAULT0,`create_time`TIMESTAMPNULLDEFAULTcurrent_timestamp(),PRIMARYKEY(`id`),UNIQUEINDEX`user_name_UNIQUE`(`user_name`ASC))ENGINE=InnoDBDEFAULTCHARACTERSET= utf8mb4 COMMENT='用户表';-- 博客表droptableifexists`java_blog_spring`.`blog`;CREATETABLE`java_blog_spring`.`blog`(`id`INTNOTNULLAUTO_INCREMENT,`title`VARCHAR(200)NULL,`content`TEXTNULL,`user_id`INT(11)NULL,`delete_flag`TINYINT(4)NULLDEFAULT0,`create_time`TIMESTAMPNULLDEFAULTcurrent_timestamp(),PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET= utf8mb4 COMMENT='博客表';-- 新增用户信息insertinto`java_blog_spring`.`user`(`user_name`,`password`,`photo`,`github_url`)values("zhangsan","123456","pic/doge.jpg","https://gitee.com/bubble-fish666/class-java45");insertinto`java_blog_spring`.`user`(`user_name`,`password`,`photo`,`github_url`)values("lisi","123456","pic/doge.jpg","https://gitee.com/bubble-fish666/class-java45");insertinto`java_blog_spring`.`blog`(`title`,`content`,`user_id`)values("第一篇博客","111我是博客正文我是博客正文我是博客正文",1);insertinto`java_blog_spring`.`blog`(`title`,`content`,`user_id`)values("第一篇博客","222我是博客正文我是博客正⽂我是博客正文",2);use java_blog_spring;-- 查询两个表的数据select*fromuser;select*from blog;
4.接口设计
- 获取所有的博客列表
5.数据库相关查询操作
- 根据用户id查询用户信息
- 根据用户名称查询用户
- 查询所有未删除的博客(按照时间降序排列)
- 根据博客id查询博客详情
- 插入一条博客
- 根据博客id更新博客
- 删除博客
- 根据用户id查询博客数量
6.写代码准备工作
1.model包下,创建Java实体类
我们在配置文件中配置了数据库表字段到类属性的自动驼峰转换,所以可以不用进行重命名
自动驼峰映射
configuration:
map-underscore-to-camel-case:true
1.User类
@DatapublicclassUser{// java中属性使用小驼峰命名// 我们配置了自动驼峰转换privateInteger id;privateString userName;privateString passWord;privateString photo;privateString githubUrl;privateByte deleteFlag;privateDate createTime;}
2.Blog类
@Data@DatapublicclassBlog{privateInteger id;privateString title;privateString content;privateInteger userId;privateInteger deleteFlag;privateDate createTime;}
2.mapper层
- 简单的sql语句我们使用注解实现
- 复杂的sql语句使用xml配置文件实现
1.userMapper接口
@MapperpublicinterfaceUserMapper{/**
* 根据用户id查询用户信息
* @param id
* @return
*/@Select("select user_name, password, photo, github_url from user where delete_flag = 0 and id = #{id}")UserselectById(Integer id);/**
* 根据用户名称查询用户
* @param userName
* @return
*/@Select(("select user_name, password, photo, github_url from user where delete_flag = 0 and user_name = #{userName}"))UserselectByName(String userName);}
2.BlogMapper接口
@MapperpublicinterfaceBlogMapper{/**
* 查询所有未删除的博客.按照时间降序排列
* @return
*/@Select("select id, title, content, user_id, create_time from blog where delete_flag = 0 order by create_time;")List<Blog>selectAllBlog();/**
* 根据博客id查询博客详情
* @param blogId
* @return
*/@Select("select id, title, content, user_id, create_time from blog where delete_flag = 0 and id = #{blogId}")BlogselectByBlogId(Integer blogId);/**
* 插入一条博客
* @param blog
* @return
*/@Insert("insert into blog (title, content, user_id) values (#{title, #{content}, #{userId})")IntegerinsertBlog(Blog blog);/**
* 根据博客id更新博客
* 删除博客就是把delete_id改为1
* @return
*/IntegerupdateBlog(Blog blog);/**
* 根据用户id查询博客数量
* @param userId
* @return
*/@Select("select count(id) from blog where delete_flag = 0 and user_id = #{userId}")IntegerselectBlogCount(Integer userId);}
因为更新博客内容的sql语句比较复杂,我们就不采用注解的方式,使用配置文件的方式来写
3.BlogMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.springblog.mapper.BlogMapper"><updateid="updateBlog">
update blog
<set><iftest="title!=null">title=#{title},</if><iftest="content!=null">content=#{content},</if><iftest="deleteFlag!=null">delete_flag=#{deleteFlag},</if></set><where>
id = #{id}
</where></update></mapper>
3.Service包下
service层被称作业务层,它用来处理逻辑上的业务,而不去考虑具体的实现,这样controller层就不会直接去调用mapper层,可以将代码解耦,便于扩展
1.UserService类
@ServicepublicclassUserService{@Autowired// 将UserMapper对象注入进来privateUserMapper userMapper;/**
* 根据用户id查询用户信息
* @param id
* @return
*/publicUserselectById(Integer id){return userMapper.selectById(id);}/**
* 根据用户名称查询用户
* @param userName
* @return
*/publicUserselectByName(String userName){return userMapper.selectByName(userName);}}
2.BlogService类
@ServicepublicclassBlogService{@AutowiredprivateBlogMapper blogMapper;/**
* 查询所有未删除的博客.按照时间降序排列
* @return
*/publicList<Blog>selectAllBlog(){return blogMapper.selectAllBlog();}/**
* 根据博客id查询博客详情
* @param blogId
* @return
*/publicBlogselectByBlogId(Integer blogId){return blogMapper.selectByBlogId(blogId);}/**
* 插入一条博客
* @param blog
* @return
*/publicIntegerinsertBlog(Blog blog){return blogMapper.insertBlog(blog);}/**
* 根据博客id更新博客
* 删除博客就是把delete_id改为1
* @return
*/publicIntegerupdateBlog(Blog blog){return blogMapper.updateBlog(blog);}/**
* 根据用户id查询博客数量
* @param userId
* @return
*/publicIntegerselectBlogCount(Integer userId){return blogMapper.selectBlogCount(userId);}}
4.测试
使用BlogMapper做演示,UserMapper同理
在mapper接口点击Fn+Alt+Insert(按钮因电脑而异,不行可以试下Alt+Insert)
然后在弹出框中点击Test
然后勾选需要测试的方法
此时就可以看到test包下出现了对应的类
然后我们就可以在这里写测试方法
1.BlogMapperTest测试类
@SpringBootTestclassBlogMapperTest{@AutowiredprivateBlogService blogService;@TestvoidselectAllBlog(){List<Blog> blogs = blogService.selectAllBlog();System.out.println(blogs.toString());}@TestvoidselectByBlogId(){System.out.println(blogService.selectByBlogId(2).toString());}@TestvoidinsertBlog(){Blog blog =newBlog();
blog.setTitle("测试");
blog.setContent("测试正文");
blog.setUserId(1);System.out.println(blogService.insertBlog(blog));}@TestvoidupdateBlog(){Blog blog =newBlog();
blog.setTitle("测试更新");
blog.setId(1);System.out.println(blogService.updateBlog(blog));}@TestvoiddeleteBlog(){Blog blog =newBlog();
blog.setDeleteFlag(1);
blog.setId(1);System.out.println(blogService.updateBlog(blog));}@TestvoidselectBlogCount(){System.out.println(blogService.selectBlogCount(2));}}
2.UserMapperTest测试类
@SpringBootTestclassUserMapperTest{@AutowiredprivateUserService userService;@TestvoidselectById(){System.out.println(userService.selectById(1).toString());}@TestvoidselectByName(){System.out.println(userService.selectByName("zhangsan").toString());}}
3.测试结果
5.添加前端界面
把之前写好的博客系统静态页面拷贝到static⽬录下
6.添加公共模块
⼯具层(common) => 统⼀返回类, 统⼀异常处理类
1.添加统一返回类Result
@DatapublicclassResult{privateInteger code;privateString msg;privateObject data;/**
* 业务执行成功返回的数据
* @return
*/publicstaticResultsuccess(String msg,Object data){Result result =newResult();
result.setCode(200);
result.setMsg(msg);
result.setData(data);return result;}/**
* 业务执行成功返回的数据
* @return
*/publicstaticResultsuccess(Object data){Result result =newResult();
result.setCode(200);
result.setMsg("执行成功");
result.setData(data);return result;}/**
* 业务执行失败返回的数据
* @return
*/publicstaticResultfail(Integer code,String msg,Object data){Result result =newResult();
result.setCode(code);
result.setMsg(msg);
result.setData(data);return result;}/**
* 业务执行失败返回的数据
* @return
*/publicstaticResultfail(Integer code,String msg){Result result =newResult();
result.setCode(code);
result.setMsg(msg);return result;}}
2.添加统一异常处理类
使用code = -1表示出现异常
@ControllerAdvice@ResponseBodypublicclassErrorAdvice{@ExceptionHandlerpublicResult error (Exception e){returnResult.fail(-1, e.getMessage());}}
3.添加统一返回格式
在数据返回之前调用此方法,将返回数据格式统一
如果是String类型会报错,所以我们要处理一下,异常使用@SneakyThrows注解
如果返回的数据格式,已经是Result类型,就不需要处理,直接返回即可
@ControllerAdvicepublicclassResponseAdviceimplementsResponseBodyAdvice{/**
* 内容是否需要重写
* 返回true表示需要重写
* @param returnType
* @param converterType
* @return
*/@Overridepublicbooleansupports(MethodParameter returnType,Class converterType){returntrue;}/**
* 方法返回之前调用此方法
*///@SneakyThrows@OverridepublicObjectbeforeBodyWrite(Object body,// 相应的正文内容MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response){//如果返回的数据格式,已经是Result类型,就不需要处理,直接返回即可if(body instanceofResult){return body;}// 如果是String类型会报错,所以我们要处理一下if(body instanceofString){ObjectMapper objectMapper =newObjectMapper();
objectMapper.writeValueAsString(body);}returnResult.success(body);}}
7.实现博客列表显示
1.约定前后端交互的接口
【请求】
- blog/getList
【响应】
- [200]
- [200]返回数据成功,显示博客列表
- [-1]目前没有博客
- [401]没有权限访问
- [error]访问出现错误,打印异常信息
浏览器给服务器发送一个blog/getList这样的Http请求,服务器返回给浏览器一个json格式的数据
2.实现服务器代码
@RestController@RequestMapping("/blog")publicclassBlogController{@AutowiredprivateBlogService blogService;@RequestMapping("/getList")publicList<Blog>getList(){// 获取博客列表List<Blog> blogs = blogService.selectAllBlog();if(blogs ==null){returnnull;}return blogs;}}
使用postman测试成功,服务器正确返回数据
3.实现客户端代码
修改 blog_list.html, 删除之前写死的博客内容(即 SimpleDateFormat 格式化 博客列表页面应该显示的是正文的摘要,并非全部显示出来,在博客的详情页面才需要全部显示出来 点击查看全文能进入当前博客详情页面,根据博客id动态的获取博客详情 【请求】 【响应】 浏览器给服务器发送一个blog.getDetails的Http请求,服务器返回给浏览器一个json格式的数据 使用postman测试成功,服务器正确返回数据 【请求】 【响应】 创建 UserController
<scriptsrc="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>
$.ajax({type:"get",url:"/blog/getList",success:function(result){if(result.code ==200&& result.data !=null&& result.data.length >0){//循环拼接数据到documentvar finalHtml ="";for(var blog of result.data){
finalHtml +='<div class="blog">';
finalHtml +='<div class="title">'+ blog.title +'</div>';
finalHtml +='<div class="date">'+ blog.createTime +'</div>';
finalHtml +='<div class="desc">'+ blog.content +'</div>'
finalHtml +='<a class="detail" href="blog_detail.html?blogId = '+blog.id+'">查看全⽂>></a>'
finalHtml +='</div>';}$(".right").html(finalHtml);}elseif(result.code ==-1){alert(result.mag);}},error:function(){
console.log("后端返回失败");}});</script>
4.处理日期显示问题
创建一个DateUtil工具类publicclassDateUtil{publicstaticStringformat(Date date){SimpleDateFormat simpleDateFormat =newSimpleDateFormat("yy-MM-dd HH:mm:ss");return simpleDateFormat.format(date);}}
重新获取博客创建时间
@DatapublicclassBlog{privateInteger id;privateString title;privateString content;privateInteger userId;privateInteger deleteFlag;privateDate createTime;publicStringgetCreateTime(){returnDateUtil.format(createTime);}}
5.处理裁剪摘要问题
修改Blog Service中的方法/**
* 查询所有未删除的博客.按照时间降序排列
* @return
*/publicList<Blog>selectAllBlog(){List<Blog> blogs = blogMapper.selectAllBlog();// 遍历如果博客的正文长度超过100,就裁剪for(Blog blog : blogs){if(blog.getContent().length()>100){
blog.setContent(blog.getContent().substring(0,100)+"...");}}return blogs;}
4.博客列表界面显示成功
8.实现博客详情
1.约定前后端交互接口
2.实现服务器代码
@RequestMapping("/blog/getBlogDetails")publicResultgetDetails(Integer blogId){// 判合法if(blogId ==null|| blogId <=0){returnResult.fail(-1,"博客不存在");}Blog blog = blogService.selectByBlogId(blogId);if(blog ==null){returnResult.fail(-1,"博客不存在");}returnResult.success(blog);}
3.实现客户端代码
1. 引⼊ editor.md
<!-- 引⼊ editor.md 的依赖 --><linkrel="stylesheet"href="blog-editormd/css/editormd.css"/><scriptsrc="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><scriptsrc="blog-editormd/lib/marked.min.js"></script><scriptsrc="blog-editormd/lib/prettify.min.js"></script><scriptsrc="blog-editormd/editormd.js"></script>
2.新增 js 代码, 从服务器获取博客详情数据
<script>
$.ajax({type:"get",url:"/blog/getBlogDetails"+ location.search,success:function(result){
console.log(result);if(result.code ==200&& result.data !=null){$(".title").text(result.data.title);$(".date").text(result.data.createTime);
editormd.markdownToHTML("content",{markdown: result.data.content,});}else{alert(result.msg);}},error:function(){
console.log('访问出错');}});</script>
4.博客列表界面显示成功
9.实现登陆
前后端分离的项⽬中, 虽然主要使⽤ ajax 进⾏前后端交互, 但是也不是完全不能⽤ form
1.约定前后端交互接口
2.实现服务器代码
@RequestMapping("/user")@RestControllerpublicclassUserController{@AutowiredprivateUserService userService;@RequestMapping("/login")publicResultlogin(String username,String password){// 判空if(!StringUtils.hasLength(username)||!StringUtils.hasLength(password)){returnResult.fail(-1,"用户名或密码不能为空");}// 判断用户名密码是否匹配User user = userService.selectByName(username);if(user ==null||!user.getPassWord().equals(password)){returnResult.fail(-2,"用户名或密码错误");}returnResult.success("登陆成功");}}
使用postman测试登录成功
3.实现客户端代码
<scriptsrc="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>functionlogin(){
$.ajax({type:"post",url:"/user/login",data:{"username":$("#username").val(),"password":$("#password").val()},success:function(result){if(result.code ==200&& result.data ==1){
location.assign("blog_list.html");}elseif(result.code ==-1){alert("⽤户名或密码不能为空");return;}elseif(result.code ==-2){alert("⽤户名或密码错误");return;}},error:function(error){
console.log(error.msg);}})}</script>
3.实现客户端代码
<scriptsrc="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>functionlogin(){
$.ajax({type:"post",url:"/user/login",data:{"username":$("#username").val(),"password":$("#password").val()},success:function(result){if(result.code ==-1){alert(result.msg);}elseif(result.code ==-2){alert(result.msg);}elseif(result.code ==200&& result.data !=null){
location.assign("blog_list.html");}},error:function(error){
console.log(error.msg);}})}</script>
4.登陆功能实现成功
剩下的功能在下篇博客实现~
版权归原作者 小锦鲤yaw 所有, 如有侵权,请联系我们删除。