阅读 64

SpringBoot集成MybatisPlus项目实操

前言

该说的在《SpringBoot集成Mybatis项目实操》一文中都讲了,本文只是将 Mybatis 换成了 Mybatis Plus,带大家将整个项目跑起来。

本文将实现 SpringBoot+Mybatis Plus 的项目搭建,Mybatis Plus 作为 Mybatis 的增强工具,功能更佳强大,所以需要我们自定义的代码就少了,实现起来也更加简单。

不说废话了,我们直接进入主题。

数据库

本项目采用的是 MySQL 数据库,版本为 8.x,建表语句如下:

CREATE TABLE `teacher` (   `id` varchar(36) NOT NULL,   `name` varchar(20) DEFAULT NULL,   `age` int(11) DEFAULT NULL,   `address` varchar(100) DEFAULT NULL,   `created_date` timestamp NULL DEFAULT NULL,   `last_modified_date` timestamp NULL DEFAULT NULL,   `del_flag` int(2) NOT NULL DEFAULT '0',   `create_user_code` varchar(36) DEFAULT NULL,   `create_user_name` varchar(50) DEFAULT NULL,   `last_modified_code` varchar(36) DEFAULT NULL,   `last_modified_name` varchar(50) DEFAULT NULL,   `version` int(11) NOT NULL DEFAULT '1',   PRIMARY KEY (`id`),   UNIQUE KEY `uk_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='教师' CREATE TABLE `student` (   `id` varchar(36) NOT NULL,   `name` varchar(20) DEFAULT NULL,   `teacher_id` varchar(36) NOT NULL,   `address` varchar(100) DEFAULT NULL,   `created_date` timestamp NULL DEFAULT NULL,   `last_modified_date` timestamp NULL DEFAULT NULL,   `del_flag` int(2) NOT NULL DEFAULT '0',   `create_user_code` varchar(30) DEFAULT NULL,   `create_user_name` varchar(50) DEFAULT NULL,   `last_modified_code` varchar(30) DEFAULT NULL,   `last_modified_name` varchar(50) DEFAULT NULL,   `version` int(11) NOT NULL DEFAULT '1',   PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生' 复制代码

搭建SpringBoot项目

使用 IDEA 新建一个 Maven 项目,叫做 mybatisplus-springboot。

一些共用的基础代码可以参考上篇文章,这里不做重复介绍,会介绍一些 MybatisPlus 相关的代码。

引入依赖

<parent>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-parent</artifactId>   <version>2.6.3</version> </parent> <properties>   <java.version>1.8</java.version>   <fastjson.version>1.2.73</fastjson.version>   <hutool.version>5.5.1</hutool.version>   <mysql.version>8.0.19</mysql.version>   <mybatis.version>2.1.4</mybatis.version>   <mapper.version>4.1.5</mapper.version>   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>   <org.projectlombok.version>1.18.20</org.projectlombok.version> </properties> <dependencies>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-web</artifactId>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-aop</artifactId>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-security</artifactId>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-validation</artifactId>   </dependency>   <dependency>     <groupId>com.alibaba</groupId>     <artifactId>fastjson</artifactId>     <version>${fastjson.version}</version>   </dependency>   <dependency>     <groupId>cn.hutool</groupId>     <artifactId>hutool-all</artifactId>     <version>${hutool.version}</version>   </dependency>   <dependency>     <groupId>org.projectlombok</groupId>     <artifactId>lombok</artifactId>     <version>${org.projectlombok.version}</version>     <optional>true</optional>   </dependency>   <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-test</artifactId>     <scope>test</scope>   </dependency>   <dependency>     <groupId>mysql</groupId>     <artifactId>mysql-connector-java</artifactId>     <version>${mysql.version}</version>     <scope>runtime</scope>   </dependency>   <dependency>     <groupId>com.baomidou</groupId>     <artifactId>mybatis-plus-boot-starter</artifactId>     <version>3.5.1</version>   </dependency>   <dependency>     <groupId>com.baomidou</groupId>     <artifactId>mybatis-plus</artifactId>     <version>3.5.1</version>   </dependency>   <dependency>     <groupId>org.springframework.data</groupId>     <artifactId>spring-data-commons</artifactId>     <version>2.4.6</version>   </dependency>   <dependency>     <groupId>org.springdoc</groupId>     <artifactId>springdoc-openapi-ui</artifactId>     <version>1.6.9</version>   </dependency>   <dependency>     <groupId>com.alibaba</groupId>     <artifactId>druid-spring-boot-starter</artifactId>     <version>1.1.18</version>   </dependency>   <dependency>     <groupId>org.mapstruct</groupId>     <artifactId>mapstruct</artifactId>     <version>${org.mapstruct.version}</version>   </dependency>   <dependency>     <groupId>org.mapstruct</groupId>     <artifactId>mapstruct-processor</artifactId>     <version>${org.mapstruct.version}</version>   </dependency> </dependencies> <build>   <plugins>     <plugin>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-maven-plugin</artifactId>     </plugin>   </plugins> </build> 复制代码

有些依赖不一定是最新版本,而且你看到这篇文章时,可能已经发布了新版本,到时候可以先模仿着将项目跑起来后,再根据自己的需求来升级各项依赖,有问题咱再解决问题。

分页处理

MybatisPlus关于分页处理有好几种方式,此处采用的是 IPage 方式。具体操作步骤如下:

1、定义 Mapper

@Repository public interface TeacherMapper extends BaseMapper<Teacher> { } 复制代码

2、自定义 IPage 的实现类

@Data public class MbpPage<T> implements IPage<T> {     private List<T> records;     private long total;     private long pageSize;     private long pageNum;     private List<OrderItem> orders;     private boolean optimizeCountSql;     private boolean isSearchCount;     private boolean hitCount;     public MbpPage() {         this.records = Collections.emptyList();         this.total = 0L;         this.pageSize = 10L;         this.pageNum = 1L;         this.orders = new ArrayList();         this.optimizeCountSql = true;         this.isSearchCount = true;         this.hitCount = false;     }     public MbpPage(long pageNum, long pageSize) {         this(pageNum, pageSize, 0L);     }     public MbpPage(long pageNum, long pageSize, long total) {         this(pageNum, pageSize, total, true);     }     public MbpPage(long pageNum, long pageSize, boolean isSearchCount) {         this(pageNum, pageSize, 0L, isSearchCount);     }     public MbpPage(long pageNum, long pageSize, long total, boolean isSearchCount) {         this.records = Collections.emptyList();         this.total = 0L;         this.pageSize = 10L;         this.pageNum = 1L;         this.orders = new ArrayList();         this.optimizeCountSql = true;         this.isSearchCount = true;         this.hitCount = false;         if (pageNum > 1L) {             this.pageNum = pageNum;         }         this.pageSize = pageSize;         this.total = total;         this.isSearchCount = isSearchCount;     }     public boolean hasPrevious() {         return this.pageNum > 1L;     }     public boolean hasNext() {         return this.pageNum < this.getPages();     }     @Override     public List<T> getRecords() {         return this.records;     }     @Override     public MbpPage<T> setRecords(List<T> records) {         this.records = records;         return this;     }     @Override     public long getTotal() {         return this.total;     }     @Override     public MbpPage<T> setTotal(long total) {         this.total = total;         return this;     }     @Override     public long getSize() {         return this.pageSize;     }     @Override     public MbpPage<T> setSize(long pageSize) {         this.pageSize = pageSize;         return this;     }     @Override     public long getCurrent() {         return this.pageNum;     }     @Override     public MbpPage<T> setCurrent(long current) {         this.pageNum = current;         return this;     }     @Override     public List<OrderItem> orders() {         return this.getOrders();     }     public List<OrderItem> getOrders() {         return this.orders;     }     public void setOrders(List<OrderItem> orders) {         this.orders = orders;     } } 复制代码

3、请求体继承 MbpPage

@Getter @Setter public class TeacherQueryPageDTO extends MbpPage<Teacher> {   private String name; } 复制代码

4、服务层的接口定义分页查询方法,其返回值类型是 IPage<实体类> 。

// 获取教师分页列表 IPage<TeacherVO> queryPage(TeacherQueryPageDTO dto); 复制代码

5、服务的实现类要继承 ServiceImpl< Mapper接口类,实体类 > ,重写分页查询方法。

@Service @RequiredArgsConstructor public class TeacherServiceImpl extends ServiceImpl<TeacherMapper, Teacher> implements     TeacherService {   private final TeacherStruct teacherStruct;   @Override   public IPage<TeacherVO> queryPage(TeacherQueryPageDTO dto) {     IPage<TeacherVO> teacherPage = this.lambdaQuery().page(dto)         .convert(teacher -> teacherStruct.modelToVO(teacher));     return teacherPage;   } } 复制代码

关于 IPage 的使用,除了上述这种用法,还有别的用法,可以参考这篇文章。

我们得到的分页查询结果是 IPage 对象,可以直接使用,也可以根据需要进行修改,比如下面这个文件:

@Getter @Setter public class PageResult<T> {   /**    * 总条数    */   private Long total;   /**    * 总页数    */   private Integer pageCount;   /**    * 每页数量    */   private Integer pageSize;   /**    * 当前页码    */   private Integer pageNum;   /**    * 分页数据    */   private List<T> data;   /**    * 处理Mybatis Plus分页结果    */   public static <T> PageResult<T> ok(IPage<T> iPage) {     PageResult<T> result = new PageResult<T>();     result.setTotal(iPage.getTotal());     int pageCount = (int) Math.ceil((double) result.getTotal() / iPage.getSize());     result.setPageCount(pageCount);     result.setPageNum((int) iPage.getCurrent());     result.setPageSize((int) iPage.getSize());     result.setData(iPage.getRecords());     return result;   } } 复制代码

MybatisPlus基础实体类

作为其他实体类的父类,封装了所有的公共字段,包括逻辑删除标志,版本号,创建人和修改人信息。相较于 Mybatis 提供的基础实体类,我们不需要自定义注解,MybatisPlus 提供的注解比较全面。

@Data @Schema(title = "核心基础实体类") @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true) public class CoreBase implements Serializable {   @TableId(value = "id", type = IdType.ASSIGN_ID)   @Schema(name = "")   private String id;   @Schema(description = "是否删除 0未删除(默认),1已删除")   @TableField(value = "del_flag")   @TableLogic(delval = "1", value = "0")   private Integer delFlag;   @Schema(description = "创建人")   @TableField(value = "create_user_code", fill = FieldFill.INSERT)   private String createUserCode;   @Schema(description = "创建人名称")   @TableField(value = "create_user_name", fill = FieldFill.INSERT)   private String createUserName;   @Schema(description = "创建时间")   @TableField(value = "created_date", fill = FieldFill.INSERT)   @DateTimeFormat(       pattern = "yyyy-MM-dd HH:mm:ss"   )   @JsonFormat(       pattern = "yyyy-MM-dd HH:mm:ss"   )   private LocalDateTime createdDate;   @Schema(description = "修改人代码")   @TableField(value = "last_modified_code", fill = FieldFill.INSERT_UPDATE)   private String lastModifiedCode;   @Schema(description = "修改人名称")   @TableField(value = "last_modified_name", fill = FieldFill.INSERT_UPDATE)   private String lastModifiedName;   @Schema(description = "修改时间")   @TableField(value = "last_modified_date", fill = FieldFill.INSERT_UPDATE)   @DateTimeFormat(       pattern = "yyyy-MM-dd HH:mm:ss"   )   @JsonFormat(       pattern = "yyyy-MM-dd HH:mm:ss"   )   private LocalDateTime lastModifiedDate;   @Schema(description = "版本号")   @Version   @TableField(value = "version")   private String version; } 复制代码

关于主键生成策略,本项目采用的是 IdType.ASSIGN_ID,即雪花算法,关于其他生成策略,可以参考MybatisPlus 主键策略(type=IdType.ASSIGN_ID等详解) 一文。

至于实体类中的一些注解,详细介绍推荐阅读MyBatisPlus--注解 和@TableLogic注解

MybatisPlus配置类

MetaObjectHandler是元对象字段填充控制器抽象类,实现公共字段自动写入。

比如通常,我们在建表时,会设置几个公共字段:创建人(creator)、更新人(uptater)、创建时间(create_time)、更新时间(update_time)。

每次将实体对象新增入库时,都要设置创建人和创建时间;每次更新实体对象时,都要设置更新人和更新时间;

MetaObjectHandler 中主要提供了两个方法:

public interface MetaObjectHandler {          /**      * 插入元对象字段填充(用于插入时对公共字段的填充)      *      * @param metaObject 元对象      */     void insertFill(MetaObject metaObject);     /**      * 更新元对象字段填充(用于更新时对公共字段的填充)      *      * @param metaObject 元对象      */     void updateFill(MetaObject metaObject); } 复制代码

这些公共字段我们都会封装在一个 Super Entity 类中,所以在这个类中,我们把所有更新和插入时需要做改动的字段都加上 @TableField 注解,并且设置 fill 属性。比如上面我们定义的 CoreBase 类。

查看我们自定义的实现类:

@ConditionalOnBean({com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration.class}) public class MybatisPlusAutoConfiguration {   @Bean   public MetaObjectHandler metaObjectHandler() {     return new FillFieldConfiguration();   }   public static class FillFieldConfiguration implements MetaObjectHandler {     @Override     public void insertFill(MetaObject metaObject) {       LocalDateTime now = DateUtil.toLocalDateTime(new Date());       metaObject.setValue("createUserCode", "1");       metaObject.setValue("createUserName", "admin");       metaObject.setValue("createdDate", now);       metaObject.setValue("lastModifiedCode", "1");       metaObject.setValue("lastModifiedName", "admin");       metaObject.setValue("lastModifiedDate", now);     }     @Override     public void updateFill(MetaObject metaObject) {       DateTime now = DateUtil.date();       metaObject.setValue("lastModifiedCode", "1");       metaObject.setValue("lastModifiedName", "admin");       metaObject.setValue("lastModifiedDate", now);     }   }   /**    * 分页插件    *    * @return    */   @Bean   public MybatisPlusInterceptor mybatisPlusInterceptor() {     MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();     mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));     return mybatisPlusInterceptor;   } } 复制代码

关于上述代码根据项目实际需求进行调整,来填充非关键数据。

最后在 resources 目录下创建META-INF目录下,在 META-INF 目录下创建 spring.factories 文件,文件内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\   com.msdn.orm.hresh.common.mybatisPlus.config.MybatisPlusAutoConfiguration 复制代码

一键式生成模版代码

运行 orm-generate 项目,在 swagger 上调用 /build 接口,调用参数如下:

{     "database": "mysql_db",     "flat": true,     "type": "mybatisplus",     "group": "hresh",     "host": "127.0.0.1",     "module": "orm",     "password": "root",     "port": 3306,     "table": [         "teacher",         "student"     ],     "username": "root",     "tableStartIndex":"0" } 复制代码

代码文件直接移到项目中就行了,稍微修改一下引用就好了。

功能实现

请求日志输出

比如说我们访问 /teachers/queryPage 接口,看看控制台输出情况:

Request Info      : {"classMethod":"com.msdn.orm.hresh.controller.TeacherController.queryPage","ip":"127.0.0.1","requestParams":{"dto":{"hitCount":false,"optimizeCountSql":true,"records":[{"name":"hresh2","age":46},{"name":"hresh1","age":46},{"name":"hresh0","age":46},{"address":"湖北大学","name":"hresh","age":44},{"address":"武汉大学","name":"acorn2","age":46}],"pageSize":5,"pageNum":1,"total":7,"orders":[{"column":"name","asc":false}],"isSearchCount":true}},"httpMethod":"GET","url":"http://localhost:8802/teachers/queryPage","result":{"code":"200","message":"操作成功","success":true},"methodDesc":"获取教师分页列表","timeCost":93} 复制代码

可以看到,日志输出中包含前端传来的请求体,请求 API,返回结果,API 描述,API 耗时。

统一返回格式

比如说分页查询,返回结果如下:

{ "data": { "total": 7, "pageCount": 2, "pageSize": 5, "pageNum": 1, "data": [ { "name": "hresh2", "age": 46, "address": null }, { "name": "hresh1", "age": 46, "address": null }, { "name": "hresh0", "age": 46, "address": null }, { "name": "hresh", "age": 44, "address": "湖北大学" }, { "name": "acorn2", "age": 46, "address": "武汉大学" } ] }, "code": "200", "message": "操作成功", "success": true } 复制代码

如果是新增请求,返回结果为:

{ "data": null, "code": "200", "message": "操作成功", "success": true } 复制代码

此处 data 没有新增的数据,如果项目需要新增的实体信息,可以稍作修改。

异常处理

下面简单演示一下参数异常的情况,在 add user 时校验参数值是否为空。

  public Boolean add(TeacherDTO dto) {     if (StringUtils.isBlank(dto.getName())) {       BusinessException.validateFailed("name 不能为空");     }     return this.save(teacherStruct.dtoToModel(dto));   } 复制代码

如果传递的 name 值为空,则返回结果为:

{ "data": null, "code": "400", "message": "name 不能为空", "success": false } 复制代码

@Validated分组校验

修改 TeacherDTO,当新增数据时,校验 name 不为空,修改数据时,address 不为空。

@Getter @Setter @AllArgsConstructor @NoArgsConstructor public class TeacherDTO {   @Schema(name = "")   @NotBlank(groups = {Add.class})   private String name;   @Schema(name = "")   private Integer age;   @Schema(name = "")   @NotBlank(groups = {Edit.class})   private String address; } 复制代码

最后修改 controller 文件

  @PostMapping   @Operation(description = "新增教师")   public Result add(@Validated(Add.class) @RequestBody TeacherDTO dto) {     boolean flag = teacherService.add(dto);     if (!flag) {       return Result.failed();     }     return Result.ok();   } 复制代码

调用新增接口,故意将 name 置为空,返回结果为:

{ "data": null, "code": "400", "message": "name不能为空", "success": false } 复制代码

填充公共字段

此处依赖于MetaObjectHandler,重点在 MybatisPlusAutoConfiguration 文件。

public static class FillFieldConfiguration implements MetaObjectHandler {     @Override     public void insertFill(MetaObject metaObject) {       LocalDateTime now = DateUtil.toLocalDateTime(new Date());       metaObject.setValue("createUserCode", "1");       metaObject.setValue("createUserName", "admin");       metaObject.setValue("createdDate", now);       metaObject.setValue("lastModifiedCode", "1");       metaObject.setValue("lastModifiedName", "admin");       metaObject.setValue("lastModifiedDate", now);     }     @Override     public void updateFill(MetaObject metaObject) {       DateTime now = DateUtil.date();       metaObject.setValue("lastModifiedCode", "1");       metaObject.setValue("lastModifiedName", "admin");       metaObject.setValue("lastModifiedDate", now);     }   } 复制代码

上述代码中关于新增者信息和修改者信息,暂时是写死的状态,实际项目中,可以根据 token 信息进行解析,然后来填充新增者和修改者信息。

批量操作

这里简单演示一下关于批量新增的代码

  @Override   public Boolean batchAdd(TeacherDTO dto) {     List<Teacher> teachers = new ArrayList<>();     for (int i = 0; i < 3; i++) {       Teacher teacher = Teacher.builder().name(dto.getName() + i).age(46).address(dto.getAddress())           .build();       teachers.add(teacher);     }     return this.saveBatch(teachers);   } 复制代码

执行效果如下:

SQL查询结果

分页查询

前端参数传递:

{ "pageNum": 1, "pageSize": 5, "orders": [ { "column": "name", "asc": false } ] } 复制代码

后端代码:

  public IPage<TeacherVO> queryPage(TeacherQueryPageDTO dto) {     IPage<TeacherVO> teacherPage = this.lambdaQuery().page(dto)         .convert(teacher -> teacherStruct.modelToVO(teacher));     return teacherPage;   } 复制代码

查询结果为:

{ "data": { "total": 7, "pageCount": 2, "pageSize": 5, "pageNum": 1, "data": [ { "name": "hresh2", "age": 46, "address": "华中", "studentVOS": null }, { "name": "hresh1", "age": 46, "address": "华师", "studentVOS": null }, { "name": "hresh0", "age": 46, "address": "华科", "studentVOS": null }, { "name": "hresh", "age": 44, "address": "湖北大学", "studentVOS": null }, { "name": "acorn2", "age": 46, "address": "武汉大学", "studentVOS": null } ] }, "code": "200", "message": "操作成功", "success": true } 复制代码

动态查询

MybatisPlus 作为 Mybatis 的升级版,确实有不少优势,比如批量操作、动态查询等。

查询方法如下:

  public List<TeacherVO> queryList(TeacherDTO dto) {     List<Teacher> teachers = this.lambdaQuery().like(Teacher::getName, dto.getName())         .orderByDesc(Teacher::getName).list();     return teacherStruct.modelToVO(teachers);   } 复制代码

执行结果如下:

{ "data": [ { "name": "hresh2", "age": 46, "address": null }, { "name": "hresh1", "age": 46, "address": null }, { "name": "hresh0", "age": 46, "address": null }, { "name": "hresh", "age": 44, "address": "湖北大学" } ], "code": "200", "message": "操作成功", "success": true } 复制代码

一对多查询

比如说我们定义的 User 和 Job 类,存在着一对多的关系,所以查询 User 信息时,还需要返回关联的 Job 数据。

关于 Mybatis 一对多、多对一处理手段,可以参考我之前的文章。

本项目采用的是结果嵌套处理方式。

StudentMapper.xml 内容如下:

<?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.msdn.orm.hresh.mapper.StudentMapper">   <resultMap id="studentResultMap" type="com.msdn.orm.hresh.model.Student">     <id column="id" property="id"/>     <result column="name" property="name"/>     <result column="teacher_id" property="teacherId"/>     <result column="address" property="address"/>     <result column="created_date" property="createdDate"/>     <result column="last_modified_date" property="lastModifiedDate"/>     <result column="del_flag" property="delFlag"/>     <result column="create_user_code" property="createUserCode"/>     <result column="create_user_name" property="createUserName"/>     <result column="last_modified_code" property="lastModifiedCode"/>     <result column="last_modified_name" property="lastModifiedName"/>     <result column="version" property="version"/>   </resultMap> </mapper> 复制代码

TeacherMapper.xml 文件

<?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.msdn.orm.hresh.mapper.TeacherMapper">   <resultMap id="teacherResultMap" type="com.msdn.orm.hresh.model.Teacher">     <id column="id" property="id"/>     <result column="name" property="name"/>     <result column="age" property="age"/>     <result column="address" property="address"/>     <result column="created_date" property="createdDate"/>     <result column="last_modified_date" property="lastModifiedDate"/>     <result column="del_flag" property="delFlag"/>     <result column="create_user_code" property="createUserCode"/>     <result column="create_user_name" property="createUserName"/>     <result column="last_modified_code" property="lastModifiedCode"/>     <result column="last_modified_name" property="lastModifiedName"/>     <result column="version" property="version"/>   </resultMap>   <resultMap id="teacherResultMap2" type="com.msdn.orm.hresh.model.Teacher"     extends="com.msdn.orm.hresh.mapper.TeacherMapper.teacherResultMap">     <collection property="students"       resultMap="com.msdn.orm.hresh.mapper.StudentMapper.studentResultMap"       columnPrefix="st_"/>   </resultMap>   <resultMap id="teacherVoResultMap" type="com.msdn.orm.hresh.vo.TeacherVO"     extends="com.msdn.orm.hresh.mapper.TeacherMapper.teacherResultMap">     <collection property="studentVOS"       resultMap="com.msdn.orm.hresh.mapper.StudentMapper.studentResultMap"       columnPrefix="st_"/>   </resultMap>   <select id="queryList" resultMap="teacherVoResultMap">     SELECT t.*,     st.name st_name,     st.address st_address     FROM     teacher t     LEFT JOIN student st ON t.id = st.teacher_id     <where>       <if test="name!=null and name!=''">         and t.name like concat('%',#{name},'%')       </if>       <if test="address != null and address !=''">         and t.address like concat('%',#{address},'%')       </if>     </where>   </select> </mapper> 复制代码

关于上述 xml 配置,如果服务层获取到返回结果后,不需要其他业务操作,可以直接获取 UserVO,反之,我们需要查询得到 User,处理完其他操作后,再转换为 UserVO。userVoResultMap 对应 UserVO 返回结果,userResultMap2 对应 User 结果。我们最终获取的是 UserVO,可以直接返回给前端。

对应的 UserMapper 文件

@Repository public interface TeacherMapper extends BaseMapper<Teacher> {   List<TeacherVO> queryList(TeacherDTO dto); } 复制代码

我们修改 UserService 中的查询方法如下:

public List<TeacherVO> queryList(TeacherDTO dto) {   return teacherMapper.queryList(dto);   //    List<Teacher> teachers = teacherMapper.queryList(dto);   //    return teacherStruct.modelToVO(teachers); } 复制代码

同时在 application.yml 文件中打开 SQL 输出配置:

mybatis-plus:   mapper-locations: classpath:mapper/*Mapper.xml   configuration:     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   type-aliases-package: com.msdn.orm.hresh.model 复制代码

调用接口,可以发现控制台输出如下:

==>  Preparing: SELECT t.*, st.name st_name, st.address st_address FROM teacher t LEFT JOIN student st ON t.id = st.teacher_id WHERE t.name like concat('%',?,'%') ==> Parameters: hresh(String) <==    Columns: id, name, age, address, created_date, last_modified_date, del_flag, create_user_code, create_user_name, last_modified_code, last_modified_name, version, st_name, st_address <==        Row: 1572579347365912577, hresh0, 46, 华科, 2022-09-21 21:31:48, 2022-09-21 21:31:48, 0, 1, admin, 1, admin, 1, 小明, 襄阳四中 <==        Row: 1572579347365912577, hresh0, 46, 华科, 2022-09-21 21:31:48, 2022-09-21 21:31:48, 0, 1, admin, 1, admin, 1, 小军, 襄阳四中 <==        Row: 1572579347877617665, hresh1, 46, 华师, 2022-09-21 21:31:48, 2022-09-21 21:31:48, 0, 1, admin, 1, admin, 1, 小红, 襄阳五中 <==        Row: 1572579347877617665, hresh1, 46, 华师, 2022-09-21 21:31:48, 2022-09-21 21:31:48, 0, 1, admin, 1, admin, 1, 小杰, 襄阳五中 <==        Row: 1572578485562318850, hresh, 44, 湖北大学, 2022-09-21 21:28:23, 2022-09-21 21:28:23, 0, 1, admin, 1, admin, 1, null, null <==        Row: 1572579347881811969, hresh2, 46, 华中, 2022-09-21 21:31:48, 2022-09-21 21:31:48, 0, 1, admin, 1, admin, 1, null, null <==      Total: 6 复制代码

返回结果为:

{ "data": [ { "name": "hresh0", "age": 46, "address": "华科", "studentVOS": [ { "name": "小明", "teacherId": null, "address": "襄阳四中" }, { "name": "小军", "teacherId": null, "address": "襄阳四中" } ] }, { "name": "hresh1", "age": 46, "address": "华师", "studentVOS": [ { "name": "小红", "teacherId": null, "address": "襄阳五中" }, { "name": "小杰", "teacherId": null, "address": "襄阳五中" } ] }, { "name": "hresh", "age": 44, "address": "湖北大学", "studentVOS": [] }, { "name": "hresh2", "age": 46, "address": "华中", "studentVOS": [] } ], "code": "200", "message": "操作成功", "success": true } 复制代码

Swagger

启动项目后,访问 swagger,页面展示如下:

swagger

总结

不重复总结了,该说的在上篇文章结尾已经说完了,本文就是把 Mybatis 换成 MybatisPlus,这两种框架本来就比较类似,部分代码是可以复用的,所以如果你完全掌握了上篇文章中提及的知识点,学习本篇内容就比较轻松了。

那么我们就期待下一篇,看看如何玩转 Spring Data JPA 。

感兴趣的朋友可以去我的 Github 下载相关代码,如果对你有所帮助,不妨 Star 一下,谢谢大家支持!


作者:hresh
链接:https://juejin.cn/post/7168731998549671967


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐