认识MyBatis-Plus

MyBatis Plus 对 MyBatis 它 Plus 在哪?

首先,MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

而且,MyBatis-Plus 它是无侵入的,只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

支持的数据库也对 MyBatis 进行了增强,而且它是国人开发的,所以也对国产数据库有所支持

image-20260323170954693

任何能使用 MyBatis 进行增删改查,并且支持标准 SQL 的数据库应该都在 MyBatis-Plus 的支持范围内,具体支持情况如上。

更多特性如下

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

最重要的是 MP 只是 “增强” 而非 “替代”,它完全保留了 MyBatis 的所有特性:

  • 仍可编写 Mapper XML,实现复杂联表查询(MP 不处理复杂联表,需原生 MyBatis 补充);
  • 仍可使用 MyBatis 的注解(@Select@Insert 等);
  • 仍可自定义拦截器、插件等;
  • 原有 MyBatis 项目接入 MP 无需修改现有代码,只需引入依赖、配置即可。

这就是向下兼容

如何理解 MyBatis Plus 的优势

无代码生成的 CRUD 封装是 MyBatis Plus 最核心的内容之一,我们都知道,原生 MyBatis 实现单表 CRUD 需要

  • 编写 Mapper 接口,定义方法
  • 编写 Mapper XML 或者使用注解,都是写 SQL 语句

MyBatis Plus 提供了 BaseMapper<T> 接口,内置了 17+ 个常用 CRUD 方法,只需让自定义 Mapper 继承它,无需写任何 SQL 即可实现单表操作。很类似 Hibernate 的感觉

可以看一下如下例子就理解了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 实体类(对应数据库表 user)
@Data
@TableName("user") // 表名映射(默认类名转下划线,可省略)
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
private Integer age;
private String email;
}

// Mapper 接口(只需继承 BaseMapper)
public interface UserMapper extends BaseMapper<User> {
// 无需写任何方法,直接拥有 CRUD 能力
}

// 业务层使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;

public void testCRUD() {
// 新增
User user = new User(null, "张三", 20, "zhangsan@test.com");
userMapper.insert(user);

// 查询单个(根据ID)
User user1 = userMapper.selectById(1L);

// 查询列表(根据条件)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 20);
List<User> userList = userMapper.selectList(wrapper);

// 更新
User updateUser = new User(1L, "张三更新", 21, "zhangsan@test.com");
userMapper.updateById(updateUser);

// 删除
userMapper.deleteById(1L);
}
}

关键就是那个BaseMapper,它内置方法包括insertdeleteByIddeleteBatchIdsupdateByIdselectByIdselectListselectCount 等,覆盖 90% 以上的单表场景。这样,简单情况下,无需编写任何 XML 或注解 SQL,直接调用方法即可

而且 MyBatis Plus 提供了强大的条件构造器

原生 MyBatis 写动态 SQL 需要在 XML 中用 <if><where><choose> 等标签,代码繁琐且易出错。

MP 提供了 QueryWrapper(普通条件构造)和 LambdaQueryWrapper(类型安全的 Lambda 构造),用链式调用替代 XML 动态 SQL,可读性和可维护性大幅提升。

1
2
3
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三").gt(User::getAge, 18);
List<User> userList = userMapper.selectList(wrapper);

而分页也是 MP 最强大的优势之一了

原生 MyBatis 实现分页需要:

  • 手动写分页 SQL(如 MySQL 的 LIMIT、Oracle 的 ROWNUM
  • 手动查询总条数(额外写 COUNT(*) SQL)
  • 封装分页结果

MP 提供了分页插件 MybatisPlusInterceptor,只需简单配置,即可实现物理分页,且自动返回总条数。使用体验非常接近于 Hibernate 了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 配置分页插件(Spring Boot 方式)
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件(适配 MySQL,其他数据库如 Oracle 需换对应方言)
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

// 分页查询使用
public void testPage() {
// 构造分页条件:第1页,每页10条
Page<User> page = new Page<>(1, 10);
// 构造查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.gt(User::getAge, 18);
// 执行分页查询(自动返回总条数、当前页数据)
Page<User> resultPage = userMapper.selectPage(page, wrapper);

// 获取分页结果
long total = resultPage.getTotal(); // 总条数
List<User> records = resultPage.getRecords(); // 当前页数据
long pages = resultPage.getPages(); // 总页数
}

最后提一嘴这个代码生成器

原生 MyBatis 若要生成实体、Mapper、Service、Controller 等代码,需借助第三方工具(如 MyBatis Generator),配置复杂且生成的代码不够灵活。

MP 提供了 AutoGenerator 代码生成器,支持自定义模板、生成规则,可一键生成实体类,Mapper 接口 + XML,Service 接口 + 实现类,还有额外的 Controller 层,这样,新建表后,几分钟即可生成全套基础代码,避免重复劳动。

其实,我倒是理解为 MP 的核心优势就是再 MyBatis 的基础上,通过一些增强封装,使得 MyBatis 这个半自动 ORM 能够在保持灵活性的基础上,给出类似 Hibernate 的快捷开发的使用体验

伟大

快速开始理解 MyBatis Plus

我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,也是基于 Spring Boot 环境

依赖导入

Spring Boot 自3.5.13开始 可以使用mybatis-plus-spring-boot4的版本

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
<version>3.5.15</version>
</dependency>

因为mybatis-plus-spring启动器自带 MyBatis,所以无需重复引入

而且集成 MyBatis-Plus 非常的简单,和 MyBatis 一样,我们仅需要一些简单的配置即可使用 MyBatis-Plus 的强大功能

1
2
3
4
5
6
7
@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

配置 MyBatis Plus

配置 MyBatis Plus 和 配置 MyBatis 差不多,而且 MP 能够继承 MyBatis 的配置项,只不过多了几个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# MyBatis-Plus 配置
mybatis-plus:
# mapper xml 位置(用 MP 可不用 xml)
mapper-locations: classpath:mapper/*.xml
type-aliases-package: hbnu.project.mybatisplusdemo.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 全局配置
global-config:
db-config:
id-type: auto # 主键自增
logic-delete-field: deleted # 逻辑删除字段名
logic-delete-value: 1 # 已删除
logic-not-delete-value: 0 # 未删除

MP下如何编写实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

/**
* 用户实体
* MP 注解演示:主键、逻辑删除、自动填充
*/
@Data
@TableName("user_plus") // 对应数据库表名
public class User {

// 主键自增
@TableId(type = IdType.AUTO)
private Long id;

// 用户名
@TableField("username")
private String username;

// 年龄
private Integer age;

// 邮箱
private String email;

// 创建时间(插入时自动填充)
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

// 更新时间(插入和更新时自动填充)
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;

// 逻辑删除字段(查询时自动过滤)
@TableLogic
@TableField("deleted")
private Integer deleted;
}

那么,从上面一个例子,我们能发现,MP 下的实体上注解的编写风格一定程度下和 Hibernate 有些相似,其中具体内容,例如相关注解,如下

  • @TableName("表名"):指定实体类对应的数据库表名,如果实体类名 = 表名,可以不写,如果实体类名 ≠ 表名,必须写

  • @TableId:主键注解,标记实体类的主键字段,必须写在主键 id 上,type 主键策略有这样四种

    类型 说明 使用场景
    IdType.AUTO 数据库自增 MySQL 主键 AUTO_INCREMENT
    IdType.INPUT 手动输入 自己 set 主键值
    IdType.ASSIGN_ID 雪花算法(默认) 分布式 ID,Long 类型
    IdType.ASSIGN_UUID UUID 字符串主键
  • @TableField:字段注解,标记实体类属性和数据库表字段的映射关系,当字段名不一致的时候使用这个注解进行关联

    1
    2
    @TableField("username")
    private String username;
    • 当然,这个注解可以达到一个类似 JPA 的审计功能,能够自动填充 创建 / 更新时间 等审计字段

      1
      2
      3
      4
      5
      6
      7
      // 插入时填充
      @TableField(fill = FieldFill.INSERT)
      private LocalDateTime createTime;

      // 插入和更新时都填充
      @TableField(fill = FieldFill.INSERT_UPDATE)
      private LocalDateTime updateTime;
      • 对于 fill 的策略

        FieldFill.INSERT:插入时自动填值

        FieldFill.UPDATE:更新时自动填值

        FieldFill.INSERT_UPDATE:插入 + 更新都填

        但是它必须配合自动填充配置类才能生效

    • 而且,用它也可以进行字段忽略

      1
      2
      @TableField(exist = false)
      private String tempData; // 数据库没有这个字段
  • @TableLogic:逻辑删除注解,标记逻辑删除字段,删除时不是真删,而是把 deleted 改成 1,删除时:不是真删,而是把 deleted 改成 1,查询时自动拼接 deleted=0

    1
    2
    3
    @TableLogic
    @TableField("deleted")
    private Integer deleted;

    这个字段也需要进行配置

    1
    2
    3
    logic-delete-field: deleted
    logic-delete-value: 1
    logic-not-delete-value: 0
  • @Version:乐观锁,和 Hibernate 一样,标记上了,MP 会自动帮你实现乐观锁。

MyBatis-Plus 的配置类

对于上面提到的审计字段的自动填充,需要编写一个配置类,参考如下

因为 MyBatis-Plus 不知道你要填充什么字段、填充什么值。你必须需要一个自定义配置器来定义自动填充规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
* MP 自动填充处理器:createTime、updateTime
* 类似 JPA 的审计字段
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

// 插入时填充
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}

// 更新时填充
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
}
  • 上述配置类,对于如下字段

    1
    2
    private LocalDateTime createTime; // 创建时间
    private LocalDateTime updateTime; // 更新时间

    避免了user.setCreateTime(LocalDateTime.now());的操作,让 MyBatis-Plus 自动帮你赋值,不用手动写。

  • 而对于MetaObjectHandler就是 MP 自动填充的相关接口,实现它获取自动填充的能力

    • strictInsertFill插入时严格填充(严格填充:只有字段为 null 时才填充,不会覆盖你手动设置的值。)
    • strictUpdateFill更新时严格填充
  • 那么,上述配置类什么时候生效呢?

    • 实体类字段上加注解,标记需要自动填充的字段和自动填充的时机,然后调用 MP 的方法,在这里是插入和更新,时候就会执行自动填充相关字段

      1
      2
      3
      4
      5
      @TableField(fill = FieldFill.INSERT)
      private LocalDateTime createTime;

      @TableField(fill = FieldFill.INSERT_UPDATE)
      private LocalDateTime updateTime;

而对于分页查询,你也需要编写一个配置类来让 MyBatis-Plus 的分页查询功能生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.baomidou.mybatisplus.annotation.DbType;
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;

/**
* MyBatis-Plus 配置类
* 注册分页插件
*/
// 分页必须加 @Configuration 注册插件
@Configuration
public class MyBatisPlusConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

如果不写这个配置类,Page<User> page = new Page<>(1,10);然后userMapper.selectPage(page, null);就不会分页,而是查全表

所以说,MP 的分页功能基于插件机制,不是默认开启的,必须通过插件(拦截器) 实现。

这个插件会拦截你的查询 SQL,自动拼接 LIMIT 关键字,自动计算总条数、总页数,然后只要用到 Page 对象,就会自动分页。

Mapper 层

那么 MyBatis Plus 与 MyBatis 带来的最大差距就是 Mapper 层上的不同

1
2
3
4
5
6
7
8
9
10
11
12
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import hbnu.project.mybatisplusdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
* UserMapper
* 继承 BaseMapper 直接拥有 CRUD,无需写 xml
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 无需写任何方法,MP 自动提供
}

我们看看 BaseMapper 这个接口

image-20260409095108274
  • 可以看到注释的内容,Mapper 接口继承 BaseMapper,什么都不用写,不用 XML,不用写 SQL,不用写方法,就几乎能完成单表所有 CRUD,其中方法在BaseMapper 里面人家都写好了,实现了肯定有其功能。

BaseMapper 是 MyBatis-Plus 提供的一个预置接口。它里面已经定义好了所有单表常用的 CRUD 方法,你只要继承它,就等于拥有了全部方法。打开 BaseMapper 源码你会看到。

BaseMapper<User> 泛型为什么必须写?泛型 <User> 的作用

  • 告诉 MP 对应的数据库表:MP 通过实体类上的 @TableName("user") 找到表名
  • 告诉 MP 字段名和类型:通过实体类的 @TableField 映射列名

然后,MP 就能自动生成 SQL,不用你写,MP 根据泛型自动拼接 SQL

使用 MyBatis-Plus 开发 Mapper 层,必须继承 BaseMapper<T>,然后涉及到条件的,使用条件构造器 QueryWrapper/LambdaQueryWrapper,这样复杂查询也不用写 SQL,例如

1
2
3
4
5
6
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三")
.ge("age", 18)
.orderByDesc("id");

List<User> list = userMapper.selectList(wrapper);

Service 层

使用 MyBatis Plus 编写 Service 层的时候也有一些不一样的地方,虽然乍一看没什么

这里只讲比较简单的内容,详细的持久层接口的相关内容可以去文档查看,https://baomidou.com/guides/data-interface/#chain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.baomidou.mybatisplus.extension.service.IService;
import hbnu.project.mybatisplusdemo.entity.User;
import java.util.List;

/**
* UserService 接口
*/
public interface UserService extends IService<User> {

/**
* 按年龄查询用户
*/
List<User> listByAge(Integer age);

/**
* 按用户名模糊查询
*/
List<User> listByUsername(String username);
}

上述接口的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import hbnu.project.mybatisplusdemo.entity.User;
import hbnu.project.mybatisplusdemo.mapper.UserMapper;
import hbnu.project.mybatisplusdemo.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;

/**
* UserService 实现
* 继承 ServiceImpl 直接拥有大量常用方法
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

/**
* 按年龄精确查询
*/
@Override
public List<User> listByAge(Integer age) {
// Lambda 条件构造器(防字段名写错)
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getAge, age);
return baseMapper.selectList(wrapper);
}

/**
* 用户名模糊查询
*/
@Override
public List<User> listByUsername(String username) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getUsername, username);
return baseMapper.selectList(wrapper);
}
}

MP 对 Service 业务层 做了极大简化,通过两个接口和一个实现类,CURD方法就几乎不怎么写了,只需要写自定义业务逻辑就可以。

  • Mapper 层:继承 BaseMapper就能拥有基础单表 CRUD
  • Service 接口:继承 IService<T>来定义业务方法
  • Service 实现类:继承 ServiceImpl<M, T>就能自动实现 IService 所有方法 + 自定义业务逻辑

Service 接口层的extends IService<User>是 MP 业务层核心接口,这样能自动继承 30 多个通用的业务方法,可以在这里扩展自定义业务方法,我们来看看 IService 里面的源码

image-20260411231108805

接着实现类extends ServiceImpl<UserMapper, User>,这又是什么?它是MP 提供的通用 Service 实现类,它能自动实现 IService 所有方法,你完全不用写,而且比较关键的是ServiceImpl自动注入baseMapper,等价于

1
2
@Autowired
private UserMapper baseMapper;

ServiceImpl 的源码也比较特殊,它内部也没有任何内容,也是通过继承和实现接口来实现的功能继承

image-20260411231500875

所以你可以直接用 baseMapper 调用 Mapper 层所有方法。而且在这里,条件构造器也依旧有效,一般情况下我们用LambdaQueryWrapper

而且我们根据上述代码可以看到,IService<T>作为接口,定义能干什么,所以需要 Service 的接口层来继承,如果不继承 IService,你就一个方法都没有,ServiceImpl<M,T>作为模板实现类,已经把方法全部写好了,继承它,你的 Service 层的实现类就能自动实现 IService 接口里的所有方法,还能自动注入 Mapper

为什么不直接实现 IService,非要继承 ServiceImpl?

因为如果你不继承 ServiceImpl,而是自己 implements IService<User>,那不就回来了,一切内容你还要自己写

Controller 层

对于 Controller 层,就没什么太多不一样的了

可以看到,只使用了比较少的代码,无论是 Mapper 中还是 Service 中,实现了各种接口,MP 自己就自动帮你做了很多比较基础和简单的查询内容,而往往这些内容占据了日常开发的很大一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import hbnu.project.mybatisplusdemo.entity.User;
import hbnu.project.mybatisplusdemo.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;

/**
* 用户控制层
* 演示 MP 所有核心常用功能
*/
@RestController
@RequestMapping("/user")
public class UserController {

@Resource
private UserService userService;

// ==================== 基础 CRUD ====================

/**
* 新增用户
*/
@PostMapping("/add")
public String add(@RequestBody User user) {
boolean save = userService.save(user);
return save ? "新增成功" : "新增失败";
}

/**
* 根据ID删除(逻辑删除)
*/
@DeleteMapping("/delete/{id}")
public String delete(@PathVariable Long id) {
boolean remove = userService.removeById(id);
return remove ? "删除成功" : "删除失败";
}

/**
* 修改用户
*/
@PutMapping("/update")
public String update(@RequestBody User user) {
boolean update = userService.updateById(user);
return update ? "修改成功" : "修改失败";
}

/**
* 根据ID查询
*/
@GetMapping("/get/{id}")
public User getById(@PathVariable Long id) {
return userService.getById(id);
}

/**
* 查询所有
*/
@GetMapping("/list")
public List<User> list() {
return userService.list();
}

// ==================== MP 高级查询 ====================

/**
* 按年龄查询
*/
@GetMapping("/list/age/{age}")
public List<User> listByAge(@PathVariable Integer age) {
return userService.listByAge(age);
}

/**
* 用户名模糊查询
*/
@GetMapping("/list/username")
public List<User> listByUsername(@RequestParam String username) {
return userService.listByUsername(username);
}

/**
* 分页查询
*/
@GetMapping("/page")
public IPage<User> page(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
Page<User> page = new Page<>(current, size);
return userService.page(page);
}
}