目录
入门案例当中的"查询所有"调用的是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));
}
结果:
