源本科技 | 码上会

MyBatis-Plus Mapper Interface

2026/04/02
2
0

引言

MyBatis-Plus(简称 MP)是在 MyBatis 基础上的增强工具,Mapper Interface 是 MP 实现数据库操作的核心组件,它无需编写 XML 映射文件,通过继承通用接口即可直接使用单表的增删改查、分页、条件构造等基础功能,大幅简化开发流程。

核心概念

什么是 Mapper Interface

MP 中的 Mapper Interface 是数据访问层(DAO)的接口定义,继承 MP 提供的 BaseMapper<T> 通用接口后,无需实现类,即可自动获得单表的全套 CRUD 操作能力。

核心作用

  • 替代传统 MyBatis 中手动编写的 Mapper 接口 + XML 映射文件

  • 直接调用内置方法完成单表操作,无需重复编写 SQL

  • 支持自定义方法扩展,兼容原生 MyBatis 语法

  • 结合条件构造器,实现复杂条件查询

基础使用

准备实体类

实体类对应数据库表,通过 MP 注解完成映射:

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data // Lombok 简化 getter/setter
@TableName("t_user") // 对应数据库表名(类名与表名一致可省略)
public class User {
    // 主键自增,对应数据库主键字段
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username; // 字段名与属性名一致,自动映射
    private Integer age;
    private String email;
}

编写 Mapper

创建接口并继承 BaseMapper<T>,泛型指定对应实体类:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

// @Mapper:让 Spring 扫描到该接口,生成代理对象
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 无需编写任何方法,直接继承 BaseMapper 所有能力
}

启动类扫描

若 Mapper 接口较多,可在启动类添加 @MapperScan 统一扫描,无需每个接口加 @Mapper

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mapper") // 指定 Mapper 接口包路径
public class MybatisPlusDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusDemoApplication.class, args);
    }
}

测试使用

在 Service/Controller 中注入 Mapper,直接调用方法:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
public class UserMapperTest {
    // 注入自定义 Mapper
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectAll() {
        // 调用 BaseMapper 内置方法:查询所有数据
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }
}

BaseMapper

BaseMapper<T> 提供了单表 17 个核心方法,覆盖增删改查全场景,按功能分类如下:

插入(Insert)

方法

作用

参数说明

int insert(T entity)

插入一条数据

entity:实体对象,非空字段会自动拼接 SQL

示例

User user = new User();
user.setUsername("张三");
user.setAge(20);
user.setEmail("zhangsan@xxx.com");
// 返回受影响行数
int rows = userMapper.insert(user);
// 插入后主键会自动回写到实体对象
System.out.println("插入成功,主键:" + user.getId());

删除(Delete)

方法

作用

参数说明

int deleteById(Serializable id)

根据主键删除

id:主键值

int deleteByMap(Map<String, Object> columnMap)

根据列名条件删除

key= 列名,value= 值,多条件 AND 拼接

int delete(Wrapper<T> queryWrapper)

根据条件构造器删除

queryWrapper:自定义条件

int deleteBatchIds(Collection<? extends Serializable> idList)

批量删除

idList:主键集合

示例(条件删除)

// 条件构造器:删除 age < 18 的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lt("age", 18);
int rows = userMapper.delete(wrapper);

更新(Update)

方法

作用

参数说明

int updateById(T entity)

根据主键更新

仅更新实体中非空字段

int update(T entity, Wrapper<T> updateWrapper)

根据条件更新

entity:要更新的字段,updateWrapper:更新条件

示例

// 根据主键更新
User user = new User();
user.setId(1L);
user.setUsername("张三更新");
userMapper.updateById(user);

// 条件更新:age=20 的用户,邮箱改为 xxx@xxx.com
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("age", 20);
User updateUser = new User();
updateUser.setEmail("xxx@xxx.com");
userMapper.update(updateUser, wrapper);

查询(Select)

查询是最常用功能,支持单条、列表、分页、计数等:

方法

作用

参数说明

T selectById(Serializable id)

根据主键查询单条

id:主键值

List<T> selectBatchIds(Collection<? extends Serializable> idList)

批量查询

idList:主键集合

List<T> selectByMap(Map<String, Object> columnMap)

根据列名条件查询

同 deleteByMap

T selectOne(Wrapper<T> queryWrapper)

查询单条(条件唯一)

queryWrapper:条件

Long selectCount(Wrapper<T> queryWrapper)

查询总条数

条件为 null 则查全表总数

List<T> selectList(Wrapper<T> queryWrapper)

查询列表

条件为 null 则查全表

List<Map<String, Object>> selectMaps(Wrapper<T> queryWrapper)

查询列表(返回 Map)

适合自定义返回字段

IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)

分页查询

page:分页对象,wrapper:条件

示例(分页查询)

// 配置分页插件(必须先配置,否则分页无效)
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

// 测试分页:第 1 页,每页 10 条
IPage<User> page = new Page<>(1, 10);
// 条件:age > 18
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18);
IPage<User> userPage = userMapper.selectPage(page, wrapper);
// 分页结果
System.out.println("总条数:" + userPage.getTotal());
System.out.println("当前页数据:" + userPage.getRecords());

自定义 Mapper

MP 支持兼容原生 MyBatis,可在 Mapper Interface 中自定义方法,满足复杂业务需求:

注解方式(无 XML)

适合简单 SQL,直接在方法上添加 @Select/@Insert 等注解:

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 自定义注解查询:根据用户名模糊查询
    @Select("SELECT * FROM t_user WHERE username LIKE CONCAT('%',#{username},'%')")
    List<User> selectByUsernameLike(@Param("username") String username);
}

XML 方式(复杂 SQL)

适合多表关联、复杂查询,步骤:

  1. resources 下创建 Mapper XML 文件(如 UserMapper.xml

  2. 编写 SQL 映射

  3. 配置文件指定 XML 路径

XML 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 对应 Mapper 接口全类名 -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 自定义查询:根据年龄范围查询 -->
    <select id="selectByAgeRange" resultType="com.example.entity.User">
        SELECT * FROM t_user WHERE age BETWEEN #{minAge} AND #{maxAge}
    </select>
</mapper>

application.yml

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml # 指定 XML 路径
  type-aliases-package: com.example.entity # 实体类别名

Mapper 接口定义

List<User> selectByAgeRange(@Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge);

高级特性

条件构造器

MP 核心特性,无需手写 SQL 拼接条件,支持 =><LIKEIN 等所有条件:

  • QueryWrapper:查询 / 删除条件构造

  • UpdateWrapper:更新条件构造

常用条件方法

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", "张三") // 等于 =
       .gt("age", 18) // 大于 >
       .like("email", "@xxx.com") // 模糊查询 LIKE
       .in("id", 1,2,3) // IN 查询
       .orderByDesc("id"); // 排序

通用 Mapper

若多个表需要相同的通用方法,可创建父类 Mapper 继承 BaseMapper,其他 Mapper 继承父类:

// 父类通用 Mapper
public interface BaseDao<T> extends BaseMapper<T> {
    // 自定义通用方法
}

// 子类 Mapper 继承父类,获得所有能力
public interface UserMapper extends BaseDao<User> {}

逻辑删除

MP 支持逻辑删除(不物理删除数据,通过字段标记),配置后 delete 方法自动变为更新:

  1. 实体类添加逻辑删除字段:

    @TableLogic // 标记逻辑删除字段
    private Integer deleted; // 0=未删除,1=已删除
  2. 配置文件开启:

    mybatis-plus:
      global-config:
        db-config:
          logic-delete-field: deleted # 逻辑删除字段名
          logic-delete-value: 1 # 已删除值
          logic-not-delete-value: 0 # 未删除值

最佳实践

  1. Mapper 职责单一:仅负责数据访问,业务逻辑放在 Service 层

  2. 优先使用条件构造器:避免手写 SQL,减少语法错误

  3. 非空字段更新:更新时仅传递需要修改的字段,MP 自动忽略 null

  4. 分页必须配置插件:否则 selectPage 只会查询所有数据

  5. 避免过度依赖:多表关联、复杂统计 SQL 建议用自定义 XML/ 注解

  6. 统一扫描 Mapper:使用 @MapperScan 替代单个 @Mapper 注解

常见问题

  1. Mapper 注入失败:检查 @Mapper/@MapperScan 注解是否添加,包路径是否正确

  2. 分页不生效:未配置 PaginationInnerInterceptor 分页插件

  3. 字段映射失败:实体类属性与数据库字段不一致,需加 @TableField("字段名")

  4. 逻辑删除无效:未配置 @TableLogic 注解或全局配置


总结

  1. Mapper Interface 是 MP 数据访问核心,继承 BaseMapper<T> 即可获得单表全量 CRUD 能力

  2. 无需 XML 即可完成基础操作,支持自定义方法兼容原生 MyBatis

  3. 核心能力:内置方法、条件构造器、分页、逻辑删除、通用复用

  4. 最佳实践:Mapper 专注数据访问,复杂 SQL 自定义扩展