MyBatis-Plus 查询构建实战:从零到精通条件构造器
MyBatis-Plus(简称 MP)的查询构建功能是个大杀器,尤其是它的 QueryWrapper
,能让你像搭积木一样拼出各种查询条件。这篇博客不整虚的,直接上大量实例,带你把这块玩熟。咱们会从 Mapper 和 Service 层代码开始,一步步搞定常见的查询场景。
1. 先搭个基础环境
假设我们用 Spring Boot + MySQL,项目里得加依赖:
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
配置文件(application.yml
):
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
表结构用两个:user
(用户表)和 order
(订单表),方便后面多表联查:
user
:id
(bigint)、username
(varchar)、age
(int)、status
(int, 0=禁用, 1=启用)order
:id
(bigint)、user_id
(bigint)、order_name
(varchar)、amount
(decimal)
2. 实体类、Mapper 和 Service 代码
2.1 实体类
先把实体类写好:
java
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("user")
public class User {
private Long id;
private String username;
private Integer age;
private Integer status;
}
@Data
@TableName("order")
public class Order {
private Long id;
private Long userId;
private String orderName;
private BigDecimal amount;
}
2.2 Mapper 层
Mapper 继承 BaseMapper
,基础 CRUD 都不用写,后面加点自定义查询:
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface UserMapper extends BaseMapper<User> {
// 自定义多表联查
@Select("SELECT u.*, o.order_name, o.amount FROM user u LEFT JOIN `order` o ON u.id = o.user_id WHERE u.status = 1")
List<Map<String, Object>> selectActiveUsersWithOrders();
}
public interface OrderMapper extends BaseMapper<Order> {
}
2.3 Service 层
Service 层继承 IService
,再写个实现类封装查询逻辑:
java
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
List<User> findByAgeRange(Integer minAge, Integer maxAge);
List<User> findByNameLike(String keyword);
List<Map<String, Object>> findActiveUsersWithOrders();
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> findByAgeRange(Integer minAge, Integer maxAge) {
return baseMapper.selectList(new QueryWrapper<User>()
.between("age", minAge, maxAge));
}
@Override
public List<User> findByNameLike(String keyword) {
return baseMapper.selectList(new QueryWrapper<User>()
.like("username", keyword));
}
@Override
public List<Map<String, Object>> findActiveUsersWithOrders() {
return baseMapper.selectActiveUsersWithOrders();
}
}
public interface OrderService extends IService<Order> {
}
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
}
分页得加个配置:
java
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
3. QueryWrapper 实战:各种查询场景
好了,基础搭好了,咱们直接上实例,玩转 QueryWrapper
。
3.1 简单等值查询
查 username = "张三"
的用户:
java
@SpringBootTest
class QueryWrapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testEqual() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", "张三");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
}
eq
是等于,简单直接。
3.2 范围查询
查年龄在 20-30 之间的用户:
java
@Test
void testBetween() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
between
包含边界值,查个范围特别方便。
3.3 模糊查询
查用户名包含"张"的:
java
@Test
void testLike() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("username", "张");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
like
是模糊匹配,左右都加 %
,还有 likeLeft
(左模糊)和 likeRight
(右模糊)可选。
3.4 多条件组合
查年龄大于 25 且状态为启用的用户:
java
@Test
void testMultiCondition() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 25) // 大于
.eq("status", 1); // 等于
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
gt
是大于,条件用链式调用,逻辑默认是 AND。
3.5 OR 条件
查年龄小于 20 或者状态为禁用的:
java
@Test
void testOr() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lt("age", 20) // 小于
.or() // 或
.eq("status", 0);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
or()
切换成 OR 逻辑,挺灵活。
3.6 分页查询
查启用用户,按年龄倒序,第 2 页,每页 3 条:
java
@Test
void testPage() {
Page<User> page = new Page<>(2, 3); // 第2页,每页3条
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1)
.orderByDesc("age");
Page<User> result = userMapper.selectPage(page, wrapper);
System.out.println("总条数: " + result.getTotal());
result.getRecords().forEach(System.out::println);
}
orderByDesc
是倒序,orderByAsc
是正序。
3.7 动态条件
根据输入动态拼条件,比如年龄范围可选:
java
@Test
void testDynamic() {
Integer minAge = 20; // 可为空
Integer maxAge = null;
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge(minAge != null, "age", minAge) // 大于等于,可动态加
.le(maxAge != null, "age", maxAge); // 小于等于,可动态加
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
ge
和 le
第一个参数是布尔值,条件成立才加进去,动态查询很实用。
3.8 IN 查询
查 ID 在某个列表里的用户:
java
@Test
void testIn() {
List<Long> ids = Arrays.asList(1L, 2L, 3L);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.in("id", ids);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
in
支持集合,查多值很方便。
4. 多表联查场景
单表玩腻了,咱们试试多表。需求:查启用用户和他们的订单。
4.1 用 Service 两次查询
先查用户,再查订单:
java
@Autowired
private UserService userService;
@Autowired
private OrderService orderService;
@Test
void testMultiTable() {
List<User> users = userService.list(new QueryWrapper<User>().eq("status", 1));
for (User user : users) {
List<Order> orders = orderService.list(new QueryWrapper<Order>().eq("user_id", user.getId()));
System.out.println(user.getUsername() + " 的订单:");
orders.forEach(order -> System.out.println(order.getOrderName() + " - " + order.getAmount()));
}
}
简单,但查两次,数据量大时有点慢。
4.2 自定义 SQL 联查
直接用 Mapper 里的自定义方法:
java
@Test
void testCustomJoin() {
List<Map<String, Object>> result = userService.findActiveUsersWithOrders();
result.forEach(System.out::println);
}
输出可能是:
ini
{id=1, username=张三, age=25, status=1, order_name=手机, amount=1999.00}
{id=2, username=李四, age=30, status=1, order_name=电脑, amount=5999.00}
一次查完,效率高。
5. 小结
MyBatis-Plus 的 QueryWrapper
真是个好帮手,等值、范围、模糊、多条件、分页、动态查询啥都能搞定。单表用 BaseMapper
和 IService
就很舒服,多表联查稍微麻烦点,可以两次查询凑合,或者写自定义 SQL 一步到位。