MybatisPlus 篇-MybatisPlus 依赖引入与基础使用(核心功能的使用、BaseMapper 接口中方法的使用、IService 接口中方法的使用、分页插件、代码自动生成等等扩展功能)
在日常开发中应该能发现,单表的 CRUD 功能代码重复度很高,也没有什么难度。而这部分代码量往往比较大,开发起来比较费时。因此,目前企业中都会使用一些组件来简化或省略单表的 CRUD 开发工作。目前在国内使用较多的一个组件就是 MybatisPlus。MyBatis-Plus 🚀 为简化开发而生为了简化单表 CRUD,MybatisPlus 提供了一个基础的 BaseMapper 基类,其中已经
🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
文章目录
1.0 MybatisPlus 概述
在日常开发中应该能发现,单表的 CRUD 功能代码重复度很高,也没有什么难度。而这部分代码量往往比较大,开发起来比较费时。
因此,目前企业中都会使用一些组件来简化或省略单表的 CRUD 开发工作。目前在国内使用较多的一个组件就是 MybatisPlus 。
官方网站如下:MyBatis-Plus 🚀 为简化开发而生
2.0 MybatisPlus 依赖引入
MybatisPlus 提供了 starter,实现了自动 Mybatis 以及 MybatisPlus 的自动装配功能,坐标如下:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version> </dependency>
注意:
如果出现以下错误信息的话:
Bean named 'ddlApplicationRunner' is expected to be of type 'org.springframework.boot. Runner' but was actually of type 'org.springframework.beans.factory.support.NullBean'
at org.springframework.beans.factory.support.AbstractBeanFactory.adaptBeanInstance(AbstractBeanFactory.java:410) ~[spring-beans-6.1.3.jar:6.1.3]
原因是 pom 文件中的配置不兼容导致:(以下是错误的、buji)
解决方法:
将 parent 版本换成 3.1.7,对应着 MybatisPlus 的版本为:3.5.5
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.7</version> <relativePath/> <!-- lookup parent from repository --> </parent>
最终,项目的依赖如下:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>MP</artifactId> <version>0.0.1-SNAPSHOT</version> <name>MP</name> <description>MP</description> <url/> <licenses> <license/> </licenses> <developers> <developer/> </developers> <scm> <connection/> <developerConnection/> <tag/> <url/> </scm> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.11</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
2.1 定义 Mapper
为了简化单表 CRUD,MybatisPlus 提供了一个基础的 BaseMapper 基类,其中已经实现了单表的 CRUD:
代码如下:
@Mapper public interface UserMapper extends BaseMapper<User> { }
BaseMapper 实现的方法:
测试:
使用 BaseMapper 基类中的方法对 User 用户表进行增删改查:
//首先引入依赖 @Autowired private UserMapper userMapper; //测试 @Test void contextLoads() { //1.根据主id查询用户信息 User user = userMapper.selectById(1L); System.out.println("user = " + user); // 2.增加用户信息 User u = new User(); u.setId(10L); u.setUsername("xbs"); u.setPassword("123456"); u.setPhone("123456789"); u.setInfo("{\"age\": 18}"); u.setBalance(100); u.setStatus(1); int insert = userMapper.insert(u); if (insert > 0){ System.out.println("插入成功"); }else { System.out.println("插入失败"); } //3.根据id修改用户信息 User uu = new User(); uu.setId(10L); uu.setStatus(0); int updateById = userMapper.updateById(uu); if (updateById > 0){ System.out.println("修改成功"); }else { System.out.println("修改失败"); } //4.删除id为10的用户 int deleteById = userMapper.deleteById(10L); if (deleteById > 0){ System.out.println("删除成功"); }else { System.out.println("删除失败"); } }
执行结果:
只需要继承 BaseMapper 就能省去所有的单表 CRUD,是不是非常简单!
2.2 常见注解
在刚刚的入门案例中,我们仅仅引入了依赖,继承了 BaseMapper 就能使用 MybatisPlus,非常简单。但是问题来了: MybatisPlus 如何知道我们要查询的是哪张表?表中有哪些字段呢?
大家回忆一下,UserMapper 在继承 BaseMapper 的时候指定了一个泛型:
MybatisPlus 就是根据实体的信息来推断出表的信息,从而生成 SQL 的。默认情况下:
1)MybatisPlus 会把实体的类名驼峰转下划线作为表名。
2)MybatisPlus 会把实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型。
3)MybatisPlus 会把名为 id 的字段作为主键。
但很多情况下,默认的实现与实际场景不符,因此 MybatisPlus 提供了一些注解便于我们声明表信息。
比如说 User 类:
@Data @AllArgsConstructor @NoArgsConstructor @Builder public class User { private Long id; private String username; private String password; private String phone; private String info; private Integer status; private Integer balance; private LocalDateTime createTime; private LocalDateTime updateTime; }
对应着数据库的 User 表:
但很多情况下,默认的实现与实际场景不符,因此 MybatisPlus 提供了一些注解便于我们声明表信息:
1)@TableName 注解:
表名注解,标识实体类对应的表,最常用的方法是指定对应数据库的表名。比如说,表名为 "tb_user",且当前为 User 类,为了实现 Java 的 User 类与数据库中的 "tb_user" 表对应,需要在实体类 User 加上 @TableName 注解,value = "tb_user":
TableName 注解除了指定表名以外,还可以指定很多其它属性。
2)@TableId 注解:
主键注解,标识实体类中的主键字段。
TableId 注解支持两个属性:
支持的类型有:
举个例子:
3)@TableFiled 注解:
普通字段注解。一般情况下我们并不需要给字段添加 @TableFiled 注解,一些特殊情况除外:
- 成员变量名与数据库字段名不一致
- 成员变量是以 isxxx 命名,按照 JavaBean 的规范,MybatisPlus 识别字段时会把 is 去除,这就导致与数据库不符。
- 成员变量名与数据库一致,但是与数据库的关键字冲突。使用 @TableFiled 注解给字段名添加转义字符:``。
@TableName("tb_user") public class User { @TableId private Long id; private String name; private Integer age; @TableField("isMarried") private Boolean isMarried; @TableField("concat") private String concat; }
2.3 常见配置
MybatisPlus 也支持基于 yaml 文件的自定义配置,大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:
1)实体类的别名扫描包
2)全局 id 类型
mybatis-plus: type-aliases-package: com.example.mp global-config: db-config: id-type: auto # 全局id类型为自增长
3)需要注意的是,MyBatisPlus 也支持手写 SQL 的,而 mapper 文件的读取地址可以自己配置:
mybatis-plus: mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。
可以看到默认值是 classpath*:/mapper/**/*.xml,也就是说我们只要把 mapper.xml 文件放置这个目录下就一定会被加载。
例如:
4)开启批量新增机制
修改项目中的 application.yml 文件,在 jdbc 的 url 后面添加参数 &rewriteBatchedStatements=true:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true driver-class-name: com.mysql.cj.jdbc.Driver username: root password: MySQL123
3.0 核心功能
之前案例中都是以 id 为条件的简单 CRUD,一些复杂条件的 SQL 语句就需要用到一些高级的功能了。
3.1 条件构造器
除了新增之外,修改、删除、查询的 SQL 语句都需要指定 where 条件。因此 BaseMapper 中提供了相关方法除了 id 作为 where 条件以外,还支持更加复杂的 where 条件。
参数中的 Wrapper 就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
Wrapper 的子类 AbstractWapper 提供了 SQL 语句 where 中包含的所有条件构造方法:
而 QueryWrapper 在 AbstractWrapper 的基础上拓展了一个 select 方法,允许指定查询字段:
而 UpdateWrapper 在 AbstractWrapper 的基础上拓展了一个 set 方法,允许指定 SQL 中的 SET 部分:
3.2 QueryWrapper 类
无论是修改、删除、查询,都可以使用 QueryWrapper 来构建查询条件。
举个例子:
1)查询出名字中带 "o" 的,存款大于等于 1000 元的人
代码如下:
//首先引入依赖 @Autowired private UserMapper userMapper; @Test public void textQueryWrapper(){ //查询出名字中带"o"的,存款大于等于1000元的人 QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper .like("username", "o") .ge("balance", 1000); List<User> users = userMapper.selectList(userQueryWrapper); System.out.println("users = " + users); }
执行结果:
2)更新用户名为 Jack 的用户的余额为 2000
代码如下:
//首先引入依赖 @Autowired private UserMapper userMapper; @Test public void updateBatchByName(){ UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>(); userUpdateWrapper.set("balance",2000) .eq("username","Jack"); userMapper.update(null, userUpdateWrapper); }
执行结果:
3.3 UpdateWrapper 类
基于 BaseMapper 中的 update 方法更新时只能直接赋值,对于一些复杂的需求就难以实现。
例如:
更新 id 为 1,2,3 的用户的余额,扣 200
代码如下:
@Test void testUpdateWrapper() { List<Long> ids = List.of(1L, 2L, 4L); // 1.生成SQL UpdateWrapper<User> wrapper = new UpdateWrapper<User>() .setSql("balance = balance - 200") // SET balance = balance - 200 .in("id", ids); // WHERE id in (1, 2, 4) // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据, // 而是基于UpdateWrapper中的setSQL来更新 userMapper.update(null, wrapper); }
3.4 LambdaQueryWrapper 类
无论是 QueryWrapper 还是 UpdateWrapper 在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?
其中一种办法是基于变量的 getter 方法结合反射技术。因此我们只要将条件对应的字段的 getter 方法传递给 MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用 JDK8 中的方法引用和 Lambda 表达式。
因此 MybatisPlus 又提供了一套基于 Lambda 的 Wrapper,包含两个:LambdaQueryWrapper、LambdaUpdateWrapper 分别对应 QueryWrapper 和 UpdateWrapper 。
其使用方式如下:
@Test public void textQueryWrapper(){ //查询出名字中带"o"的,存款大于等于1000员的人 LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>(); userQueryWrapper .select(User::getUsername, User::getPassword, User::getInfo, User::getBalance) .like(User::getUsername, "o") .ge(User::getBalance, 1000); List<User> users = userMapper.selectList(userQueryWrapper); System.out.println("users = " + users); }
@Test public void updateBatchByName(){ LambdaUpdateWrapper<User> userUpdateWrapper = new LambdaUpdateWrapper<>(); userUpdateWrapper.set(User::getBalance,2000) .eq(User::getUsername,"Jack"); userMapper.update(null, userUpdateWrapper); }
3.5 自定义 SQL
在演示 UpdateWrapper 的案例中,我们在代码中编写了更新的 SQL 语句:
这种写法在某些企业也是不允许的,因为 SQL 语句最好都维护在持久层,而不是业务层。就当前案例来说,由于条件是 in 语句,只能将 SQL 写在 Mapper.xml 文件,利用 foreach 来生成动态 SQL。 这实在是太麻烦了。假如查询条件更复杂,动态 SQL 的编写也会更加复杂。
所以,MybatisPlus 提供了自定义 SQL 功能,可以让我们利用 Wrapper 生成查询条件,再结合 Mapper.xml 编写 SQL 。
简单来说,就是使用 LambdaQueryWrapper 类编写 SQL 语句中的条件,而手动编写 SQL 复杂的语句。
代码如下:
//首先引入依赖 @Autowired private UserMapper userMapper; @Test public void UpdateBalanceByIds(){ //首先使用LambdaUpdateWrapper来构造条件 LambdaUpdateWrapper<User> userUpdateWrapper = new LambdaUpdateWrapper<>(); userUpdateWrapper .in(User::getId, CollUtil.newArrayList(1L, 2L, 3L)); userMapper.updateBalanceByIds(100, userUpdateWrapper); }
Mapper 层:
@Mapper public interface UserMapper extends BaseMapper<User> { void updateBalanceByIds(@Param("money") int balance, @Param("ew") LambdaUpdateWrapper<User> userUpdateWrapper); }
UserMapper.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.example.mp.Mapper.UserMapper"> <update id="updateBalanceByIds"> UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment} </update> </mapper>
注意,在 Mapper 层传入参数时,需要指定 @Param("ew") 对应 LambdaUpdateWrapper 实体类,而且拼写完自定义的 SQL 后,紧接着加上 "${ew.customSqlSegment}" 来引用 SQL 条件语句。
4.0 Service 接口
MybatisPlus 不仅提供了 BaseMapper,还提供了通用的 Service 接口及默认实现,封装了一些常用的 service 模板方法。 通用接口为 IService ,默认实现为 ServiceImpl,其中封装的方法可以分为以下几类:
save:新增
remove:删除
update:更新
get:查询单个结果
list:查询集合结果
count:计数
page:分页查询
1)新增方法:
save 是新增单个元素
saveBatch 是批量新增
saveOrUpdate 是根据 id 判断,如果数据存在就更新,不存在则新增
saveOrUpdateBatch 是批量的新增或修改
2)删除方法:
removeById:根据 id 删除
removeByIds:根据 id 批量删除
removeByMap:根据 Map 中的键值对为条件删除
remove(Wrapper<T>):根据 Wrapper 条件删除
3)修改方法:
updateById:根据 id 修改
update(Wrapper<T>):根据 UpdateWrapper 修改,Wrapper 中包含 set 和 where 部分
update(T,Wrapper<T>):按照 T 内的数据修改与 Wrapper 匹配到的数据
updateBatchById:根据 id 批量修改
4)查询方法:
get 方法:
getById:根据 id 查询 1 条数据
getOne(Wrapper<T>):根据 Wrapper 查询 1 条数据
getBaseMapper:获取 Service 内的 BaseMapper 实现,某些时候需要直接调用 Mapper 内的自定义 SQL 时可以用这个方法获取到 Mapper
List 方法:
listByIds:根据 id 批量查询
list(Wrapper<T>):根据 Wrapper 条件查询多条数据
list():查询所有
count 方法:
count():统计所有数量
count(Wrapper<T>):统计符合 Wrapper 条件的数据数量
getBaseMapper 方法:
4.1 基本用法
首先,定义 UserService,继承 IService:
import com.baomidou.mybatisplus.extension.service.IService; import com.example.mp.domain.po.User; public interface UserService extends IService<User> { }
然后,编写 UserServiceImpl 类,继承 ServiceImpl,实现 UserService:
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.mp.Mapper.UserMapper; import com.example.mp.domain.Service.UserService; import com.example.mp.domain.po.User; import org.springframework.stereotype.Service; @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
代码如下:
//首先引入依赖 @Autowired private UserServiceImpl userService; /** * 添加用户 */ @Test public void addUser(){ User user = new User(); user.setUsername("xbs"); user.setPassword("123456"); user.setInfo("{\"age\": 18}"); userService.save(user); } /** * 根据ID删除用户 */ @Test public void deleteUser(){ userService.removeById(13); } /** * 查询用户 */ @Test public void selectUser(){ //查询所有用户 List<User> users = userService.list(); System.out.println("users = " + users); //根据id查询用户 User user = userService.getById(1L); System.out.println("user = " + user); //根据id批量查询用户 List<User> userList = userService.listByIds(CollUtil.newArrayList(1L, 2L, 3L)); System.out.println("userList = " + userList); }
4.2 Lambda 使用
IService 中还提供了 Lambda 功能来简化我们的复杂查询及更新功能。
举个例子:
实现一个根据复杂条件查询用户的接口,查询条件如下:
name:用户名关键字,可以为空。
status:用户状态,可以为空。
minBalance:最小余额,可以为空。
maxBalance:最大余额,可以为空。
代码实现:
//首先引入依赖 @Autowired private UserServiceImpl userService; @Test public void testLambda(){ textLambda("Jack",1,100,2000); } public void textLambda(String username,Integer status,Integer minBalance,Integer maxBalance){ List<User> list = userService.lambdaQuery() .like(username != null,User::getUsername, username) .eq(status != null,User::getStatus, status) .ge(minBalance != null,User::getBalance, minBalance) .le(maxBalance != null,User::getBalance, maxBalance) .list(); for (User user : list){ System.out.println("user = " + user); } }
执行结果:
可以发现 lambdaQuery 方法中除了可以构建条件,还需要在链式编程的最后添加一个 list(),这是在告诉 MP 我们的调用结果需要是一个 list 集合。这里不仅可以用 list(),可选的方法有:
1)one():最多1个结果。
2)list():返回集合结果。
3)count():返回计数结果 MybatisPlus 会根据链式编程的最后一个方法来判断最终的返回结果。
与 lambdaQuery 方法类似,IService 中的 lambdaUpdate 方法可以非常方便的实现复杂更新业务。
改造根据 ID 修改用户余额,如果扣减后余额为 0,则将用户 status 修改为冻结状态。也就是说我们在扣减用户余额时,需要对用户剩余余额做出判断,如果发现剩余余额为 0,则应该将 status 修改为 2,这就是说 update 语句的 set 部分是动态的。
举个例子:
//首先引入依赖 @Autowired private UserServiceImpl userService; @Test public void textLambdaUpdate(){ lambdaUpdate(0, "Jack"); } private void lambdaUpdate(Integer balance,String username){ boolean update = userService.lambdaUpdate() .set(User::getBalance, balance) .set(balance == 0,User::getStatus,2) .eq(User::getUsername, username) .update(); if (update){ System.out.println("修改成功"); }else { System.out.println("修改失败"); } }
执行结果:
4.3 静态工具
有的时候 Service 之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus 提供一个静态工具类:Db,其中的一些静态方法与 IService 中方法签名基本一致,也可以帮助我们实现 CRUD 功能:
简单来说,就是静态版本的 Lambda 类,以上的方法也十分熟悉,相比于简单的 lambdaQuery 或者 lambdaUpdate 只不过需要在参数上加上具体操作的实体类字节码,比如说 User.class 。
举个例子:
1)使用 Db.lambdaQuery 静态工具:
代码如下:
@Test public void testDbLambdaQuery(){ lambdaQuery("Jack",1,100,2000); } private void lambdaQuery(String username,Integer status,Integer minBalance,Integer maxBalance){ List<User> list = Db.lambdaQuery(User.class) .eq(User::getUsername, username) .eq(User::getStatus, status) .ge(User::getBalance, minBalance) .le(User::getBalance, maxBalance) .list(); for(User user : list){ System.out.println("user = " + user); } }
执行结果:
2)使用 Db.lambdaUpdate 静态工具:
代码如下:
@Test public void testDbLambdaUpdate(){ lambdaUpdate("Jack",2); } private void lambdaUpdate(String username,Integer status){ boolean update = Db.lambdaUpdate(User.class) .set(User::getStatus, status) .like(User::getUsername, username) .update(); if (update){ System.out.println("修改成功"); }else { System.out.println("修改失败"); } }
执行结果:
5.0 分页插件
在未引入分页插件的情况下,MybatisPlus 是不支持分页功能的,Iservice 和 BaseMapper 中的分页方法都无法起效。
配置分页插件代码如下:
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; @Configuration public class PageConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
5.1 分页插件的基本使用
配置完分页插件之后,在 Iservice 和 BaseMapper 中的 Page 方法就可以使用了。
5.1.1 BaseMapper 接口中的 Page 方法
1)最基本的分页查询,不带条件
//首先引入依赖 @Autowired private UserMapper userMapper; @Test public void userMapperPage(){ //首先定义Page对象 Page<User> page = Page.of(1, 2); //简单的分页查询,没有条件的查询 Page<User> userPage = userMapper.selectPage(page, null); //查询的总页数 System.out.println("总页数 = " + userPage.getPages()); //查询的总数量 System.out.println("总条数 = " + userPage.getTotal()); //查询的具体内容 List<User> records = userPage.getRecords(); for (User user:records){ System.out.println("user = " + user); } }
执行结果:
2)有需求的分页查询
//首先引入依赖 @Autowired private UserMapper userMapper; @Test public void userMapperPageWithCondition(){ //Page对象 Page<User> page = Page.of(1, 2); //根据条件进行查询 LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<User>() .ge(User::getBalance,1000); //执行查询 Page<User> userPage = userMapper.selectPage(page, qw); //查询的总页数 System.out.println("总页数 = " + userPage.getPages()); //查询的总数量 System.out.println("总条数 = " + userPage.getTotal()); //查询的具体内容 List<User> records = userPage.getRecords(); for (User user:records){ System.out.println("user = " + user); } }
执行结果:
5.1.2 Iservice 接口中的 Page 方法:
1)最基本的分页查询,没有条件的查询。
//首先引入依赖 @Autowired private UserServiceImpl userService; @Test public void userServicePage(){ Page<User> page = Page.of(1, 2); userService.page(page, null); //查询的总页数 long pages = page.getPages(); System.out.println("总页数 = " + pages); //查询的总条数 long total = page.getTotal(); System.out.println("总条数 = " + total); //查询的具体内容 List<User> records = page.getRecords(); for (User user: records){ System.out.println("user = " + user); } }
执行结果:
2)有条件的分页查询
可以使用基本的 Iservice 中的 Page 方法,然后传入两个参数,一个参数是 Page 对象、另一个是 LambdaQueryWrapper 条件对象。
//首先引入依赖 @Autowired private UserServiceImpl userService; @Test public void userServiceWithCondition(){ //首先创建分页实体类 Page<User> page = Page.of(1, 2); //根据条件进行查询 LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<User>() .ge(User::getBalance, 1000); userService.page(page, qw); //查询的总页数 long pages = page.getPages(); System.out.println("总页数 = " + pages); //查询的总条数 long total = page.getTotal(); System.out.println("总条数 = " + total); //查询的具体内容 List<User> records = page.getRecords(); for (User user: records){ System.out.println("user = " + user); } }
执行结果:
3)使用 LambdaQuery 进行分页查询
//首先引入依赖 @Autowired private UserServiceImpl userService; @Test public void userServiceWithConditionLambda(){ Page<User> page = Page.of(1, 2); userService.lambdaQuery() .ge(User::getBalance, 1000) .page(page); //查询的总页数 long pages = page.getPages(); System.out.println("总页数 = " + pages); //查询的总条数 long total = page.getTotal(); System.out.println("总条数 = " + total); //查询的具体内容 List<User> records = page.getRecords(); for (User user: records){ System.out.println("user = " + user); } }
执行结果:
6.0 扩展功能
6.1 代码生成
在使用 MybatisPlus 以后,基础的 Mapper、Service、PO 代码相对固定,重复编写也比较麻烦。因此 MybatisPlus 官方提供了代码生成器根据数据库表结构生成 PO、Mapper、Service 等相关代码。只不过代码生成器同样要编码使用,也很麻烦。
这里推荐大家使用一款 MybatisPlus 的插件,它可以基于图形化界面完成 MybatisPlus 的代码生成,非常简单。
在 Idea 的 plugins 市场中搜索并安装 MyBatisPlus 插件:
6.1.1 具体使用
1)首先需要配置数据库地址,在 Idea 顶部菜单中,找到 other,选择 Config Database:
2)在弹出的窗口中填写数据库连接的基本信息:
对于 dbUrl 参照 yml 配置文件:
3)然后再次点击 Idea 顶部菜单中的 other,然后选择 Code Generator:
在弹出的表单中填写信息:
执行效果:
最终,代码自动生成到指定的位置了
6.2 逻辑删除
对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:
1)在表中添加一个字段标记数据是否被删除
2)当删除数据时把标记置为 true
3)查询时过滤掉标记为 true 的数据
一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。为了解决这个问题,MybatisPlus 就添加了对逻辑删除的支持。
接下来,只需要在 application.yml 中配置逻辑删除字段:
mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
方法与普通删除一模一样,但是底层的 SQL 逻辑变了:
综上, 开启了逻辑删除功能以后,我们就可以像普通删除一样做 CRUD,基本不用考虑代码逻辑问题。还是非常方便的。
6.3 通用枚举
User 类中有一个用户状态字段:
像这种字段我们一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是我们数据库采用的是 int 类型,对应的 PO 也是 Integer。因此业务操作时必须手动把枚举与 Integer 转换,非常麻烦。
因此,MybatisPlus 提供了一个处理枚举的类型转换器,可以帮我们把枚举类型与数据库类型自动转换。
1)首先配置枚举处理器:
mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
2)设置枚举类型:
import com.baomidou.mybatisplus.annotation.EnumValue; import lombok.Getter; @Getter public enum UserStatus { NORMAL(1, "正常"), FREEZE(2, "冻结") ; private final int value; private final String desc; UserStatus(int value, String desc) { this.value = value; this.desc = desc; } }
要让 MybatisPlus 处理枚举与数据库类型自动转换,我们必须告诉 MybatisPlus,枚举中的哪个字段的值作为数据库值。
MybatisPlus 提供了 @EnumValue 注解来标记枚举属性,并且,在 StatusEnum枚举中通过 @JsonValue 注解标记 JSON 序列化时展示的字段:
@Getter public enum StatusEnum { NORMAL(1,"正常"), FREEZE(2,"冻结") ; @EnumValue private final Integer value; @JsonValue private final String desc; StatusEnum(Integer value, String desc) { this.value = value; this.desc = desc; } }
测试:
//首先引入依赖 @Autowired private UserServiceImpl userService; @Test public void textStatusEnum(){ List<User> list = userService.lambdaQuery() .eq(User::getStatus, StatusEnum.NORMAL) .list(); for (User user: list){ System.out.println("user = " + user); } }
执行结果:
6.4 JSON 类型处理器
数据库的 user 表中有一个 info 字段,是 JSON 类型:
而目前 User 实体类中却是 String 类型:
这样一来,我们要读取 info 中的属性时就非常不方便。如果要方便获取,info 的类型最好是一个 Map 或者实体类。
而一旦我们把 info 改为对象类型,就需要在写入数据库时手动转为 String,再读取数据库时,手动转换为对象,这会非常麻烦。
因此 MybatisPlus 提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理 JSON 就可以使用 JacksonTypeHandler 处理器。
6.4.1 具体使用
1)首先,我们定义一个单独实体类来与 info 字段的属性匹配:
package com.itheima.mp.domain.po; import lombok.Data; @Data public class UserInfo { private Integer age; private String intro; private String gender; }
2)接下来,将 User 类的 info 字段修改为 UserInfo 类型,并声明类型处理器:
测试:
可以看到 info 字段是 UserInfo 类型对象,而不是字符串了。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)