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));
    }

结果:

相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔4 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大9 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai9 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说9 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json