赞
踩
一共有6个网页,分别是博客列表页面,博客详情页面,发布博客页面,博客登陆页面,博客更新页面,修改个人信息页面(暂未实现),我们要实现的功能有,实现博客列表的展示页面,博客详情页面的展示功能,用户登录功能,显示用户信息功能,编辑博客功能,发布博客功能,删除博客功能,退出登录功能
我们现在就开始写吧
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: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration: # 配置打印 MyBatis 执行的 SQL
log-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: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis: #上线就不用打印mybatis执行日志
configuration:
map-underscore-to-camel-case: true #自动驼峰转换
这个项目的数据库表比较简单
只有两个表
-- 建表SQL create database if not exists `java_blog_spring1` charset utf8mb4; -- 用户表 drop table if exists `java_blog_spring`.`user`; CREATE TABLE `java_blog_spring`.`user` ( `id` INT NOT NULL AUTO_INCREMENT, `user_name` VARCHAR(128) NOT NULL, `password` VARCHAR(128) NOT NULL, `photo` VARCHAR(128) NOT NULL, `github_url` VARCHAR(128) NULL, `delete_flag` TINYINT(4) NULL DEFAULT 0, `create_time` TIMESTAMP NULL DEFAULT current_timestamp(), PRIMARY KEY (`id`), UNIQUE INDEX `user_name_UNIQUE` (`user_name` ASC)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8mb4 COMMENT = '用户表'; -- 博客表 drop table if exists `java_blog_spring`.`blog`; CREATE TABLE `java_blog_spring`.`blog` ( `id` INT NOT NULL AUTO_INCREMENT, `title` VARCHAR(200) NULL, `content` TEXT NULL, `user_id` INT(11) NULL, `delete_flag` TINYINT(4) NULL DEFAULT 0, `create_time` TIMESTAMP NULL DEFAULT current_timestamp(), PRIMARY KEY (`id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表'; -- 新增用户信息 insert into `java_blog_spring`.`user` (`user_name`, `password`,`photo`,`github_url`)values ("zhangsan","123456","pic/doge.jpg","https://gitee.com/bubble-fish666/class-java45"); insert into `java_blog_spring`.`user` (`user_name`, `password`,`photo`,`github_url`)values ("lisi","123456","pic/doge.jpg","https://gitee.com/bubble-fish666/class-java45"); insert into `java_blog_spring`.`blog` (`title`,`content`,`user_id`) values ("第一篇博客","111我是博客正文我是博客正文我是博客正文",1); insert into `java_blog_spring`.`blog` (`title`,`content`,`user_id`) values ("第一篇博客","222我是博客正文我是博客正⽂我是博客正文",2); use java_blog_spring; -- 查询两个表的数据 select * from user; select * from blog;
我们在配置文件中配置了数据库表字段到类属性的自动驼峰转换,所以可以不用进行重命名
configuration:
map-underscore-to-camel-case: true
@Data
public class User {
// java中属性使用小驼峰命名
// 我们配置了自动驼峰转换
private Integer id;
private String userName;
private String passWord;
private String photo;
private String githubUrl;
private Byte deleteFlag;
private Date createTime;
}
@Data
@Data
public class Blog {
private Integer id;
private String title;
private String content;
private Integer userId;
private Integer deleteFlag;
private Date createTime;
}
@Mapper public interface UserMapper { /** * 根据用户id查询用户信息 * @param id * @return */ @Select("select user_name, password, photo, github_url from user where delete_flag = 0 and id = #{id}") User selectById(Integer id); /** * 根据用户名称查询用户 * @param userName * @return */ @Select(("select user_name, password, photo, github_url from user where delete_flag = 0 and user_name = #{userName}")) User selectByName(String userName); }
@Mapper public interface BlogMapper { /** * 查询所有未删除的博客.按照时间降序排列 * @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}") Blog selectByBlogId(Integer blogId); /** * 插入一条博客 * @param blog * @return */ @Insert("insert into blog (title, content, user_id) values (#{title, #{content}, #{userId})") Integer insertBlog(Blog blog); /** * 根据博客id更新博客 * 删除博客就是把delete_id改为1 * @return */ Integer updateBlog(Blog blog); /** * 根据用户id查询博客数量 * @param userId * @return */ @Select("select count(id) from blog where delete_flag = 0 and user_id = #{userId}") Integer selectBlogCount(Integer userId); }
因为更新博客内容的sql语句比较复杂,我们就不采用注解的方式,使用配置文件的方式来写
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.springblog.mapper.BlogMapper"> <update id="updateBlog"> update blog <set> <if test="title!=null">title=#{title},</if> <if test="content!=null">content=#{content},</if> <if test="deleteFlag!=null">delete_flag=#{deleteFlag},</if> </set> <where> id = #{id} </where> </update> </mapper>
service层被称作业务层,它用来处理逻辑上的业务,而不去考虑具体的实现,这样controller层就不会直接去调用mapper层,可以将代码解耦,便于扩展
@Service public class UserService { @Autowired // 将UserMapper对象注入进来 private UserMapper userMapper; /** * 根据用户id查询用户信息 * @param id * @return */ public User selectById(Integer id) { return userMapper.selectById(id); } /** * 根据用户名称查询用户 * @param userName * @return */ public User selectByName(String userName) { return userMapper.selectByName(userName); } }
@Service public class BlogService { @Autowired private BlogMapper blogMapper; /** * 查询所有未删除的博客.按照时间降序排列 * @return */ public List<Blog> selectAllBlog() { return blogMapper.selectAllBlog(); } /** * 根据博客id查询博客详情 * @param blogId * @return */ public Blog selectByBlogId(Integer blogId) { return blogMapper.selectByBlogId(blogId); } /** * 插入一条博客 * @param blog * @return */ public Integer insertBlog(Blog blog) { return blogMapper.insertBlog(blog); } /** * 根据博客id更新博客 * 删除博客就是把delete_id改为1 * @return */ public Integer updateBlog(Blog blog) { return blogMapper.updateBlog(blog); } /** * 根据用户id查询博客数量 * @param userId * @return */ public Integer selectBlogCount(Integer userId) { return blogMapper.selectBlogCount(userId); } }
在mapper接口点击Fn+Alt+Insert(按钮因电脑而异,不行可以试下Alt+Insert)
然后在弹出框中点击Test
然后勾选需要测试的方法
此时就可以看到test包下出现了对应的类
然后我们就可以在这里写测试方法
@SpringBootTest class BlogMapperTest { @Autowired private BlogService blogService; @Test void selectAllBlog() { List<Blog> blogs = blogService.selectAllBlog(); System.out.println(blogs.toString()); } @Test void selectByBlogId() { System.out.println(blogService.selectByBlogId(2).toString()); } @Test void insertBlog() { Blog blog = new Blog(); blog.setTitle("测试"); blog.setContent("测试正文"); blog.setUserId(1); System.out.println(blogService.insertBlog(blog)); } @Test void updateBlog() { Blog blog = new Blog(); blog.setTitle("测试更新"); blog.setId(1); System.out.println(blogService.updateBlog(blog)); } @Test void deleteBlog() { Blog blog = new Blog(); blog.setDeleteFlag(1); blog.setId(1); System.out.println(blogService.updateBlog(blog)); } @Test void selectBlogCount() { System.out.println(blogService.selectBlogCount(2)); } }
@SpringBootTest class UserMapperTest { @Autowired private UserService userService; @Test void selectById() { System.out.println(userService.selectById(1).toString()); } @Test void selectByName() { System.out.println(userService.selectByName("zhangsan").toString()); } }
把之前写好的博客系统静态页面拷贝到static⽬录下
⼯具层(common) => 统⼀返回类, 统⼀异常处理类
@Data public class Result { private Integer code; private String msg; private Object data; /** * 业务执行成功返回的数据 * @return */ public static Result success(String msg, Object data) { Result result = new Result(); result.setCode(200); result.setMsg(msg); result.setData(data); return result; } /** * 业务执行成功返回的数据 * @return */ public static Result success(Object data) { Result result = new Result(); result.setCode(200); result.setMsg("执行成功"); result.setData(data); return result; } /** * 业务执行失败返回的数据 * @return */ public static Result fail(Integer code, String msg, Object data) { Result result = new Result(); result.setCode(code); result.setMsg(msg); result.setData(data); return result; } /** * 业务执行失败返回的数据 * @return */ public static Result fail(Integer code, String msg) { Result result = new Result(); result.setCode(code); result.setMsg(msg); return result; } }
使用code = -1表示出现异常
@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
@ExceptionHandler
public Result error (Exception e) {
return Result.fail(-1, e.getMessage());
}
}
在数据返回之前调用此方法,将返回数据格式统一
如果是String类型会报错,所以我们要处理一下,异常使用@SneakyThrows注解
如果返回的数据格式,已经是Result类型,就不需要处理,直接返回即可
@ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { /** * 内容是否需要重写 * 返回true表示需要重写 * @param returnType * @param converterType * @return */ @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } /** * 方法返回之前调用此方法 */ // @SneakyThrows @Override public Object beforeBodyWrite(Object body, // 相应的正文内容 MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { //如果返回的数据格式,已经是Result类型,就不需要处理,直接返回即可 if (body instanceof Result) { return body; } // 如果是String类型会报错,所以我们要处理一下 if (body instanceof String) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writeValueAsString(body); } return Result.success(body); } }
【请求】
【响应】
浏览器给服务器发送一个blog/getList这样的Http请求,服务器返回给浏览器一个json格式的数据
@RestController @RequestMapping("/blog") public class BlogController { @Autowired private BlogService blogService; @RequestMapping("/getList") public List<Blog> getList() { // 获取博客列表 List<Blog> blogs = blogService.selectAllBlog(); if (blogs == null) { return null; } return blogs; } }
使用postman测试成功,服务器正确返回数据
修改 blog_list.html, 删除之前写死的博客内容(即 <div class=“blog”> ), 并新增js 代码处理 ajax 请求.
<script src="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) { //循环拼接数据到document var 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); } else if (result.code == -1) { alert(result.mag); } }, error: function () { console.log("后端返回失败"); } }); </script>
SimpleDateFormat 格式化
创建一个DateUtil工具类
public class DateUtil {
public static String format(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
return simpleDateFormat.format(date);
}
}
@Data
public class Blog {
private Integer id;
private String title;
private String content;
private Integer userId;
private Integer deleteFlag;
private Date createTime;
public String getCreateTime() {
return DateUtil.format(createTime);
}
}
博客列表页面应该显示的是正文的摘要,并非全部显示出来,在博客的详情页面才需要全部显示出来
修改Blog Service中的方法
/**
* 查询所有未删除的博客.按照时间降序排列
* @return
*/
public List<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;
}
点击查看全文能进入当前博客详情页面,根据博客id动态的获取博客详情
【请求】
【响应】
浏览器给服务器发送一个blog.getDetails的Http请求,服务器返回给浏览器一个json格式的数据
@RequestMapping("/blog/getBlogDetails")
public Result getDetails(Integer blogId) {
// 判合法
if (blogId == null || blogId <= 0) {
return Result.fail(-1,"博客不存在");
}
Blog blog = blogService.selectByBlogId(blogId);
if (blog == null) {
return Result.fail(-1,"博客不存在");
}
return Result.success(blog);
}
使用postman测试成功,服务器正确返回数据
<!-- 引⼊ editor.md 的依赖 -->
<link rel="stylesheet" href="blog-editormd/css/editormd.css" />
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js">
</script>
<script src="blog-editormd/lib/marked.min.js"></script>
<script src="blog-editormd/lib/prettify.min.js"></script>
<script src="blog-editormd/editormd.js"></script>
<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>
前后端分离的项⽬中, 虽然主要使⽤ ajax 进⾏前后端交互, 但是也不是完全不能⽤ form
【请求】
【响应】
创建 UserController
@RequestMapping("/user") @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/login") public Result login(String username, String password) { // 判空 if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) { return Result.fail(-1,"用户名或密码不能为空"); } // 判断用户名密码是否匹配 User user = userService.selectByName(username); if (user == null || !user.getPassWord().equals(password)) { return Result.fail(-2,"用户名或密码错误"); } return Result.success("登陆成功"); } }
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script> <script> function login() { $.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"); } else if(result.code == -1){ alert("⽤户名或密码不能为空"); return; } else if(result.code == -2){ alert("⽤户名或密码错误"); return; } }, error : function (error) { console.log(error.msg); } }) } </script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script> <script> function login() { $.ajax({ type: "post", url: "/user/login", data: { "username": $("#username").val(), "password": $("#password").val() }, success: function (result) { if(result.code == -1){ alert(result.msg); } else if(result.code == -2){ alert(result.msg); } else if (result.code == 200 && result.data != null) { location.assign("blog_list.html"); } }, error : function (error) { console.log(error.msg); } }) } </script>
剩下的功能在下篇博客实现~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。