MyBatis-Plus基本CRUD

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());
    }
相关推荐
程序员岳焱1 小时前
Java泛型高级玩法:通配符、上下界与类型擦除避坑实战(纯干货,附完整工具类)
java·后端·程序员
❀͜͡傀儡师1 小时前
maven 仓库的Central Portal Namespaces 怎么验证
java·maven·nexus
豐儀麟阁贵1 小时前
9.3获取字符串信息
java·开发语言·前端·算法
Mai Dang1 小时前
黑马Mybatis-Plus学习笔记
笔记·学习·mybatis
武子康1 小时前
大数据-175 Elasticsearch Term 精确查询与 Bool 组合实战:range/regexp/fuzzy 全示例
大数据·后端·elasticsearch
YJlio1 小时前
第9章小结(9.19):Sysinternals 安全工具组合拳与脚本清单
java·学习·平面
甜鲸鱼1 小时前
【Spring Boot + OpenAPI 3】开箱即用的 API 文档方案(SpringDoc + Knife4j)
java·spring boot·后端
foxsen_xia1 小时前
go(基础10)——错误处理
开发语言·后端·golang
robch1 小时前
Java后端优雅的实现分页搜索排序-架构2
java·开发语言·架构