MyBatis-Plus基本CRUD
基本CRUD
MyBatis-Plus中的基本CRUD在内置的BaseMapper中已得到了实现,可以直接使用
BaseMapper接口源码如下:

下面进行逐个验证
增删改查
int insert(T entity);

java
@SpringBootTest
public class MybatisPlusTest {
@Autowired
private UserMapper userMapper;
/**
* 插入一条记录
*/
@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);
}
}
执行结果

所获取的id为1996416071225659393,这是因为 MyBatis-Plus 在实现插入数据时,会默认基于雪花算法的策略生成id。
int deleteById(Serializable id);

java
/**
* 根据id删除
*/
@Test
public void deleteById(){
User user = new User();
user.setId(1996416071225659393L);
int res = userMapper.deleteById(user);
System.out.println("受影响的行数:"+res);
}
User 类中,id 字段定义的是 Long 类型,而 setId() 方法是 @Data 自动生成的,参数类型也是 Long,所以调用 user.setId(...) 时,传入的参数类型必须能匹配 Long 类型,加 L 的作用是把默认的 int 类型字面量显式改成 Long 类型,让参数类型和方法要求的类型完全一致。

int deleteById(T entity);

java
/**
* 根据id、实体(ID)删除
*/
@Test
public void deleteById(){
User user = new User();
user.setId(5L);
int res = userMapper.deleteById(user);
System.out.println("受影响的行数:"+res);
}

int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

其中 @Param 是 MyBatis 核心的参数命名注解,其核心作用是给方法参数显式指定一个名称,让 MyBatis 能在 XML 映射文件 / 注解 SQL 中精准、无歧义地引用这个参数。
java
/**
* 根据Map删除
*/
@Test
public void deleteByMap(){
Map<String,Object> map = new HashMap<>();
map.put("name","Steve");
map.put("age",12);
int res = userMapper.deleteByMap(map);
System.out.println("受影响的行数:"+res);
}
即删除名字为Steve且年龄为12的记录

int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

java
/**
* QueryWrapper是Mybatis-Plus提供的条件构造器
* 可以构造复杂的SQL查询条件
*/
@Test
public void deleteWithQueryWrapper(){
QueryWrapper<User> wrapper=new QueryWrapper<>();
wrapper.eq("name","Steve").eq("age",12).like("email","baomidou");
int res=userMapper.delete(wrapper);
System.out.println("受影响的行数:"+res);
}
即删除名字为Steve,12岁,邮箱名称包含baomidou内容的记录

int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);

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 + " 条记录");
}

int updateById(@Param(Constants.ENTITY) T entity);

java
/**
* 根据id修改
*/
@Test
public void updateById(){
User user=new User();
user.setId(1L);
user.setAge(99);
userMapper.updateById(user);
}

int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

java
/**
* 复杂条件wrapper修改
*/
@Test
public void updateWithWrapper(){
UpdateWrapper<User> wrapper=new UpdateWrapper<>();
wrapper.set("age",89)
.set("email","update@email.com")
.eq("name","张三")
.or()
.eq("name","李四");
userMapper.update(null,wrapper);
}
MP 会把上述代码自动转换为以下 SQL 执行:
sql
UPDATE user
SET age = 89, email = 'update@email.com'
WHERE name = '张三' OR name = '李四';

T selectById(Serializable id);

java
/**
* 根据id查询用户信息
*/
@Test
public void testSelectById(){
User user = userMapper.selectById(4L);
System.out.println(user);
}

List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

java
/**
* 查询固定的几个id
*/
@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);
}

List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

java
/**
* 通过map查询用户信息
*/
@Test
public void testSelectByMap(){
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);
}

T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper)

selectOne 方法的核心价值是强制保证查询结果的唯一性(多条抛异常、无则返回 null)
适用场景:
按业务唯一标识查询单条记录
按组合唯一条件查询单条记录
状态限定的唯一记录查询
唯一配置项查询
java
/**
* 测试查询结果的唯一性
*/
@Test
void testSelectOne() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("id", 1);
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}

boolean exists(Wrapper<T> queryWrapper)

java
/**
* 根据 Wrapper 条件,判断是否存在记录
*/
@Test
void testExists() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("email", "test");
Boolean res = userMapper.exists(wrapper);
System.out.println(res);
}

Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

java
/**
* 根据 Wrapper 条件,查询总记录数
*/
@Test
void testSelectCount() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("email", "test");
Long res = userMapper.selectCount(wrapper);
System.out.println(res);
}

List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

java
/**
* 根据 entity 条件,查询全部记录,list封装操作类
*/
@Test
void testSelectListEntity() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("email", "test");
List<User> list = userMapper.selectList(wrapper);
list.forEach(System.out::println);
}
//selectList()根据MP内置的条件构造器查询-个list集合,null表示没有条件,即查询所有
@Test
void testSelectListEntity1() {
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}


List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

java
/**
* 根据 entity 条件,查询全部记录,map封装操作类
*/
@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "email")
.ge("age", 20);
List<Map<String, Object>> userMaps = userMapper.selectMaps(queryWrapper);
for (Map<String, Object> map : userMaps) {
String name = (String) map.get("name");
String email = (String) map.get("email");
System.out.println("用户名:" + name + ",邮箱:" + email);
}
}

List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

java
/**
* 根据 Wrapper 条件,查询全部记录,只返回第一个字段的值
*/
@Test
void testSelectObjs() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("email", "test");
List<Object> list = userMapper.selectObjs(wrapper);
list.forEach(System.out::println);
}

分页
<P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

方法参数 P page 要求传入一个 IPage 接口的实现类,作为入参承载分页参数,而方法返回值 P 并非新对象,而是返回被 MP 填充了分页结果的同一个 page 对象,以此实现 page 兼具入参(传分页参数)和出参(返回分页结果)的特性。
其中 IPage 是 MyBatis-Plus 为分页功能定义的顶层抽象接口,它的核心作用是标准化分页数据的访问规则,规定了分页操作中数据列表、分页元信息(当前页、页大小、总记录数等)的获取和设置方式,是 MP 分页体系的通用规范。

Page 是 IPage 最常用的默认实现类,也是 MP 推荐使用的分页容器
需要指定泛型

无需自定义实现,直接通过构造器指定参数

在编写分页测试函数前需要添加自定义配置类
java
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
MyBatis-Plus的分页功能并非MyBatis原生支持的特性,其实现核心是基于物理分页机制------即通过拦截器在SQL执行环节改写原始查询语句,动态拼接适配的分页条件来完成分页。需要注意的是,MyBatis-Plus并不会默认开启分页功能,这是因为不同数据库的分页语法存在显著差异(例如MySQL使用LIMIT/OFFSET语法,Oracle依赖ROWNUM嵌套查询,SQL Server则采用OFFSET...FETCH NEXT等语法),无法统一默认配置分页规则。
对应的配置类核心作用,是向Spring容器中注册MybatisPlusInterceptor拦截器容器,并将指定了数据库类型(DbType)的PaginationInnerInterceptor分页拦截器注入其中,从而将该拦截器纳入MyBatis-Plus的SQL执行流程。其中,PaginationInnerInterceptor是实现分页逻辑的关键组件,它必须依据指定的DbType来生成对应数据库的分页SQL语句,若未指定或指定的数据库类型错误,会直接导致分页SQL语法错误,最终引发执行失败。
本质上,MyBatis-Plus实现高效的物理分页,完全依赖分页拦截器拦截SQL执行过程、动态拼接适配当前数据库的分页语法;而该配置类的核心价值就是完成分页拦截器的注册与注入,让MyBatis-Plus能够自动识别分页意图、处理全套分页逻辑(包括改写SQL、查询总记录数、校验分页参数等),而非退化为低效的内存分页方式。
分页测试函数
java
/**
* 分页查询条件,返回实体类
*/
@Test
public void testPage(){
Page<User> page = new Page<>(1, 5); //设置分页参数
userMapper.selectPage(page, null);
List<User> list = page.getRecords(); //获取分页数据
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
执行 MP 提供的分页查询方法 userMapper.selectPage(page, null); ,触发分页逻辑并将结果填充到 page 对象中。这一步会触发配置的 PaginationInnerInterceptor 拦截器,拦截 SQL 执行请求,先根据 Page 的参数和指定的 DbType,给原始查询 SQL 拼接分页语法。
执行结果


java
/**
* 分页查询条件,map封装操作类
*/
@Test
public void testSelectMapsPage() {
Page<Map<String, Object>> page = new Page<>(1, 5);
userMapper.selectMapsPage(page, null);
List<Map<String, Object>> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
