Mybatis-plus

670 阅读3分钟

一、查询

构造器分为QueryWrapper和LambdaQueryWrapper

创建实体类User

package com.system.mybatisplus.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;

@TableName("user") // 指定表名,如果表名和类名一致,可以省略
@Data // 使用Lombok简化开发
public class User implements Serializable { // 实现序列化接口
    @TableId(type = IdType.AUTO)
    private Long id;
    @TableField("name") // 指定表字段名,如果字段名和属性名一致,可以省略
    private String name;
    // @TableField(select = false) // select = false 表示查询时不查询该字段
    private Integer age;
    private String email;
    @TableField(exist = false) // exist = false 表示该字段不是数据库字段,但是可以使用
    private Boolean isOnline;
}

编写mapper接口UserMapper

package com.system.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.system.mybatisplus.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper // 此注解用于标记这是一个mybatis的mapper类,否则会报错,因为没有加@Mapper
public interface UserMapper extends BaseMapper<User> {// 继承BaseMapper
    // 根据名称查询
    User selectByName(@Param("name") String name);
}

编写service及其实现类

package com.system.mybatisplus.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.system.mybatisplus.model.User;
// 需要继承IService
public interface UserService extends IService<User> {

}
package com.system.mybatisplus.service.impl;

import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.system.mybatisplus.mapper.UserMapper;
import com.system.mybatisplus.model.User;
import com.system.mybatisplus.service.UserService;
import org.springframework.stereotype.Service;

@Service // 此注解用于标记这是一个service类,否则会报错,因为没有加@Service
// 此处继承ServiceImpl(ServiceImpl实现了IService接口),同时实现UserService接口
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

1、等值查询

查询条件必须完全匹配才行,可以拼接多个eq

	@Test
    void testEq() {
        // 1、创建条件构造器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 2、设置条件,指定字段名和字段值
        wrapper.eq("name", "jone");
        // 3、执行查询,使用selectOne方法,查询结果只能有一条,否则报错,如果查询结果有多条,使用selectList方法
        System.out.println(userMapper.selectList(wrapper));
    }
	@Test
    void testEq() {
        // 简写
        System.out.println(userMapper.selectList(new QueryWrapper<User>().eq("name", "jone")));
    }

相当于执行了如下SQL

SELECT id,name,age,email FROM user WHERE (name = ?)

可以使用LambdaQueryWrapper

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "jone"); // 等价于wrapper.eq("name", "jone"); 但是这样写更安全,因为不会出现字段名写错的情况
System.out.println(userMapper.selectList(wrapper));

同时多个查询条件,必须同时满足才行

    @Test
    void testEq() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getName, "jone").eq(User::getAge, 19); // 相当于并列条件
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (name = ? AND age = ?)

空值null的判断与处理

当某个查询条件值为空时,不参与拼接

一般用于多条件查询中

 @Test
    void testNull() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        String name = "jone";
        // 查询条件是否为null的判断
        wrapper.eq(name != null, User::getName, name); // 如果name不为空,就加入条件,否则不加入条件
        System.out.println(userMapper.selectOne(wrapper));
    }

多条件查询也可以使用allEq

使用HashMap存储查询条件,就没法使用LambdaQueryWrapper

 @Test
    void testAllEq() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "jone");
        map.put("age", 19);
        wrapper.allEq(map, false); // 等价于wrapper.eq("name", "jone").eq("age", 19); 如果第二个参数为false,表示如果map中有null值,就不加入条件
        System.out.println(userMapper.selectList(wrapper));
    }

除了eq,还有ne,即不等值查询

@Test
    void testNe() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.ne("name", "jone"); // 不等于
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (name <> ?)  <> 不等于

2、范围查询

  1. gt 大于
@Test
    void testGt() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.gt("age", 20); // 大于,适用于数据类型为数字的字段和日期类型的字段
        System.out.println(userMapper.selectList(wrapper));
    }
  1. ge 大于等于
    @Test
    void testGe() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.ge("age", 20); // 大于等于,适用于数据类型为数字的字段和日期类型的字段
        System.out.println(userMapper.selectList(wrapper));
    }
  1. lt 小于
 @Test
    void testLt() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.lt("age", 20); // 小于,适用于数据类型为数字的字段和日期类型的字段
        System.out.println(userMapper.selectList(wrapper));
    }
  1. le 小于等于
 @Test
    void testLe() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.le("age", 20); // 小于等于,适用于数据类型为数字的字段和日期类型的字段
        System.out.println(userMapper.selectList(wrapper));
    }
  1. between
    @Test
    void testBetween() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(User::getAge, 18, 20); // 适用于数据类型为数字的字段和日期类型的字段, 包含18和20
        System.out.println(userMapper.selectList(wrapper));
    }
  1. notBetween
    @Test
    void testNotBetween() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.notBetween(User::getAge, 18, 20); // 适用于数据类型为数字的字段和日期类型的字段, 不包含18和20
        System.out.println(userMapper.selectList(wrapper));
    }

3、模糊查询

  1. like
@Test
    void testLike() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(User::getName, "j"); // 模糊查询
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (name LIKE %j%)
  1. notLike

查询名称中不含 j的,不区分大小写

@Test
    void testLike() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.notLike(User::getName, "j"); // 模糊查询
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (name NOT LIKE %j%)
  1. likeLeft

区分大小写

    @Test
    void testLikeLeft() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.likeLeft(User::getName, "j"); // 左模糊查询, 等价于wrapper.like(User::getName, "%j")
        System.out.println(userMapper.selectList(wrapper));
    }
  1. likeRight
@Test
    void testLikeRight() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 查找名字以j开头的用户,区分大小写
        wrapper.likeRight(User::getName, "j"); // 右模糊查询, 等价于wrapper.like(User::getName, "j%")
        System.out.println(userMapper.selectList(wrapper));
    }

4、判空查询

  1. isNull
    @Test
    void testIsNull() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNull(User::getEmail); // 查找email为null的用户
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (email IS NULL)
  1. isNotNull
    @Test
    void testIsNull() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNotNull(User::getEmail); // 查找email不为null的用户
        System.out.println(userMapper.selectList(wrapper));
    }

5、包含查询

  1. in
    @Test
    void testIn() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(User::getAge, Arrays.asList(18,19,20)); // 查找年龄为18,19,20的用户
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (age IN (?,?,?))
  1. notIn
    @Test
    void testNotIn() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.notIn(User::getAge, Arrays.asList(18,19,20)); // 查找年龄不为18,19,20的用户
        System.out.println(userMapper.selectList(wrapper));
    }
  1. inSql

可以编写嵌套查询

    @Test
    void testInSql() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.inSql(User::getId, "select id from user where id < 3"); // 查找id小于3的用户
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (id IN (select id from user where id < 3))
  1. notInSql
    @Test
    void testNotInSql() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.notInSql(User::getId, "select id from user where id < 3"); // 查找id不小于3的用户
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (id NOT IN (select id from user where id < 3))

6、分组查询

使用groupBy分组

    @Test
    void testGroupBy() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.groupBy("age"); // 按照年龄分组
        // 这里使用字符串的方式,所以只能使用QueryWrapper,不能使用LambdaQueryWrapper
        wrapper.select("age, count(*) as count"); // 查询年龄和年龄的数量
        System.out.println(userMapper.selectMaps(wrapper)); // 返回类型为List<Map<String, Object>>
    }
SELECT age, count(*) as count FROM user GROUP BY age

7、聚合查询

在6的基础上,过滤出count大于等于2的数据,就需要使用having过滤

先分组,再查询,最后过滤

    @Test
    void testHaving() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.groupBy("age"); // 按照年龄分组
        wrapper.select("age, count(*) as count"); // 查询年龄和年龄的数量
        wrapper.having("count >= 2"); // 查询数量大于等于2的年龄
        System.out.println(userMapper.selectMaps(wrapper)); // 返回类型为List<Map<String, Object>>
    }
SELECT age, count(*) as count FROM user GROUP BY age HAVING count >= 2

[{count=2, age=18}, {count=2, age=20}]

8、排序查询

  1. orderByASC 升序
    @Test
    void testOrderByAsc() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getId, User::getName, User::getAge); // 查询id, name, age字段
        wrapper.orderByAsc(User::getAge); // 按照年龄升序排序
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age FROM user ORDER BY age ASC
  1. orderByDesc 降序
    @Test
    void testOrderByDesc() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getId, User::getName, User::getAge); // 查询id, name, age字段
        wrapper.orderByDesc(User::getAge); // 按照年龄降序排序
        System.out.println(userMapper.selectList(wrapper));
    }
  1. orderBy

相比较前面两个,灵活性更高

    @Test
    void testOrderBy() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getId, User::getName, User::getAge); // 查询id, name, age字段
        // 第一个参数表示当该字段值为null时,是否还要作为排序条件
        // 第二个参数表示是否升序排序
        // 第三个参数表示排序的字段
        wrapper.orderBy(true, true, User::getAge); // 按照年龄升序排序
        // 当年龄相同时,按照id降序排序
        wrapper.orderBy(true, false, User::getId); // 按照id降序排序
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age FROM user ORDER BY age ASC,id DESC

9、逻辑查询

(1)内嵌逻辑查询func

当某个需求的条件有多个时,可以使用func

    @Test
    void testFunc() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // func函数需要传入一个Consumer接口,该接口的accept方法接收一个LambdaQueryWrapper对象
        wrapper.func(new Consumer<LambdaQueryWrapper<User>>() {
            @Override
            public void accept(LambdaQueryWrapper<User> userLambdaQueryWrapper) {
                // 这里需要使用实际开发中的业务逻辑来替换
                if(true) {
                    userLambdaQueryWrapper.eq(User::getId, 1);
                }else {
                    userLambdaQueryWrapper.ne(User::getId, 1);
                }
            }
        });
        System.out.println(userMapper.selectList(wrapper));
    }
    @Test
    void testFunc() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // func函数需要传入一个Consumer接口,该接口的accept方法接收一个LambdaQueryWrapper对象
        // 使用Lambda表达式来简化代码
        wrapper.func(userLambdaQueryWrapper -> {
            // 这里需要使用实际开发中的业务逻辑来替换
            if(true) {
                userLambdaQueryWrapper.eq(User::getId, 1);
            }else {
                userLambdaQueryWrapper.ne(User::getId, 1);
            }
        });
        System.out.println(userMapper.selectList(wrapper));
    }
(2)and

正常的拼接默认就是and,表示条件需要同时成立

    @Test
    void testAnd() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 18).lt(User::getAge, 25); // 查询年龄大于18并且小于20的用户
        System.out.println(userMapper.selectList(wrapper));
    }

使用and嵌套

通常需要嵌套or

    @Test
    void testAnd() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId, 1)
                .and(userLambdaQueryWrapper -> userLambdaQueryWrapper.eq(User::getAge, 18)
                        .or().eq(User::getAge, 20)); // 查询id为1并且年龄为18或20的用户
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (id = ? AND (age = ? OR age = ?))
(3)or

表示多个条件只需要成立其中之一即可

    @Test
    void testOr() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 25)
                .or()
                .lt(User::getAge, 20); // 查询年龄大于25或小于20的用户
        System.out.println(userMapper.selectList(wrapper));
    }

or也可以嵌套

(4)nested

表示嵌套查询

    @Test
    void testNested() {
        // nested表示嵌套查询, 用于构建复杂的查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId, 1)
                .nested(userLambdaQueryWrapper -> userLambdaQueryWrapper.eq(User::getAge, 18)
                        .or().eq(User::getAge, 20)); // 查询id为1并且年龄为18或20的用户
        System.out.println(userMapper.selectList(wrapper));
    }

10、自定义查询

使用apply函数

可以定制更复杂的查询条件

    @Test
    void testApply() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // apply方法用于拼接SQL语句,这里需要使用实际开发中的业务逻辑来替换
        wrapper.apply("id = 1"); // 查询id为1的用户
        System.out.println(userMapper.selectList(wrapper));
    }

11、last

last主要用于分页查询中,需要传入字符串参数

    @Test
    void testLast() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.last("limit 1"); // 查询第一个用户
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user limit 1
    @Test
    void testLast() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.last("limit 0, 2"); // 查询前两条数据
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user limit 0, 2

12、exists

    @Test
    void testExists() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 查询age为18的用户是否存在,如果存在则返回true,否则返回false
        // 返回true后,会继续执行后面的查询操作
        wrapper.exists("select id from user where age = 18");
        System.out.println(userMapper.selectList(wrapper));
    }
SELECT id,name,age,email FROM user WHERE (EXISTS (select id from user where age = 18))
    @Test
    void testNotExists() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 查询age为18的用户是否不存在,如果不存在则返回true,否则返回false
        // 返回true后,会继续执行后面的查询操作
        wrapper.notExists("select id from user where age = 18");
        System.out.println(userMapper.selectList(wrapper));
    }

二、主键策略

1、AUTO

需要在表的主键上设置自增,然后在实体类上的对应的属性上加上注解

@TableId(type = IdType.AUTO)
private Long id;

这样设置后,每次增加一条数据时,会自动生成对应的id

2、INPUT

不需要在表的主键上设置自增,每次新增数据时需要自己设置id

@TableId(type = IdType.AUTO)
private Long id;

3、ASSIGN_ID

使用雪花算法可以实现有序、唯一、且不直接暴露排序的数字

@TableId(type = IdType.ASSIGN_ID)
private Long id;

4、NONE

使用该策略表示不指定主键生成策略,而是跟随全局策略,可以在配置文件中使用id-type指定全局主键策略

@TableId(type = IdType.NONE)
private Long id;

5、ASSIGN_UUID

UUID是全局唯一标识符,定义为一个字符串主键,采用32位字符组成,保证始终唯一,需要设置id的类型为字符串

@TableId(type = IdType.ASSIGN_UUID)
private Long id;

三、分页查询

首先需要编写配置类

以下适用于mybatis-plus 3.5以上版本

@Configuration
// @MapperScan("com.system.mybatisplus.mapper") // 如果在启动类上已经配置了,这里就不需要再配置了
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // DbType.MYSQL 表示数据库类型是mysql
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

测试

    @Test
    void testPage() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 创建一个分页对象,传入两个参数:当前页和每页显示的记录数
        IPage<User> pageParam = new Page<>(1, 2);
        // 调用分页查询的方法,将分页对象和查询条件对象传入
        userMapper.selectPage(pageParam, wrapper);
        // 从分页对象中获取分页数据
        System.out.println("总页数:" + pageParam.getPages());
        System.out.println("总记录数:" + pageParam.getTotal());
        System.out.println("当前页码:" + pageParam.getCurrent());
        System.out.println("每页显示的记录数:" + pageParam.getSize());
    }

一般会自定义查询语句,所以通用的写法如下

controller中

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/users/{page}/{limit}")
    public IPage<User> listPage(@PathVariable("page") Long page, @PathVariable("limit") Long limit) {
        // 创建一个分页对象,传入两个参数:当前页和每页显示的记录数
        IPage<User> pageParam = new Page<>(page, limit);
        // 调用Service中的方法,一般还会传入一个条件查询对象
        return userService.listPage(pageParam);
    }
}

Service中

public interface UserService extends IService<User> {

    IPage<User> listPage(IPage<User> pageParam);
}
@Service // 此注解用于标记这是一个service类,否则会报错,因为没有加@Service
// 此处继承ServiceImpl(ServiceImpl实现了IService接口),同时实现UserService接口
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Override
    public IPage<User> listPage(IPage<User> pageParam) {
        return userMapper.selectUserByPage(pageParam);
    }
}

mapper中

@Mapper // 此注解用于标记这是一个mybatis的mapper类,否则会报错,因为没有加@Mapper
public interface UserMapper extends BaseMapper<User> {
	// 返回值是一个IPage对象
    IPage<User> selectUserByPage(IPage<User> pageParam); // 传入分页参数
}
<?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.system.mybatisplus.mapper.UserMapper">
    <select id="selectUserByPage" resultType="com.system.mybatisplus.model.User">
        select * from user
    </select>
</mapper>