
一共有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 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语句比较复杂,我们就不采用注解的方式,使用配置文件的方式来写
update blog title=#{title}, content=#{content}, delete_flag=#{deleteFlag}, id = #{id} 
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 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 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 getList() {
        // 获取博客列表
        List blogs = blogService.selectAllBlog();
        if (blogs == null) {
            return null;
        }
        return blogs;
    }
}
   
使用postman测试成功,服务器正确返回数据

修改 blog_list.html, 删除之前写死的博客内容(即 ), 并新增js 代码处理 ajax 请求.
    
    
 
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 selectAllBlog() {
        List 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测试成功,服务器正确返回数据

    
        
 

前后端分离的项⽬中, 虽然主要使⽤ 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("登陆成功");
    }
}
 

    
    
 
    
    
 

剩下的功能在下篇博客实现~