MyBatis-Plus使用记录

警告
本文最后更新于 2023-03-06,文中内容可能已过时,请谨慎使用。

MyBatis-Plus提供了一个通用的增删改查的Mapper,可以通过自定义Mapper接口继承BaseMapper<T>完成基础的增删改查

@Mapper
public interface UserMapper extends BaseMapper<User> {
}
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录, 如果查询结果超过1条则会报TooManyResultsException异常
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

样例:

// 查询表中所有数据
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);

// 根据id查询,例如id=2的用户信息
userMapper.selectById(2); 

// 多条件查询
// 根据id和name进行查询

// 方法1: 使用QueryWrapper
QueryWrapper<User> qw = new QueryWrapper<>();
qw.eq("id",id);
qw.eq("name",name);
userMapper.selectList(qw);

// 方法2: 使用selectByMap
// 返回一个List
HashMap<String,Object> map = new HashMap<>();
map.put("id",1);
map.put("name","Jone");
System.out.println(userMapper.selectByMap(map));

// 查询有多少条数据
System.out.println("mapper.selectCount(null) = " + userMapper.selectCount(null));

// selectList查询部分字段,会返回所有的,没查询的值为null
// 而selectMaps只会返回你查询的字段

// 查询多个字段的值,null表示返回全部字段
System.out.println("mapper.selectMaps(null) = " + userMapper.selectMaps(null));
// 查询部分字段
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age");
List<Map<String, Object>> users = userMapper.selectMaps(queryWrapper);
users.forEach(System.out::println);

// 查询单字段的值,null表示返回主键值
// 注意,如果填了多个字段,只返回第一个字段的值
System.out.println("mapper.selectObjs(null) = " + userMapper.selectObjs(null));
// 表中是否存在数据
System.out.println("mapper.exists(null) = " + userMapper.exists(null));

// 同时查询多个id的用户数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(2L);
list.add(3L);
System.out.println("userMapper.selectBatchIds(list) = " + userMapper.selectBatchIds(list));
// 插入一条记录
int insert(T entity);

样例:

User u = new User(7L,"admin",18,"xxx");
userMapper.insert(u);
// 根据whereEntity条件,更新记录
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
// 根据ID修改
int updateById(@Param(Constants.ENTITY) T entity);

样例:

# 根据用户id找到该用户然后修改他的名字
UpdateWrapper<User> userUpdateWrappers = new UpdateWrapper<>();
userUpdateWrappers.eq("id",2).set("name","bbb");
System.out.println(userMapper.update(null,userUpdateWrappers));

# 根据id修改其他字段值
User u = new User(1,"aaa",19,"111@qq.com");
userMapper.updateById(u);
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

样例:

// 根据id进行删除
// 返回值为受影响的行数
System.out.println(userMapper.deleteById(2));

// 利用键值对进行多条件删除
HashMap<String,Object> map = new HashMap<>();
map.put("id",3);
map.put("name","Tom");
System.out.println("userMapper.deleteByMap(map) = " + userMapper.deleteByMap(map));

// 根据id列表批量删除数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(5L);
System.out.println("userMapper.deleteBatchIds(list) = " + userMapper.deleteBatchIds(list));


// 利用QueryWrapper删除
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",1);
queryWrapper.eq("name","Jone");
System.out.println("userMapper.delete(queryWrapper) = " + userMapper.delete(queryWrapper));
  • @TableName("表名")在类名和表名不一致的情况下,可以在类名上添加此注解指定表名

  • @TableId("id名称")可以在属性上指定id所在的字段,在id和表中的id不一致时,可以在括号中,@TableId(type = IdType.XXX)可以指定插入主键值的生成策略,默认为雪花算法

MyBatis-Plus默认实现5种主键生成策略,分别是:

  • AUTO配合数据库设置自增主键,可以实现主键的自动增长
  • INPUT,由用户输入
  • NONE,不设置,等同于INPUT
  • ASSIGN_ID,只有当用户未输入时,采用雪花算法生成一个适用于分布式环境的全局唯一主键,类型可以是Stringnumber
  • ASSIGN_UUID,只有当用户未输入时,生成一个String类型的主键,但不保证全局唯一
@TableId(type = IdType.AUTO)
private Integer id;

如果表创建的时候没有设置自增,需要修改id列为自增

/images/all/image-20230306203230448.png

一个通用的Service接口,MyBatis Plus提供了一个实现类为:ServiceImpl<M extends BaseMapper<T>, T>

@Service
public class UserService extends ServiceImpl<UserMapper, User> {
}

简单使用

@Test
void insertData() {
    // 存入一个user
    User user = new User(null,"zzz",18,"aa");
    //        service.save(user);
    // 批量存入
    List<User> list = new ArrayList<>();
    list.add(user);
    list.add(user);
    list.add(user);
    service.saveBatch(list);
    // 查看所有数据
    service.list().forEach(System.out::println);
}

@Test
void insertUpdate() {
    System.out.println("service.list() = " + service.list());
    User user = new User(null, "admin", 20, "xxx@qq.com");
    // 根据主键进行插入或者更新某条记录,如果数据库中不存在这个主键,那么将会进行插入;
    // 如果存在这个主键,就进行更新这条记录
    service.saveOrUpdate(user);
    System.out.println("service.list() = " + service.list());
}

@Test
void insertBatch() {
    service.list().forEach(System.out::println);
}

@Test
void select() {
    System.out.println("service.count() = " + service.count());
    System.out.println("service.list() = " + service.list());
    System.out.println("service.listMaps() = " + service.listMaps());
    System.out.println("service.listObjs() = " + service.listObjs());
    System.out.println("service.getById(1) = " + service.getById(1));
}

@Test
void delete() {
    service.removeById(1);
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    service.removeByIds(list);
    list.add(4);
    service.removeBatchByIds(list);
    System.out.println("service.list() = " + service.list());
    HashMap<String, Object> map = new HashMap<>();
    map.put("id", 5);
    map.put("name","Billie");
    service.removeByMap(map);
    System.out.println("service.list() = " + service.list());
}
@Test
void test() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.ge("id", 5)
        .or(qw -> qw.ge("age",25).le("age",100));
    //         小于
    //        queryWrapper.lt("id", 3);

    //        小于等于
    //        queryWrapper.le("id", 3);

    //        大于
    //        queryWrapper.gt("id", 2);

    //        大于等于
    //        queryWrapper.ge("id", 3);

    //        等于
    //        queryWrapper.eq("id", 3);

    //        不等于
    //        queryWrapper.ne("id", 2);

    //        联合起来写,代表大于等于2且小于等于5,且不等于3
    //        queryWrapper.ge("id", 2)
    //                        .le("id", 5)
    //                                .ne("id", 3);

    //        between条件
    //        queryWrapper.between("id", 2, 4); // id在2到4

    //        like条件
    //        queryWrapper.like("name", "J%");

    //        not like
    //        queryWrapper.notLike("name", "%a%");

    //        查询某几个字段
    //        queryWrapper.select("id", "name");

    /*
        也可以使用group by,select中也可以写别名
        */
    //        queryWrapper.groupBy("age");
    //        queryWrapper.select("age 年龄", "count(*) 数量");
    //        queryWrapper.select("age as 年龄", "count(*) as 数量");

    //        in的用法
    //        queryWrapper.in("id", 2, 3, 4);
    //        queryWrapper.in("id", List.of(2, 3, 4));

    //        not in
    //        queryWrapper.notIn("id", 2, 3, 5);
    //        queryWrapper.notIn("id", List.of(2, 3, 5)); // jdk 9新API

    //        排序
    //        正序
    //        queryWrapper.orderBy(true, true, "id");
    //        queryWrapper.orderByAsc("id");
    //        倒序
    //        queryWrapper.orderBy(true, true, "id");
    //        queryWrapper.orderByDesc("id");

    //        ((id >= ?) OR (age >= ? AND age <= ?))
    //        queryWrapper.ge("id", 5)
    //                .or()
    //                .ge("age", 25)
    //                .le("age", 100);


    //        queryWrapper.ge("id", 5)
    //                .or(wrapper -> wrapper.ge("age", 25).le("age", 100));

    //                queryWrapper.isNull("列名").isNotNull("列名")
    userMapper.selectList(queryWrapper).forEach(System.out::println);
}

避免手打的字段名出错,其他功能和QueryWrapper一致

@Test
void lambdaQueryWrapperTest() {
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
    lqw.select(User::getName,User::getAge);
    userMapper.selectMaps(lqw).forEach(System.out::println);
}

Mybatis plus提供了条件组装,只有条件为true时才会组装这条sql

只有第一个参数 condition 成立时才进行 sql拼接

@Test
void condition() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    String name = "名称";
    Integer minAge = 10, maxAge = 1500;
    /**
	 * 如果名字不为空,会按照名称进行查询
	 * 如果最小年龄不为空,同时也会将这个条件加进去
	 * 如果最大年龄不会空,也会将这个条件加进去
	*/
    queryWrapper.eq(name != null, "name", name);
    queryWrapper.ge(minAge != null, "age", minAge);
    queryWrapper.le(maxAge != null, "age", maxAge);
    userMapper.selectList(queryWrapper);
}
// 查询用户年龄的最大值和最小值
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("max(age) as max,min(age) as min");
List<Map<String, Object>> res = userMapper.selectMaps(qw);

System.out.println(Integer.valueOf(res.get(0).get("max").toString()));
System.out.println(Integer.valueOf(res.get(0).get("min").toString()));

新增一个分页配置类

@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 数据库类型为mysql
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

分页查询api

/**
 *
 * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
 
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

方法需要传入两个参数:

  • IPage<T>
  • Wrapper<T>

IPage<T> 可以使用实现类 Page<T>Wrapper<T> 跟上面的一样使用 QueryWrapper<T> 即可

Page<T>无参构造方法默认页码为current=1,每页有size=10条数据,可以传入指定页码和页大小

@Test
public void selectByWrapperPage() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    Page<User> page = new Page<>(1,10);
    IPage<User> iPage = userMapper.selectPage(page, queryWrapper);

    System.out.println("总页数: " + iPage.getPages());
    System.out.println("数据总数: " + iPage.getTotal());
    // 返回分页数据
    List<User> userList = iPage.getRecords();
    userList.forEach(System.out::println);

    // 获取分页的其他信息
    System.out.println("当前页号 = " + iPage.getCurrent());
    System.out.println("页大小 = " + iPage.getSize());
}

注意事项:

  • Mapper接口中的返回值设置为Page<类型>
  • 第一个参数设置为Page<类型> page
  • 其他地方不变
  • 需要注意的是,自己写的sql语句末尾一定不要添加;,因为分页是在sql语句末尾进行追加的sql语句

这里我们使用注解方式实现,也可以通过xml注入的方式实现

@Mapper
public interface UserMapper extends BaseMapper<User> {
    /**
     * 只对超过age的数据进行分页处理
     * @return 分页器
     */
    @Select("select * from user where age >= ${age}")
    Page<User> selectByAge(Page<User> page, Integer age);
}

测试使用

@Test
public void selectByWrapperPageByAge() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    Page<User> page = new Page<>(1,3);
    // 这里使用我们自定义的分页
    IPage<User> iPage = userMapper.selectByAge(page,25);
    iPage.getRecords().forEach(System.out::println);

    System.out.println("总页数: " + iPage.getPages());
    System.out.println("数据总数: " + iPage.getTotal());
    // 返回分页数据
    List<User> userList = iPage.getRecords();
    userList.forEach(System.out::println);

    // 获取分页的其他信息
    System.out.println("当前页号 = " + iPage.getCurrent());
    System.out.println("页大小 = " + iPage.getSize());
}

导入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.1</version>
</dependency>

添加配置

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        slave_2:
          url: ENC(xxxxx) # 内置加密,使用请查看详细文档
          username: ENC(xxxxx)
          password: ENC(xxxxx)
          driver-class-name: com.mysql.jdbc.Driver
       #......省略
       #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2

相关文章