MyBatis-Plus当中BaseMapper接口的增删查改操作

目录

1.查询

根据id查询

根据多个id查询

根据字段名-值对的映射关系进行查询

期望返回一条记录的查询

查询符合条件的记录数

查询所有符合条件的记录

​编辑查询返回Map列表

返回单个字段(一般是id)的查询

分页查询

返回特定字段的分页查询

2.增(插入)

3.删除

根据id删除

传入对象删除

根据Map删除

根据queryWrapper删除

批量删除

4.修改

根据id修改

queryWrapper修改


入门案例当中的"查询所有"调用的是BaseMapper当中的selectList,接下来我们从增删查改来了解BaseMapper接口当中的方法。

java 复制代码
    /**
     * 查询所有
     */
    @Test
    public void testSelectList(){
        userMapper.selectList(null).forEach(System.out::println);
    }

1.查询

根据id查询

java 复制代码
    T selectById(Serializable id);

这个没什么好说的就是根据id查询,返回T类型的对象。注意Serializable,这里使用Serializable 的原因是数据库的主键类型多种多样,比如Integer、Long、String等,而Long, Integer, String 等包装类和基本类型的包装类都实现了 Serializable 接口。这就使得以Serializable作为参数类型的selectById变得很通用。

调用代码:

java 复制代码
    /**
     * 根据id查询
     */
    @Test
    public void selectById(){
        userMapper.selectById(1);
    }

运行结果:

根据多个id查询

java 复制代码
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

Collection是Java集合框架的根接口,List、Set、Queue等都实现了这个接口

? extends Serializable是泛型通配符,表示"任何继承了Serializable接口的类型"

所以我们传入的参数应该是实现了Serializable接口的元素的集合,例如:

java 复制代码
List<Long> idList = Arrays.asList(1L, 2L, 3L);
Set<Integer> idSet = new HashSet<>(Arrays.asList(1, 2)); 
List<String> idStrings = Arrays.asList("A", "B", "C");
ArrayList<BigInteger> bigInts = new ArrayList<>();

selectBatchIds 接收这样一个集合,返回T类对象的集合

调用代码:

java 复制代码
    /**
     * 批量查询,也可以查询一个
     */
    @Test
    public void selectBatchByIds() {
        List<Long> idList = Arrays.asList(1L, 2L, 3L, 4L, 5L);

        List<User> users = userMapper.selectBatchIds(idList);
        System.out.println("查询到 " + users.size() + " 条记录:");
        users.forEach(System.out::println);
    }

运行结果:

根据字段名-值对的映射关系进行查询

java 复制代码
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

它接受一个字段名和对应值的Map,返回所有匹配的记录。

生成的SQL是 等值查询的 AND 组合,类似于:

sql 复制代码
SELECT * FROM table_name WHERE column1 = ? AND column2 = ? AND ...;

我们要注意Map当中的字段名字是数据库当中的名字而不是JavaBean当中的名字

调用代码:这段代码相当于查询了名字是Mike并且年龄为18的所有用户,并返回它们的集合

java 复制代码
    /**
     * 通过map查询用户信息
     */
    @Test
    public void selectByMap(){
        //通过map条件查询用户信息
        //SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
        Map<String, Object> map = new HashMap<>();
        map.put("age", 18);
        map.put("name", "Mike");
        List<User> list = userMapper.selectByMap(map);
        list.forEach(System.out::println);
    }

运行结果:

期望返回一条记录的查询

java 复制代码
default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
    // 1. 先调用 selectList 查询所有匹配的记录
    List<T> ts = this.selectList(queryWrapper);
    
    // 2. 检查结果列表是否不为空
    if (CollectionUtils.isNotEmpty(ts)) {
        // 3. 如果结果数量不等于1,抛出异常
        if (ts.size() != 1) {
            throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);
        } else {
            // 4. 如果正好只有1条记录,返回它
            return ts.get(0);
        }
    } else {
        // 5. 如果没有记录,返回null
        return null;
    }
}

QueryWrapper 是 MyBatis-Plus 提供的查询条件封装器,用于以面向对象的方式动态构建 SQL 查询条件,避免了手动拼接 SQL 字符串的麻烦。有了QueryWrapper 之后,我们便可以使用Java代码来构建SQL查询条件,例如:

java 复制代码
// 查询 name = '张三' 并且 age = 25 的用户
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "张三")
           .eq("age", 25);

List<User> users = userMapper.selectList(queryWrapper);
// 生成SQL: SELECT * FROM user WHERE name = '张三' AND age = 25

QueryWrapper还有很多复杂的用法,我们会在后面遇到。

我们可以看到selectOne调用了前面的selectList方法查询所有符合条件的记录,再检查记录条数是否是1.如果是1则返回,多余1则抛出异常,少于1则返回null

调用:这是刚好为1的情况

java 复制代码
    /**
     * 查询单条记录
     */
    @Test
    public void selectOne() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", "Mike")
                .eq("age", 18);

        User user = userMapper.selectOne(wrapper);
        System.out.println("查询到的用户: " + user);
    }

结果:

大于1的情况:

java 复制代码
    /**
     * 查询单条记录
     */
    @Test
    public void selectOne() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("age", 89);

        User user = userMapper.selectOne(wrapper);
        System.out.println("查询到的用户: " + user);
    }

结果:

少于1的情况:

java 复制代码
    /**
     * 查询单条记录
     */
    @Test
    public void selectOne() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("age", 1111);

        User user = userMapper.selectOne(wrapper);
        System.out.println("查询到的用户: " + user);
    }

结果:

查询符合条件的记录数

java 复制代码
Long selectCount(@Param("ew") Wrapper<T> queryWrapper);

queryWrapper包装好查询条件,查询到符合条件的记录数并且返回

代码:

java 复制代码
    /**
     * 查询记录数量
     */
    @Test
    public void selectCount() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.gt("age", 20);

        Long count = userMapper.selectCount(wrapper);
        System.out.println("年龄大于20的用户数量: " + count);
    }

结果:

查询所有符合条件的记录

java 复制代码
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

在入门案例中,我们传入了null,也就是没有任何条件,也就是查询所有记录

这里我们传入查询条件

代码:

java 复制代码
    /**
     * 查询所有符合条件的记录
     */
    @Test
    public void testSelectList(){
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.gt("age",50);
        userMapper.selectList(queryWrapper).forEach(System.out::println);
    }

结果:

查询返回Map列表

java 复制代码
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

这个方法主要用来返回某些特定的字段,依旧是用queryWrapper来构造查询条件

调用代码:

java 复制代码
    /**
     * 查询返回Map列表
     */
    @Test
    public void selectMaps() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("id", "name", "age")
                .lt("age", 30);//gt是大于,lt是小于

        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        System.out.println("查询结果:");
        maps.forEach(System.out::println);
    }

这个方法当中查询了年龄小于30的用户的id,名字,年龄。

结果:

返回单个字段(一般是id)的查询

java 复制代码
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

即使我们指定了多个字段,结果也只会返回第一列

调用:

java 复制代码
    /**
     * 只返回第一列的查询
     */
    @Test
    public void selectObjs(){
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.select("id","name","email")
                .eq("age","12");
        System.out.println("返回的数据:"+userMapper.selectObjs(queryWrapper));
    }

结果:

可以看到只有第一列的数据被返回,即便我们指定了name和email字段。

我们看到Columns中有三个字段,那只是数据库SQL执行结束的值,selectObjs只取了第一列。

分页查询

java 复制代码
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);

泛型:<P extends IPage<T>> - 接受任何实现了 IPage<T> 接口的分页对象

IPage类:

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.core.metadata;

import java.io.Serializable;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public interface IPage<T> extends Serializable {
    List<OrderItem> orders();

    default boolean optimizeCountSql() {
        return true;
    }

    default boolean optimizeJoinOfCountSql() {
        return true;
    }

    default boolean searchCount() {
        return true;
    }

    default long offset() {
        long current = this.getCurrent();
        return current <= 1L ? 0L : Math.max((current - 1L) * this.getSize(), 0L);
    }

    default Long maxLimit() {
        return null;
    }

    default long getPages() {
        if (this.getSize() == 0L) {
            return 0L;
        } else {
            long pages = this.getTotal() / this.getSize();
            if (this.getTotal() % this.getSize() != 0L) {
                ++pages;
            }

            return pages;
        }
    }

    default IPage<T> setPages(long pages) {
        return this;
    }

    List<T> getRecords();

    IPage<T> setRecords(List<T> records);

    long getTotal();

    IPage<T> setTotal(long total);

    long getSize();

    IPage<T> setSize(long size);

    long getCurrent();

    IPage<T> setCurrent(long current);

    default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
        List<R> collect = (List)this.getRecords().stream().map(mapper).collect(Collectors.toList());
        return this.setRecords(collect);
    }

    default String countId() {
        return null;
    }
}

参数1:P page - 分页参数对象(页码、页大小等)

参数2:@Param("ew") Wrapper<T> queryWrapper - 查询条件

返回值:P - 包含分页数据和分页信息的结果对象

要调用这个接口,在实现它之前我们要先配置MybatisPlus的分页插件:

java 复制代码
package com.qcby.config;

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 MybatisPlusConfig {

    /**
     * PaginationInnerInterceptor
     * 分页插件,专门处理分页查询
     * 会自动在分页查询时:执行 COUNT(*) 查询获取总记录数
     * 在原 SQL 上添加 LIMIT 子句(MySQL)或对应的分页语法
     * 将分页结果封装到 Page 对象中
     *
     * DbType.MYSQL
     * 指定数据库类型为 MySQL
     * MySQL: LIMIT offset(偏移量), size(页大小)
     * 指定类型后插件会自动生成正确的分页 SQL
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

调用代码:

java 复制代码
@Test
public void selectPage() {
    // 1. 创建分页对象:第1页,每页10条
    Page<User> page = new Page<>(1, 10);
    
    // 2. 创建查询条件包装器
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    
    // 3. 添加排序条件:按照age字段降序排列
    wrapper.orderByDesc("age");
    
    // 4. 执行分页查询
    Page<User> result = userMapper.selectPage(page, wrapper);
    
    // 5. 输出分页信息
    System.out.println("总记录数: " + result.getTotal());
    System.out.println("当前页记录:");
    result.getRecords().forEach(System.out::println);
}

结果:

返回特定字段的分页查询

java 复制代码
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);

代码:

java 复制代码
    /**
     * 分页查询返回Map,相比于普通的分页查询,Map可以不需要返回完整的对象,只返回部分字段
     */
    @Test
    public void selectMapsPage() {
        Page<Map<String, Object>> page = new Page<>(1, 2);
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("id", "name");

        Page<Map<String, Object>> result = userMapper.selectMapsPage(page, wrapper);
        System.out.println("Map分页结果:");
        result.getRecords().forEach(System.out::println);
    }

结果:

我们发现,在配置了分页插件之后,会拦截Sql,并且生成两条,一条查询总记录数,一条执行分页查询。

2.增(插入)

java 复制代码
int insert(T entity);

如果要进行批量插入也只是写循环。

调用:

java 复制代码
    /**
     * 插入一条记录
     */
    @Test
    public void insert(){
        User user=new User();
        user.setName("Steve");
        user.setAge(12);
        user.setEmail("test9@baomidou.com");
        int res=userMapper.insert(user);
        System.out.println("受影响的行数:"+res);
    }

结果:

3.删除

根据id删除

java 复制代码
int deleteById(Serializable id);

代码:

java 复制代码
    /**
     * 根据id删除
     */
    @Test
    public void deleteById(){
        User user=new User();
        user.setId(9L);//加上L表示Long类型
        int res=userMapper.deleteById(user);//也可以直接传入id值
        System.out.println("受影响的行数:"+res);
    }

因为我的主键是Long类型的所以我写了9L

结果:

传入对象删除

java 复制代码
int deleteById(T entity);

这个和上面的删除是类似的,只不过传入的是对象。

根据Map删除

java 复制代码
int deleteByMap(@Param("cm") Map<String, Object> columnMap);

用Map来构造删除条件。

调用:

java 复制代码
    /**
     * 根据Map删除
     */
    @Test
    public void deleteByMap(){
        Map<String,Object> map=new HashMap<>();
        map.put("name","Steve");//删除名字为Steve的记录
        map.put("age",12);//删除年龄为12的记录
        int res=userMapper.deleteByMap(map);//删除名字为Steve且年龄为12的记录
        System.out.println("受影响的行数:"+res);
    }

结果:

我们发现用Map构造的条件只能用and连接。

根据queryWrapper删除

我们使用这种方式删除,可以构造比Map更加复杂的条件。

java 复制代码
int delete(@Param("ew") Wrapper<T> queryWrapper);

代码:

java 复制代码
@Test
    public void deleteWithQueryWrapper(){
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        wrapper.eq("name","Steve")
                .eq("age",12)
                .like("email","baomidou");//删除名字为Steve,12岁,邮箱名称包含baomidou的记录
//        wrapper.eq("name", "Steve");        // name = 'Steve'
//        wrapper.ne("name", "Steve");        // name != 'Steve'
//        wrapper.gt("age", 18);              // age > 18
//        wrapper.ge("age", 18);              // age >= 18
//        wrapper.lt("age", 65);              // age < 65
//        wrapper.le("age", 65);              // age <= 65

        //模糊查询
//        wrapper.like("name", "Ste");        // name LIKE '%Ste%'
//        wrapper.likeLeft("name", "eve");    // name LIKE '%eve'
//        wrapper.likeRight("name", "Ste");   // name LIKE 'Ste%'
//        wrapper.notLike("name", "Steve");   // name NOT LIKE '%Steve%'


        //范围查询
//        wrapper.in("age", Arrays.asList(18, 19, 20));    // age IN (18,19,20)
//        wrapper.notIn("age", Arrays.asList(60, 65, 70)); // age NOT IN (60,65,70)
//        wrapper.between("age", 18, 30);                  // age BETWEEN 18 AND 30
//        wrapper.notBetween("age", 60, 100);              // age NOT BETWEEN 60 AND 100
        //空值判断
//        wrapper.isNull("email");            // email IS NULL
//        wrapper.isNotNull("email");         // email IS NOT NULL
        int res=userMapper.delete(wrapper);
        System.out.println("受影响的行数:"+res);
    }

结果:

可以看到QueryWrapper支持非常多条件。

我们如果需要用or连接条件的话,需要用.or()来连接,例如:

java 复制代码
        wrapper.eq("name", "Steve")
                .or()
                .eq("name", "Tom");      // name='Steve' OR name='Tom'

批量删除

java 复制代码
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

传入多个id删除

代码:

java 复制代码
    /**
     * 删除固定的几个id,批量删除
     */
    @Test
    public void deleteBatchByIds() {
        List<Long> idList = Arrays.asList(1L, 2L, 3L, 4L, 5L);
        int res = userMapper.deleteBatchIds(idList);
        System.out.println("批量删除了 " + res + " 条记录");
    }

结果:我改成了20,21,23,24,25这个id序列,可以看到即使某些id不存在也不会报错

4.修改

根据id修改

java 复制代码
int updateById(@Param("et") T entity);

代码:

java 复制代码
    /**
     * 根据id修改
     */
    @Test
    public void updateById(){
        User user=new User();
        user.setId(1L);
        user.setAge(99);
        userMapper.updateById(user);
        //userMapper.update(user,null);
    }

结果:

queryWrapper修改

java 复制代码
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

这个方法可以传入实体类和queryWrapper混合使用,也可以只传入实体类或者只传入queryWrapper。在根据id修改当中,注释的那一行就是使用的当前方法,只不过它是只传入的实体类。

只传入queryWrapper:

java 复制代码
    /**
     * 复杂条件wrapper修改
     */
    @Test
    public void updateWithWrapper(){
        UpdateWrapper<User> wrapper=new UpdateWrapper<>();
        wrapper.set("age",89)
                .set("email","update@email.com")//set设置更新字段
                .eq("name","张三")//eq设置条件
                .or()//or连接
                .eq("name","李四");
        userMapper.update(null,wrapper);
    }

结果:

都传入:

java 复制代码
    /**
     * 两种都传入的修改
     */
    @Test
    public void updateWithEntityAndWrapper(){
        // 创建实体对象-设置要更新的字段值
        User user = new User();
        user.setName("新名字");     // 要更新的字段
        user.setEmail("new@email.com");
        user.setAge(25);

        // 创建Wrapper-设置更新条件
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.eq("age", 89);      // 条件:年龄为89的用户

        System.out.println("受影响行数:" + userMapper.update(user, wrapper));
    }

结果:

相关推荐
纪莫2 小时前
技术面:MySQL(一条SQL在MySQL的执行过程?、MyISAM和InnoDB的区别?数据库事务机制?)
java·数据库·java面试⑧股
闲人编程2 小时前
Python协程的演进:从yield到async/await的完整历史
java·前端·python·async·yield·await·codecapsule
q***92512 小时前
PHP操作redis
开发语言·redis·php
帅中的小灰灰2 小时前
C++编程建造器设计模式
java·c++·设计模式
大佬,救命!!!2 小时前
python实现五子棋
开发语言·python·个人开发·pygame·少儿编程·五子棋
动感小麦兜3 小时前
应用-常用工具部署命令
java·开发语言
日日行不惧千万里3 小时前
IDEA 是用什么开发的?
java·ide·intellij-idea
k***85843 小时前
SpringBoot(整合MyBatis + MyBatis-Plus + MyBatisX插件使用)
spring boot·tomcat·mybatis
立志成为大牛的小牛3 小时前
数据结构——五十一、散列表的基本概念(王道408)
开发语言·数据结构·学习·程序人生·算法·散列表