MyBatis-Plus selectBatchIds:Collection 类型参数到底怎么传值?

目录

[① 导读卡片](#① 导读卡片)

[② 背景与目标](#② 背景与目标)

为什么学?

学完能怎样?

[③ 概念与原理](#③ 概念与原理)

[Collection 是什么?](#Collection 是什么?)

[selectBatchIds 方法签名](#selectBatchIds 方法签名)

[为什么不用 Integer\[\] 或 List 本身?](#为什么不用 Integer[] 或 List 本身?)

[④ 逻辑与对比](#④ 逻辑与对比)

[List vs Set 传参怎么选?](#List vs Set 传参怎么选?)

前端传参方式对比

[⑤ 核心详解](#⑤ 核心详解)

[5.1 最推荐的传值方式:List](#5.1 最推荐的传值方式:List)

[5.2 场景一:前端传 JSON 数组(POST)](#5.2 场景一:前端传 JSON 数组(POST))

[5.3 场景二:前端传逗号分隔字符串(GET)](#5.3 场景二:前端传逗号分隔字符串(GET))

[5.4 场景三:前端传多个同名参数](#5.4 场景三:前端传多个同名参数)

[5.5 完整 Mapper 和 Service 示例](#5.5 完整 Mapper 和 Service 示例)

[5.6 ⚠️ 空值校验是必须的](#5.6 ⚠️ 空值校验是必须的)

[5.7 ⚡ 性能建议:ID 数量控制在 1000 以内](#5.7 ⚡ 性能建议:ID 数量控制在 1000 以内)

[⑥ 案例实战](#⑥ 案例实战)

完整案例:批量查询用户信息

控制台日志验证

[⑦ 避坑 & 最佳实践](#⑦ 避坑 & 最佳实践)

[❌ 常见错误](#❌ 常见错误)

[错误 1:想传 Collection 但不知道传什么](#错误 1:想传 Collection 但不知道传什么)

[错误 2:没做空值校验就调用](#错误 2:没做空值校验就调用)

[错误 3:字符串分割时没处理空格](#错误 3:字符串分割时没处理空格)

[✅ 最佳实践清单](#✅ 最佳实践清单)

[⑧ 总结 & 路线图](#⑧ 总结 & 路线图)

记住了什么?

金句

下一步去哪?


① 导读卡片

项目 内容
一句话定位 彻底搞懂 MyBatis-Plus 的 selectBatchIds(Collection<? extends Serializable>) 参数该传什么、怎么从前端获取、如何转换
适合人群 Java 后端开发者、MyBatis-Plus 使用者、SpringBoot 项目新手
难度 ⭐⭐(简单,适合所有 CRUD 开发者)
阅读时长 8 分钟
前置知识 MyBatis-Plus 基础 CRUD、SpringBoot 基本使用

② 背景与目标

为什么学?

很多同学刚用 MyBatis-Plus 时,看到 selectBatchIds(Collection<? extends Serializable> idList) 这个方法的参数类型会犯愁:

  • Collection 是接口,不能 new,到底传什么?

  • 前端传的 ID 列表是字符串,怎么转?

  • 传 ArrayList 还是 HashSet?

  • idList 为空会怎样?

这些问题看着简单,但实际写代码时就是卡住。

学完能怎样?

  • ✅ 看到 Collection 参数就知道传 List

  • ✅ 能处理前端两种传参方式:数组 + 逗号分隔字符串

  • ✅ 知道为什么要做空值校验

  • ✅ 不会写出 WHERE id IN () 的 SQL 语法错误


③ 概念与原理

Collection 是什么?

Collection 是 Java 集合框架的 顶级接口,定义了一组元素的集合操作规范:

java 复制代码
// Collection 接口的继承关系
Iterable
  └── Collection         ← 这是顶级接口
        ├── List         ← 有序、可重复(ArrayList、LinkedList)
        └── Set          ← 无序、不可重复(HashSet、TreeSet)

selectBatchIds 方法签名

java 复制代码
// 来自 MyBatis-Plus BaseMapper
List<T> selectBatchIds(Collection<? extends Serializable> idList)
  • 参数Collection<? extends Serializable> --- 任何实现了 Serializable 的元素的集合

  • 返回值List<T> --- 查询到的实体列表

  • 底层 SQLWHERE id IN (?, ?, ?)

为什么不用 Integer\[\] 或 List 本身?

MyBatis-Plus 选择 Collection<? extends Serializable> 是为了 最大灵活性

  • List<Integer> 能用 ✅(List 实现 Collection,Integer 实现 Serializable)

  • Set<String> 能用 ✅(Set 实现 Collection,String 实现 Serializable)

  • ArrayList<Long> 能用 ✅

  • Collection<Integer> 本身也能传 ✅(实际传的是它的实现类)

简单记:只要元素类型实现了 Serializable,集合是 List 或 Set 都可以传。


④ 逻辑与对比

List vs Set 传参怎么选?

对比维度 List(推荐 ✅) Set(特定场景)
前端传参适配 天然适配数组/JSON 数组 需要手动转换
ID 顺序 保留前端传入顺序 不保证顺序
重复 ID 允许重复(SQL 中 IN 允许重复) 自动去重
95% 场景使用 极少

前端传参方式对比

传参方式 示例 推荐度
JSON 数组(POST) {"ids": [1,2,3]} ⭐⭐⭐⭐⭐ 最常用
逗号分隔字符串 ?ids=1,2,3 ⭐⭐⭐ 简单查询
多个同名参数 ?ids=1&ids=2&ids=3 ⭐⭐ 方便但少见

⑤ 核心详解

5.1 最推荐的传值方式:List

java 复制代码
// ✅ 正确且推荐:传 List
List<Long> idList = Arrays.asList(1L, 2L, 3L);
userMapper.selectBatchIds(idList);

5.2 场景一:前端传 JSON 数组(POST)

前端请求:

POST /user/batch/query Content-Type: application/json ​ {"ids": 1, 2, 3}

后端代码:

java 复制代码
@Data
public class BatchQueryDTO {
    private List<Long> ids;  // 直接映射前端 JSON 数组
}
​
@RestController
@RequestMapping("/user")
public class UserController {
​
    @Autowired
    private UserMapper userMapper;
​
    @PostMapping("/batch/query")
    public List<User> batchQuery(@RequestBody BatchQueryDTO dto) {
        List<Long> idList = dto.getIds();
​
        // ⚠️ 重要:空值校验
        if (idList == null || idList.isEmpty()) {
            return Collections.emptyList();
        }
​
        return userMapper.selectBatchIds(idList);
    }
}

5.3 场景二:前端传逗号分隔字符串(GET)

前端请求:

GET /user/batch/query?ids=1,2,3

后端代码:

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
​
    @Autowired
    private UserMapper userMapper;
​
    @GetMapping("/batch/query")
    public List<User> batchQuery(@RequestParam("ids") String idsStr) {
​
        // 1. 空值校验
        if (StringUtils.isEmpty(idsStr)) {
            return Collections.emptyList();
        }
​
        // 2. 分割字符串 → 转 Long → 收集成 List
        List<Long> idList = Arrays.stream(idsStr.split(","))
                .map(String::trim)       // 去除空格
                .map(Long::parseLong)    // 转 Long
                .collect(Collectors.toList());
​
        // 3. 调 MyBatis-Plus
        return userMapper.selectBatchIds(idList);
    }
}

5.4 场景三:前端传多个同名参数

前端请求:

GET /user/batch/query?ids=1&ids=2&ids=3

后端代码:

java 复制代码
@GetMapping("/batch/query")
public List<User> batchQuery(@RequestParam("ids") List<Long> ids) {
    // Spring 会自动将多个同名参数解析为 List
    // 注意:这里 ids 可能是 null 或空列表
    if (ids == null || ids.isEmpty()) {
        return Collections.emptyList();
    }
    return userMapper.selectBatchIds(ids);
}

5.5 完整 Mapper 和 Service 示例

java 复制代码
// ====== 实体类 ======
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
}

// ====== Mapper 接口 ======
@Repository
public interface UserMapper extends BaseMapper<User> {
    // 继承 BaseMapper 就有了 selectBatchIds 方法
}

// ====== Service ======
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> getUsersByIds(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return Collections.emptyList();
        }
        return userMapper.selectBatchIds(ids);
    }
}

5.6 ⚠️ 空值校验是必须的

java 复制代码
// ❌ 错误示范:空列表传进去会报 SQL 语法错误
List<Long> ids = new ArrayList<>(); // 空列表
userMapper.selectBatchIds(ids);
// → SQL: WHERE id IN () → ❌ SQL 语法错误
// ✅ 正确做法:判断空值
if (ids == null || ids.isEmpty()) {
    return Collections.emptyList(); // 返回空结果
}

5.7 ⚡ 性能建议:ID 数量控制在 1000 以内

java 复制代码
// 当 ID 数量过大时,分批查询
public List<User> getUsersByIdsSafe(List<Long> ids) {
    if (ids == null || ids.isEmpty()) {
        return Collections.emptyList();
    }

    List<List<Long>> batches = Lists.partition(ids, 1000); // Guava 工具
    List<User> result = new ArrayList<>();

    for (List<Long> batch : batches) {
        result.addAll(userMapper.selectBatchIds(batch));
    }
    return result;
}

⑥ 案例实战

完整案例:批量查询用户信息

需求: 前端传选中的用户 ID,后端返回用户列表。

java 复制代码
@RestController
@RequestMapping("/api/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 场景:前端表格多选后点击"批量查询"
     * 请求:POST /api/user/batch
     * Body: {"ids": [1, 2, 3, 4, 5]}
     */
    @PostMapping("/batch")
    public R<List<User>> batchQueryUsers(@RequestBody @Valid BatchQueryDTO dto) {
        List<Long> ids = dto.getIds();

        // 校验
        if (CollectionUtils.isEmpty(ids)) {
            return R.fail("请至少选择一个用户");
        }

        // 限制数量
        if (ids.size() > 1000) {
            return R.fail("一次最多查询1000个用户");
        }

        // 执行查询
        List<User> users = userService.getUsersByIds(ids);
        return R.ok(users);
    }
}

控制台日志验证

复制代码
-- 当传入 ids=[1, 2, 3] 时,实际生成的 SQL:
SELECT id, name, age FROM user WHERE id IN (1, 2, 3)
-- 查询结果:3 条记录

⑦ 避坑 & 最佳实践

❌ 常见错误

错误 1:想传 Collection 但不知道传什么
java 复制代码
// ❌ 错误:Collection 是接口,不能 new
Collection<Long> ids = new Collection<>(); // 编译错误!
// ✅ 正确:传 List(ArrayList 是最常用的实现类)
List<Long> ids = new ArrayList<>();
错误 2:没做空值校验就调用
java 复制代码
// ❌ 错误:前端可能传空数组
List<Long> ids = dto.getIds(); // 可能是空列表
return userMapper.selectBatchIds(ids);
// → SQL: WHERE id IN () → 报错!
// ✅ 正确:空值校验
if (ids == null || ids.isEmpty()) {
    return Collections.emptyList();
}
错误 3:字符串分割时没处理空格
java 复制代码
// ❌ 错误:前端传 "1, 2, 3"(有空格)会抛 NumberFormatException
Arrays.stream("1, 2, 3".split(","))
      .map(Long::parseLong)  // " 2" 无法解析!
// ✅ 正确:trim 后再解析
Arrays.stream("1, 2, 3".split(","))
      .map(String::trim)       // 先去掉空格
      .map(Long::parseLong)    // 再解析

✅ 最佳实践清单

实践 说明
优先用 List 99% 场景传 List<Long> / List<Integer> / List<String>
空值校验 调用前判断 nullisEmpty()
ID 数量控制 单次 IN 查询建议 ≤ 1000 条
字符串分割加 trim 防止前端传参带空格导致解析异常
类型匹配 确保泛型类型实现了 Serializable(Long/Integer/String 都满足)

⑧ 总结 & 路线图

记住了什么?

知识点 一句话总结
参数传什么 List<Long> / List<Integer> / List<String>
底层原理 List 实现了 Collection,基本类型实现了 Serializable
前端传参 JSON 数组(POST)或逗号字符串(GET)
空值校验 必须做,否则 WHERE id IN () 报错
分批查询 ID 超过 1000 时用 Guava Lists.partition 分批

下一步去哪?

  • 🔗 MyBatis-Plus 条件构造器QueryWrapper / LambdaQueryWrapper 复杂查询

  • 🔗 Page 分页查询selectPage 方法使用

  • 🔗 批量插入:MyBatis-Plus 批量插入的性能优化