该说的在《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='学生'
使用 IDEA 新建一个 Maven 项目,叫做 mybatisplus-springboot。
一些共用的基础代码可以参考上篇文章,这里不做重复介绍,会介绍一些 MybatisPlus 相关的代码。
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.3version>
parent>
<properties>
<java.version>1.8java.version>
<fastjson.version>1.2.73fastjson.version>
<hutool.version>5.5.1hutool.version>
<mysql.version>8.0.19mysql.version>
<mybatis.version>2.1.4mybatis.version>
<mapper.version>4.1.5mapper.version>
<org.mapstruct.version>1.4.2.Finalorg.mapstruct.version>
<org.projectlombok.version>1.18.20org.projectlombok.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>${fastjson.version}version>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>${hutool.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${org.projectlombok.version}version>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plusartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-commonsartifactId>
<version>2.4.6version>
dependency>
<dependency>
<groupId>org.springdocgroupId>
<artifactId>springdoc-openapi-uiartifactId>
<version>1.6.9version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.18version>
dependency>
<dependency>
<groupId>org.mapstructgroupId>
<artifactId>mapstructartifactId>
<version>${org.mapstruct.version}version>
dependency>
<dependency>
<groupId>org.mapstructgroupId>
<artifactId>mapstruct-processorartifactId>
<version>${org.mapstruct.version}version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
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;
}
}
作为其他实体类的父类,封装了所有的公共字段,包括逻辑删除标志,版本号,创建人和修改人信息。相较于 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注解
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
}
修改 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);
}
执行效果如下:
前端参数传递:
{
"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 内容如下:
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 文件
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 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,页面展示如下:
不重复总结了,该说的在上篇文章结尾已经说完了,本文就是把 Mybatis 换成 MybatisPlus,这两种框架本来就比较类似,部分代码是可以复用的,所以如果你完全掌握了上篇文章中提及的知识点,学习本篇内容就比较轻松了。
那么我们就期待下一篇,看看如何玩转 Spring Data JPA 。
感兴趣的朋友可以去我的 Github 下载相关代码,如果对你有所帮助,不妨 Star 一下,谢谢大家支持!