springboot网站开发02-接入持久层框架mybatisPlus

springboot网站开发02-接入持久层框架mybatisPlus!经过上一小节内容分享,我们的项目嵌套模式框架搭建好了,下面就是开始编辑具体的业务代码了,我们使用到了持久层框架是mybatisPlus插件。下面是一些具体的植入框架的操作步骤。


第一步,在Pom.xml加入参数,让maven帮我们管理插件的依赖包。

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>blog</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>blog-framework</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--lombk-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--SpringSecurity启动器-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-security</artifactId>-->
<!--        </dependency>-->
        <!--redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--fastjson依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <!--jwt依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <!--mybatisPlus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!--mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--阿里云OSS-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
        </dependency>

        <!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>

    </dependencies>

</project>

这是完整的依赖包参数配置,因为我们不可能只是仅仅需要mybatisPlus,肯定还需要其他的依赖 ,比如Lombk-实体类注解包,数据库mysql底层连接池mysql-connector-java等。还有json格式转化插件fastjson。


加好了参数后点击右侧的菜单栏,点击按钮,帮助我们下载依赖代码插件。


 

这个是我们公共子模块的里面的文件夹名字,每一个都有各自的作用。

第一个config里面存放了一些配置对象类。比如mybatisPlus的配置类,比如webConfig完成了跨域请求的设置,已经json格式的封装。

 

package com.blog.config;

import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
      // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOriginPatterns("*")
                // 是否允许cookie
                .allowCredentials(true)
                // 设置允许的请求方式
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                // 设置允许的header属性
                .allowedHeaders("*")
                // 跨域允许时间
                .maxAge(3600);
    }

    @Bean//使用@Bean注入fastJsonHttpMessageConvert
    public HttpMessageConverter fastJsonHttpMessageConverters() {
        //1.需要定义一个Convert转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

        SerializeConfig.globalInstance.put(Long.class, ToStringSerializer.instance);

        fastJsonConfig.setSerializeConfig(SerializeConfig.globalInstance);
        fastConverter.setFastJsonConfig(fastJsonConfig);
        HttpMessageConverter<?> converter = fastConverter;
        return converter;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(fastJsonHttpMessageConverters());
    }

}

 这个代码就是WebConfig配置类内容了,写的注解很详细,不再一一介绍了。

package com.blog.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
    public class MbatisPlusConfig {

        /**
         * 3.4.0之后版本
         * @return
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }

这个就是mybatisPlus配置类内容,我们里面配置了一下关于分页管理的功能。


package com.blog.constants;

/**
 * 常量定义的工具实体类
 */
public class SystemConstants {
    /**
     * 文章是草稿状态,默认值为1
     */
    public static final int ARTICLE_STATUS_DRAFT = 1;

    /**
     * 文章是正常发布状态,默认值为0
     */
    public static final int ARTICEL_STATUS_NORMAL = 0;
    /**
     * 页面当前的页码,默认初始值为1
     */
    public static final int PAGE_NUMBER = 1;
    /**
     * 页面当前的list容量,默认初始值为10
     */
    public static final int PAGE_SIZE = 10;
    /**
     * 状态码是正常的,默认值为0
     */
    public static final String STATUS_NORMAL = "0";
    /**
     * 友链状态码正常的 默认值为0
     */
    public static final String LINK_STATUS_NORMAL = "0";
}

 以上是关于系统内使用到了一些常量定义,实体类。我们封装成这样的常量后,使用起来就便于代码的协调统一了。一处修改,其他地方调用了都会跟着也一起变化了,省事,效率高。


这个是分开设计的思想,entity里面写的实体类,是对应数据库内数据表的,但是实际传递给页面前端渲染的数据,经常会遇到(个别属性值的传递)。我们就会在额外定义一个专门用于前端渲染使用的实体类对象(Map-Model)(view-modle)-页面模型。区别于数据库表模型。

这样设计的优势是,后端服务器在响应给前端页面接口的数据时传递返回过去的数据信息会减少一些不必要的内存占用。毕竟网络带宽传输是有限制的,传输的精简一些,渲染起来会快,而且响应速度快,客户体验就会好一点。这是站在客户体验的角度这样设计的。实际开发中也的确是这样设计的。

题外话:如果你是有多个终端,比如安卓,苹果,平板,电脑,小程序等,甚至公司会要求你单独创建对应的文件夹,不同终端存放不同的实体类模型。以此来区别开对待数据渲染的任务。


 以下代码是文章(新闻文章)实体类,里面有一个逻辑删除的标志。大家注意一下。

package com.blog.domain.entity;

import java.util.Date;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 文章表(Article)表实体类
 *
 * @author makejava
 * @since 2023-05-16 10:37:43
 */
@SuppressWarnings("serial")
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sg_article")
@Accessors(chain = true)
public class Article  {
    @TableId
    private Long id;
    //标题
    private String title;
    //文章内容
    private String content;
    //文章摘要
    private String summary;
    //所属分类id
    private Long categoryId;
    //分类名字
    @TableField(exist = false)
    private String categoryName;
    //缩略图
    private String thumbnail;
    //是否置顶(0否,1是)
    private String isTop;
    //状态(0已发布,1草稿)
    private String status;
    //访问量
    private Long viewCount;
    //是否允许评论 1是,0否
    private String isComment;
    
    private Long createBy;
    
    private Date createTime;
    
    private Long updateBy;
    
    private Date updateTime;
    //删除标志(0代表未删除,1代表已删除)
    @TableLogic(value = "0",delval = "1")
    private Integer delFlag;



}


配合着给大家展示一个前端渲染新闻文章的实体类内容,这个ArticleDetailVo对象,用于前端新闻列表的渲染使用,因为列表页面,其实并不会让用户看见具体的新闻内容,所以里面不需要content(详情属性字段)

package com.blog.domain.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ArticleDetailVo {
    private Long id;
    //标题
    private String title;
    //文章摘要
    private String summary;
    //文章内容
    private String content;
    //所属分类id
    private Long categoryId;
    //所属分类名字
    private String categoryName;
    //缩略图
    private String thumbnail;

    //访问量
    private Long viewCount;

    private Date createTime;
}

如图,前端列表页渲染使用的页面模型对象是不需要这个属性值的。


 核心思想就是渲染需要什么属性,我们就封装什么属性,追求的极简主义。一个多余的也不封装。

比如前端页面有一个热点新闻栏目列表,里面只需要2个属性值:title(标题)和vieCount(浏览量)

这个HotArticleVo页面模型就是专门封装起来给热门新闻列表使用的一个实体对象。


package com.blog.domain.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageVo {

    //列表对象封装的对象集合
    private List rows;
    //一共有多少条数据
    private Long total;
}

 我们为了便于做分页处理,封装了一个页面对象,它里面2个值,一个是封装好了将来要遍历循环的集合对象,一个是总共多少条数据。


package com.blog.domain;

import com.blog.enums.AppHttpCodeEnum;

import java.io.Serializable;

public class ResponseResult<T> implements Serializable {
    private Integer code;
    private String msg;
    private T data;

    public ResponseResult() {
        this.code = AppHttpCodeEnum.SUCCESS.getCode();
        this.msg = AppHttpCodeEnum.SUCCESS.getMsg();
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }

    public static ResponseResult okResult() {
        ResponseResult result = new ResponseResult();
        return result;
    }

    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }

    public static ResponseResult okResult(Object data) {
        ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg());
        if (data != null) {
            result.setData(data);
        }
        return result;
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums) {
        return setAppHttpCodeEnum(enums, enums.getMsg());
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg) {
        return setAppHttpCodeEnum(enums, msg);
    }

    public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums) {
        return okResult(enums.getCode(), enums.getMsg());
    }

    private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg) {
        return okResult(enums.getCode(), msg);
    }

    public ResponseResult<?> error(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data) {
        this.code = code;
        this.data = data;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
        return this;
    }

    public ResponseResult<?> ok(T data) {
        this.data = data;
        return this;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

这个是一个页面回传(响应请求,携带数据返回给前端的实体类工具类),里面主要有三个属性,

private Integer code;//响应的状态码,
private String msg;//响应的描述信息
private T data;//响应的具体数据信息。是一个泛型。

package com.blog.enums;

public enum AppHttpCodeEnum {
    // 成功
    SUCCESS(200,"操作成功"),
    // 登录
    NEED_LOGIN(401,"需要登录后操作"),
    NO_OPERATOR_AUTH(403,"无权限操作"),
    SYSTEM_ERROR(500,"出现错误"),
    USERNAME_EXIST(501,"用户名已存在"),
    PHONENUMBER_EXIST(502,"手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"),
    REQUIRE_USERNAME(504, "必需填写用户名"),
    LOGIN_ERROR(505,"用户名或密码错误");
    int code;
    String msg;

    AppHttpCodeEnum(int code, String errorMessage){
        this.code = code;
        this.msg = errorMessage;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

为了节省时间提升效率,我们专门封装了一个来做关于前端请求反馈的枚举类型对象。enum(枚举)。


这2个,大家最为熟悉,之前学习SSM的时候,经常使用,一个是持久层接口,一个是业务层接口,里面嵌套了一个业务层接口实现类。不过多介绍了。


 

package com.blog.utils;

import com.blog.domain.entity.Article;
import com.blog.domain.vo.HotArticleVo;
import com.fasterxml.jackson.databind.util.BeanUtil;
import org.springframework.beans.BeanUtils;

import java.util.List;
import java.util.stream.Collectors;

public class BeanCopyUtils {
    private BeanCopyUtils() {
    }

    public static <V> V copyBean(Object source,Class<V> vClass){
       //创建目标对象
        V result = null;

        try {
            result = vClass.newInstance();
            //copy对象属性
            BeanUtils.copyProperties(source,result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回对象
        return result;

    }

    public static <O,V> List<V> copyBeanList(List<O> list, Class<V> vClass){
        return list.stream()
                .map(o -> copyBean(o, vClass))
                .collect(Collectors.toList());
    }


}

这个是一个用于数据过滤(抽取数据集合)的工具类。可以帮助我们完成一些数据集合的二次过滤抽取。达到我们想要的效果。我们不可能总是一遍又一遍的去反复查询数据库信息,有些业务,我们只需要查询一次,就可以反复从集合中抽取我们想要的信息就行了。可以节省服务器的cpu资源和内存资源。回头 抽时间,专门做一期分享,讲这个数据抽取,它用了是stream流的模式操作的,现在有些公司喜欢使用流操作。其实背后底层还是原始的,只是流操作看上去代码较为简洁而已。


server:
  port: 7777
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sg_blog?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password:
    driver-class-name: com.mysql.cj.jdbc.Driver
  servlet:
    multipart:
      max-file-size: 2MB
      max-request-size: 5MB
mybatis-plus:
  configuration:
    # 日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-field: delFlag
      logic-delete-value: 1
      logic-not-delete-value: 0
      id-type: auto

这个是前端项目模块的springboot启动配置文件信息。里面有服务器接口,数据源参数,我的本地数据库是没有设置密码的,所以我那里是空白,不需要输入。还设置好了mybatisPlus的配置信息。全局配置里有一个逻辑删除的配置。默认是0表示未删除,1代表已删除。


为了节省时间,提升效率,我们使用了一个插件EasyCode.它可以帮助我们自动把数据表,映射为项目内的实体类,包括业务层,持久层,都会自动创建好的。我们仅仅需要告诉它存储路径。

 

如图,你选择好,模块(存储到哪个项目下面去!我们肯定是存在公共模块下面啊,前端,后端都要用到这个代码的。)后面的Tempalte模块勾选,我们不选controller(控制器),控制器我们在前端项目里自己写的,我们只需要EasyCode插件帮助我们写好基础的东西就行了,比如持久层,业务层,实体类。持久层实现类。

持久层实现类具体如何实现业务逻辑,还是需要我们自己后面完善的,它只是做一个基础的架构而已。并不会真正的达到人工智能的效果。还是离不开程序员的思维+操作。 


后面会单独出一期讲解这个EasyCode插件如何使用的教程。


走到这一步,基本上,项目的骨架填充,就已经完成一大半了。下一个小节,我们以首页查询新新闻分类为案例,展示一下,如何使用mybatisPlus实现具体的业务逻辑操作。