一、核心概念

方法 说明
selectById(Serializable id) 根据主键查询一条记录
selectList(@Param("ew") Wrapper<T> queryWrapper) 查询多条记录,返回 List<T>
selectOne(Wrapper<T> queryWrapper) 查询单条记录,返回 T,若多条则抛异常
selectCount(Wrapper<T> queryWrapper) 统计满足条件的记录数,返回 Long
selectPage(Page<T> page, Wrapper<T> queryWrapper) 分页查询,返回 IPage<T>
selectMaps(Wrapper<T> queryWrapper) 查询结果以 List<Map<String, Object>> 返回
selectObjs(Wrapper<T> queryWrapper) 查询单列字段,返回 List<Object>(如 ID 列表)

📌 所有方法均来自 BaseMapper<T> 接口,无需手动实现。


二、准备工作(Spring Boot + MySQL)

步骤 1:数据库表准备

CREATE DATABASE mydemo;
USE mydemo;

CREATE TABLE user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    age INT,
    email VARCHAR(100),
    status TINYINT DEFAULT 1 COMMENT '1:正常, 0:禁用',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

插入测试数据:

INSERT INTO user (name, age, email) VALUES 
('张三', 25, 'zhangsan@example.com'),
('李四', 30, 'lisi@example.com'),
('王五', 22, 'wangwu@example.com');

步骤 2:实体类 User.java

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private Integer status;
    private LocalDateTime createTime;
}

步骤 3:Mapper 接口

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 无需写方法,继承 BaseMapper 即可使用所有 select* 方法
}

步骤 4:配置 application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydemo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 开发环境打印 SQL

步骤 5:启用 Mapper 扫描(主类)

@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

三、详细查询操作示例

✅ 1. selectById:根据主键查询

@Autowired
private UserMapper userMapper;

@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) {
    return userMapper.selectById(id);
}

🔍 说明:直接传入主键值,返回 User 对象,若不存在返回 null


✅ 2. selectList:查询所有或条件查询

// 查询所有
List<User> allUsers = userMapper.selectList(null);

// 条件查询:年龄 > 23
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 23);
List<User> users = userMapper.selectList(wrapper);

🔍 支持链式调用:

wrapper.eq("status", 1)
       .like("name", "张")
       .orderByDesc("id");

✅ 3. selectOne:查询单条记录

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三").eq("age", 25);

User user = userMapper.selectOne(wrapper);

⚠️ 注意:如果查询结果 多于一条selectOne 会抛出 TooManyResultsException 异常。
✅ 适用于“唯一条件”查询,如用户名唯一、邮箱唯一等。


✅ 4. selectCount:统计数量

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 20).eq("status", 1);

Long count = userMapper.selectCount(wrapper);
System.out.println("满足条件的用户数:" + count);

✅ 性能优势:只查 COUNT(*),不查数据,适合分页总数、统计报表。


✅ 5. selectPage:分页查询(需配置分页插件)

步骤 1:配置分页插件

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

步骤 2:执行分页查询

// 第1页,每页2条
Page<User> page = new Page<>(1, 2);

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");

IPage<User> result = userMapper.selectPage(page, wrapper);

// 获取数据
List<User> records = result.getRecords();
long total = result.getTotal();
int pages = result.getPages();
boolean hasNext = result.hasNext();

✅ 返回 IPage 对象,包含分页元数据。


✅ 6. selectMaps:查询结果为 Map 列表

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("name", "age") // 指定字段
       .gt("age", 20);

List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);

返回示例:

[
  {"name": "张三", "age": 25},
  {"name": "李四", "age": 30}
]

✅ 用途:无需实体类、只查部分字段、前端直接渲染。


✅ 7. selectObjs:查询单列值列表

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id"); // 只查 id 字段

List<Object> ids = userMapper.selectObjs(wrapper);
// 返回 [1, 2, 3],注意是 Object 类型

✅ 用途:获取 ID 列表用于后续查询,如:

List<Long> longIds = ids.stream().map(obj -> (Long) obj).collect(Collectors.toList());

四、常见错误与解决方案

错误 原因 解决方案
Invalid bound statement (not found) Mapper 未被扫描 检查 @MapperScan 路径
Property 'xxx' not found Wrapper 使用了不存在的属性 使用 QueryWrapper("column_name")LambdaQueryWrapper
分页无效,返回所有数据 未配置 PaginationInnerInterceptor 添加分页插件配置类
selectOne 抛出 TooManyResultsException 查询结果不唯一 改用 selectList 或加强查询条件
selectMaps 返回字段名为数据库名(如 user_name) 未开启驼峰映射 配置 map-underscore-to-camel-case: true

五、注意事项

  1. selectOne 必须确保结果唯一,否则会抛异常。
  2. 分页查询必须配置 PaginationInnerInterceptor,否则 selectPage 不生效。
  3. selectMapsselectObjs 返回的是原始类型,注意类型转换。
  4. 字段名映射:Java 驼峰自动转下划线,如 userNameuser_name
  5. null 值处理:查询条件中 null 值不会自动忽略,需手动判断。

六、使用技巧

✅ 1. 使用 LambdaQueryWrapper(类型安全)

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.gt(User::getAge, 20)
       .like(User::getName, "张");

List<User> users = userMapper.selectList(wrapper);

✅ 优势:字段名重构安全,避免字符串错误。


✅ 2. 动态查询条件(避免 NPE)

String name = "张";
Integer age = null;

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(name), "name", name)
       .gt(age != null, "age", age); // 条件成立才添加

condition 参数控制是否添加条件。


✅ 3. 只查指定字段(提升性能)

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "age"); // 只查这三个字段
List<User> users = userMapper.selectList(wrapper);

✅ 减少网络传输和内存占用。


✅ 4. 使用 selectMapsPage 分页返回 Map

Page<Map<String, Object>> page = new Page<>(1, 2);
IPage<Map<String, Object>> result = userMapper.selectMapsPage(page, wrapper);

✅ 适用于 API 接口直接返回字段映射。


七、最佳实践与性能优化

实践 说明
✅ 使用 LambdaQueryWrapper 类型安全,避免拼写错误
✅ 分页必配 PaginationInnerInterceptor 否则分页无效
✅ 查询只选必要字段 避免 SELECT *
✅ 统计用 selectCount selectList + size() 更高效
✅ 复杂查询使用 @Param("ew") 在自定义 SQL 中使用 Wrapper
✅ 缓存热点查询结果 结合 Redis 缓存 selectByIdselectCount
✅ 避免在循环中调用 selectById 改用 selectBatchIds 批量查询
✅ 使用 selectObjs 获取 ID 列表 用于后续批量操作

八、扩展:批量查询 selectBatchIds

List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.selectBatchIds(ids);

✅ 高效替代多次 selectById


九、总结:查询方法选择指南

需求 推荐方法
根据主键查一条 selectById(id)
查询多条 selectList(wrapper)
查询唯一记录 selectOne(wrapper)(确保唯一)
统计数量 selectCount(wrapper)
分页查询 selectPage(page, wrapper) + 分页插件
返回 Map 列表 selectMaps(wrapper)
返回单列值列表 selectObjs(wrapper)
批量主键查询 selectBatchIds(List<ID>)