诗林 发表于 2024-11-19 07:16:06

24.<Spring博客体系①(数据库+公共代码+持久层+显示博客列表+博客详情)

项目整体预览

   登录页面
https://i-blog.csdnimg.cn/direct/92c7938ad41f4e2293deb1e8b6c728a1.png
    主页
 https://i-blog.csdnimg.cn/direct/286d9d94a5d943b293dddb2d204a9605.png
   查看全文 
https://i-blog.csdnimg.cn/direct/411284ba4d3a434686f58aea24290d30.png
    编辑
https://i-blog.csdnimg.cn/direct/a204cf193d36469389cfba46d7444942.png
   写博客 
https://i-blog.csdnimg.cn/direct/5a2d36ce5b1c4e65af5dcb9ab8df5f58.png
PS:Service.impl(现在流行写法)

   https://i-blog.csdnimg.cn/direct/a39602229edc413fade4ef4d1e4e7b53.png
推荐写法。后续完成项目。会实验这样写。
接口可以有多个实现。每个实现都可以不同。
这也算一种设计模式。叫做(策略模式)。
我们博客项目较为简朴。Service层我们还是只写一层。如果后续有更复杂的项目。我们就会利用多层。

而。
Mapper.impl
Mapper里放的接口。
impl里面放的是接口的实现。(早期写法)
    1.分析需求

2.技能方案设计
①画UML图、流程图、ER图

②数据库的设计、
数据库的表通常分两类。实体表、关系表。
实体表:可以以为就是一个对象。一个对象包含什么属性。好比人员表、员工表、部门表、图书表、博客表、这些都是实体表
关系表:表示实体之间的关系,好比员工属于什么部门。用户表、权限表、用户和权限相关关系。实体之间都是有关系的。对于简朴的关系。该表可以省略。可以放在实体表之中。
好比一个用户可以有多个权限。


③接口设计、、

3.开辟
4.测试
5.测试(QA)
6.联调
7.验收
8.上线

   数据库的设计
用户表:用户名、暗码、照片、昵称、github地点
博客表:标题、日期、内容、作者、分类。
这两者有很多关系。那么我们可以单独建立一个表

接口的设计
后端人员是主动地
登录接口
一、创建项目

   创建细节就不一一演示了。这是是我们利用的IDEA中重要的依赖的版本。
    <properties>
      <java.version>1.8</java.version>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <spring-boot.version>2.6.13</spring-boot.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.2.2</version>
      </dependency> 二、设置文件处理

   将
https://i-blog.csdnimg.cn/direct/c880777ad15a45428b06a8b2e295ac61.png
修改为
https://i-blog.csdnimg.cn/direct/8080ae64cbb44cec99718ccded00ccaf.png

三、建立数据库

https://i-blog.csdnimg.cn/direct/1e8d4f4849b3495fa45c3d7cac7ce048.png
--创建数据库
create database if not exists spring_blog charset utf8mb4;
-- ⽤⼾表
DROP TABLE IF EXISTS spring_blog.user;
CREATE TABLE spring_blog.user(
    `id` INT NOT NULL AUTO_INCREMENT,
    `user_name` VARCHAR ( 128 ) NOT NULL,
    `password` VARCHAR ( 128 ) NOT NULL,
    `github_url` VARCHAR ( 128 ) NULL,
    `delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
    `create_time` DATETIME DEFAULT now(),
    `update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id ),
UNIQUE INDEX user_name_UNIQUE ( user_name ASC )) ENGINE = INNODB DEFAULT
CHARACTER SET= utf8mb4 COMMENT = '⽤⼾表';
-- 博客表
drop table if exists spring_blog.blog;
CREATE TABLE spring_blog.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` DATETIME DEFAULT now(),
    `update_time` DATETIME DEFAULT now(),
    PRIMARY KEY (id))
    ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';

-- 新增⽤⼾信息
insert into spring_blog.user (user_name, password,github_url)values('zhangsan','123456','https://gitee.com/Bwindmill');
insert into spring_blog.user (user_name, password,github_url)values('lisi','123456','https://gitee.com/Bwindmill');
insert into spring_blog.user (user_name, password,github_url)values('wangwu','123456','https://gitee.com/Bwindmill');
insert into spring_blog.user (user_name, password,github_url)values('luliu','123456','https://gitee.com/Bwindmill');

insert into spring_blog.blog (title,content,user_id) values('第⼀篇博客','1111111',1);
insert into spring_blog.blog (title,content,user_id) values('第二篇博客','2222222',2);

四、测试 

    之后运行一下程序,看程序有没有问题。
并且写一个简朴的程序测测运行正常不正常。
eg:
@RestController
public class UserController {
    @RequestMapping("/test")
    public String test(){
      return "Hello";
    }
}https://i-blog.csdnimg.cn/direct/15706e0f129f46da84704c28a5708fcb.png
对于新手来说。我们就写一点测一点。逐步来。
将前端代码cv到staric目录中。我们再测一测前端代码能不能正常访问。
https://i-blog.csdnimg.cn/direct/d1cc94434574421689e8da7a4d7ce020.png

五、誊写项目公共模块代码

项目架构图

https://i-blog.csdnimg.cn/direct/cdbc1f14b7dc4c588cf4b0aa0c2a1f41.png
5.1实体类

   UserInfo类
@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String password;
    private String githubUrl;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}   BlogInfo类
@Data
public class BlogInfo {
    private Integer id;
    private String title;
    private String content;
    private Integer userId;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
} 5.2同一结果返回

   Result类
/**
* 统一返回结果
* 我们先设定返回的结果
* 为了让其他地方方便调用。我们统一给方法加上static
*/
@Data
public class Result {
    private int code; //200成功, -1失败-2未登录
    private String errMsg;
    private Object data;

    public static Result success(Object data){
      Result result = new Result();
      result.setCode(Constant.SUCCESS_CODE);
      result.setErrMsg("");
      result.setData(data);
      return result;
    }

    public static Result fail(String errMsg){
      Result result = new Result();
      result.setCode(Constant.FAIL_CODE);
      result.setErrMsg(errMsg);
      result.setData(null);
      return result;
    }

    public static Result fail(String errMsg,Object data){
      Result result = new Result();
      result.setCode(Constant.FAIL_CODE);
      result.setErrMsg(errMsg);
      result.setData(data);
      return result;
    }

    public static Result unLogin(String errMsg){
      Result result = new Result();
      result.setCode(Constant.UNLOGIN_CODE);
      result.setErrMsg("用户未登录");
      result.setData(null);
      return result;
    }

}   ResponseAdvice类
@ControllerAdvice //注意加上这个注解才有效。
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
      /**
         * 设定哪些方法统一返回结果
         * 哪个接口执行统一结果返回
         */
      return true;
    }

    @SneakyThrows
    //这个注解帮我们对
    // return objectMapper.writeValueAsString(Result.success(body));
    //进行try catch。异常处理
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
      //统一结果返回的具体逻辑

      if(body instanceof Result){
            return body;
      }

      //对String 类型单独处理.否则会报错
      if (body instanceof String){
            return objectMapper.writeValueAsString(Result.success(body));
      }

      return Result.success(body);
    }
}
5.3同一非常处理

   第一种写法
ErrorHandler类
三个注解:@ResponseBody @ControllerAdvice @ExceptionHandler
@ResponseBody
@ControllerAdvice
public class ErrorHandler {
    @ExceptionHandler
    public Result handler(Exception e){
      return Result.fail(e.getMessage());
    }
}   第二种写法
eg:
/捕获空指针非常代码的两种写法
    //捕获空指针异常代码的两种写法
    @ExceptionHandler(NullPointerException.class)
    public Result handler(Exception e){
      return Result.fail(e.getMessage());
    }
    @ExceptionHandler
    public Result handler(NullPointerException e){
      return Result.fail(e.getMessage());
    } 如果注解里面没有写捕获什么非常。那么就会以参数中为准。

六、业务代码

MybatisGenerate插件

   注:持久层的代码可以由MybatisGenerate插件一键天生。
直接根据我们的数据库表
天生实体类,mapper类,xml文件
不需要我们自己写mapper类,和基于标签的mapper.xml这些简朴又繁琐的工作
但是值得注意的是天生的方法中的细微区别!!
接口分析

   1.用户登录
根据用户名和暗码、判断是否正确。

2.查询用户信息
根据用户Id.查询用户信息。

3.博客列表
查询所有博客

4.查询作者信息
①根据博客,拿到作者Id
②根据作者id,获取作者信息

5.查询博客详情
根据博客id,查询博客信息

6.修改博客
根据博客id,修改博客信息

7.添加博客
插入博客信息

8.删除博客
    梳理一下

用户表:
1.根据用户名,查询用户信息。
2.根据用户id,查询用户信息

博客表
1.查询博客列表
2.根据博客id,查询博客信息
3.根据博客id,修改博客信息
4.插入博客
5.删除博客 
6.1持久层代码

   UserMapper类
@Mapper
public interface UserMapper {

    //根据用户名,查询用户信息\
    @Select("select * from user where user_name = #{userName} and delete_flag = 0")
    UserInfo selectByName(String userName);

    @Select("select *from user where id = #{userId} and delete_flag = 0")
    UserInfo selectById(Integer userId);
}    BlogMapper类
注:由于update既是uptate功能的接口。也是delete功能的接口,因此我们要写动态SQL。
动态SQL我们利用XML的方式来誊写。
@Mapper
public interface BlogMapper {
    //查询博客列表
    @Select("select * from blog where delete_flag =0 order by create_time desc ")
    List<BlogInfo> selectAllBlog();

    //根据博客Id,查询博客信息。
    @Select("select * from blog where id = #{blogId} and delete_flag = 0")
    BlogInfo selectById(Integer blogId);

    //根据博客Id,修改博客信息。
    //此修改包括修改和删除。根据参数决定修改什么。
    //我们用动态SQL来实现
    Integer updateById(BlogInfo blogInfo);

    //插入博客
    @Insert("insert into blog (title, content, user_id) values (#{title},#{content},#{userId})")
    Integer interBlog(BlogInfo blogInfo);
   
}
<?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.qyy.springblog.mapper.BlogMapper">
    <update id="updateById">
      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}
    </update>
</mapper>   写完之后。我们测试一下持久层代码是否都正确执行。 
6.2实现前后端接口

6.2.1实现博客列表显示的接口

   后端
    @RequestMapping("/getList")
    public List<BlogInfo> queryBlogList(){
      return blogService.queryBlogList();
    }     public List<BlogInfo> queryBlogList() {
      return blogMapper.selectAllBlog();
    }   前端 
    <script>
      $.ajax({
            type: "get",
            url: "blog/getList",
            success:function(result){
                //逻辑不全,可以再完善
                //比如code == 200,但是data为空,页面可以提示,当前没有任何博客,快去写博客。然后进行跳转
                if(result.code == 200 && result.data!=null){
                  var finalHtml = "";
                  for(var blog of result.data){//循环的时候每个数据叫blog
                        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+'">查看全文&gt;&gt;</a>';
                        finalHtml += '</div>';
                  }
                  $(".right").html(finalHtml);
                }

            }
      });
    </script>   我们发现显示的时间很长。 返回的时间就如我们postman返回的时间一样。
如何修改呢。我们最好在后端同一修改了
https://i-blog.csdnimg.cn/direct/fe6f9bc6a82649f0b652bf9e77a26050.png
https://i-blog.csdnimg.cn/direct/0cc6db9739ba4f618329f1687ffd643a.png
修改时间显示 

   我们在blog实体类中修改。
1.创建utils包。
2.创建 DataUtils 类
3.创建formatDate方法
指定我们的时间返回格式的方法参考如下表。
public class DateUtils {
    public static String formatDate(Date date){
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
      return simpleDateFormat.format(date);
    }
}4.在blog实体类添加如下代码
    public String getCreateTime(){
      return DateUtils.formatDate(createTime);
    }

https://i-blog.csdnimg.cn/direct/0dccd1819ba3472193d5d0111ed74cdc.png
存的的问题

这是博客列表显示的 
https://i-blog.csdnimg.cn/direct/c5bd5a6bc6b9491dab7b24327bf55942.png
   在博客列表中我们发现存在####。
不但云云。我们还希望。在博客列表中只显示一部分博客。而不是将博客全部显示出来。这个又该怎么利用呢?
这个如何修改呢 
1.去掉特殊符号
2.对内容长度进行处理
①通过SQL截断
②通过Java截断
 我选择通过java截断
    public List<BlogInfo> queryBlogList() {

      List<BlogInfo> blogInfos = blogMapper.selectAllBlog();
      for (BlogInfo blog: blogInfos){
            String result = blog.getContent();
            result = result.replace("#","");
            if(result.length() >= 66){
                result = result.substring(0,65)+"......";
            }
            blog.setContent(result);
      }
      return blogInfos;
    }实现成功
https://i-blog.csdnimg.cn/direct/17cad6043da448c8b3e69e039dec8f38.png
6.2.2实现博客查看全文接口

   后端
    @RequestMapping("getBlogDetail")
    public BlogInfo queryBlogById(Integer blogId){
      return blogService.queryBlogById(blogId);
    }     @RequestMapping("getBlogDetail")
    public BlogInfo queryBlogById(Integer blogId){
      return blogService.queryBlogById(blogId);
    } https://i-blog.csdnimg.cn/direct/47d8340cffe440eeae36846ea34f0726.png
正确返回 
   前端
      <div class="right">
            <div class="content">
                <div class="title"></div>
                <div class="date"></div>
                <div class="detail"></div>
                <div class="operating">
                  <button onclick="window.location.href='blog_update.html'">编辑</button>
                  <button onclick="deleteBlog()">删除</button>
                </div>
            </div>

      </div>       $.ajax({
            type: "get",
            url: "/blog/getBlogDetail"+location.search, //location.search拿到问号及后面的内容
            success:function(result){
                var blog = result.data;
                if(result.code == 200 && result.data!=null){
                  $(".right .content .title").text(blog.title);
                  $(".right .content .date").text(blog.createTime);
                  $(".right .content .detail").text(blog.content);
                }
            }
      }); https://i-blog.csdnimg.cn/direct/91a6dc224a84457c9d5affd174ab3f67.png
   成功显示。 
本篇博客暂时就写到这里啦。后续内容会在下一篇文章更新哦。
后面登录的接口才是本次项目的大Boss。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 24.<Spring博客体系①(数据库+公共代码+持久层+显示博客列表+博客详情)