一、核心概念
方法 | 说明 |
---|---|
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 |
五、注意事项
selectOne
必须确保结果唯一,否则会抛异常。- 分页查询必须配置
PaginationInnerInterceptor
,否则selectPage
不生效。 selectMaps
和selectObjs
返回的是原始类型,注意类型转换。- 字段名映射:Java 驼峰自动转下划线,如
userName
→user_name
。 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 缓存 selectById 、selectCount 等 |
✅ 避免在循环中调用 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>) |