🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 MybatisPlus 概述

        2.0 MybatisPlus 依赖引入

        2.1 定义 Mapper

        2.2 常见注解

        2.3 常见配置

        3.0 核心功能

        3.1 条件构造器

        3.2 QueryWrapper 类

        3.3 UpdateWrapper 类

        3.4 LambdaQueryWrapper 类

        3.5 自定义 SQL

        4.0 Service 接口

        4.1 基本用法

        4.2 Lambda 使用

        4.3 静态工具

        5.0 分页插件

        5.1 分页插件的基本使用

        5.1.1 BaseMapper 接口中的 Page 方法

        5.1.2 Iservice 接口中的 Page 方法:

        6.0 扩展功能

        6.1 代码生成

        6.1.1 具体使用

        6.2 逻辑删除

        6.3 通用枚举

        6.4 JSON 类型处理器

        6.4.1 具体使用


        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 类型对象,而不是字符串了。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐