Skip to content

MyBatis-Plus 完全指南

MyBatis-Plus 是 MyBatis 的增强工具,在 MyBatis 基础上提供通用 CRUD、分页、条件构造器等功能。你可以同时使用两者——用 MyBatis-Plus 的 BaseMapper 处理简单 CRUD,用 MyBatis XML Mapper 处理复杂 SQL。

适用版本:mybatis-plus-boot-starter: 3.5.5(Spring Boot 2.7.x)


一、Maven 依赖

xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>

二、application.yml 配置

yaml
mybatis-plus:
  # XML Mapper 文件位置(自定义 SQL 用 XML 方式时配置)
  mapper-locations: classpath*:/mapper/**/*.xml
  type-aliases-package: com.example.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

三、实体类与注解

java
@Data
@TableName("tb_user")                  // 表名
public class User {
    @TableId(type = IdType.AUTO)        // 主键策略:AUTO 数据库自增
    private Long id;

    @TableField("user_name")            // 字段映射
    private String userName;

    private Integer age;
    private String email;

    @TableField(fill = FieldFill.INSERT)       // 插入自动填充
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE) // 更新自动填充
    private LocalDateTime updateTime;

    @TableLogic                                 // 逻辑删除
    private Integer deleted;

    @TableField(exist = false)                  // 非表字段
    private String remark;
}

常用注解速查

注解用途示例
@TableName指定表名@TableName("tb_user")
@TableId主键 + 生成策略@TableId(type = IdType.AUTO)
@TableField字段映射与配置@TableField("user_name")
@TableLogic逻辑删除@TableLogic
@Version乐观锁@Version
@EnumValue枚举存储值@EnumValue

主键策略 IdType

策略说明
AUTO数据库自增
ASSIGN_ID雪花算法(Long)默认
ASSIGN_UUIDUUID(String)
INPUT手动设置

自动填充

java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

四、Mapper 与 BaseMapper

java
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 继承 BaseMapper 即拥有通用 CRUD
    // 复杂查询(多表 JOIN、子查询)才需要自定义方法

    List<User> selectByCondition(@Param("name") String name,
                                  @Param("minAge") Integer minAge,
                                  @Param("maxAge") Integer maxAge);
}

BaseMapper 核心方法

继承 BaseMapper<T> 后,Mapper 接口自动获得以下通用 CRUD 方法:

方法说明示例
insert(T entity)插入一条记录userMapper.insert(user)
deleteById(Serializable id)按 ID 删除userMapper.deleteById(1L)
deleteByMap(Map<String, Object> columnMap)按 Map 条件删除userMapper.deleteByMap(map)
delete(Wrapper<T> wrapper)条件删除userMapper.delete(wrapper)
deleteBatchIds(Collection<? extends Serializable> idList)批量 ID 删除userMapper.deleteBatchIds(ids)
updateById(T entity)按 ID 更新userMapper.updateById(user)
update(T entity, Wrapper<T> updateWrapper)条件更新userMapper.update(user, wrapper)
selectById(Serializable id)按 ID 查询userMapper.selectById(1L)
selectBatchIds(Collection<? extends Serializable> idList)批量 ID 查询userMapper.selectBatchIds(ids)
selectByMap(Map<String, Object> columnMap)按 Map 查询userMapper.selectByMap(map)
selectOne(Wrapper<T> queryWrapper)查询单条userMapper.selectOne(wrapper)
selectCount(Wrapper<T> queryWrapper)查询总数userMapper.selectCount(wrapper)
selectList(Wrapper<T> queryWrapper)条件查询列表userMapper.selectList(wrapper)
selectMaps(Wrapper<T> queryWrapper)查询 Map 列表userMapper.selectMaps(wrapper)
selectObjs(Wrapper<T> queryWrapper)查询对象列表userMapper.selectObjs(wrapper)
selectPage(IPage<T> page, Wrapper<T> queryWrapper)分页查询userMapper.selectPage(page, wrapper)

自定义 SQL —— 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">
<mapper namespace="com.example.mapper.UserMapper">

    <select id="selectByCondition" resultType="User">
        SELECT * FROM tb_user
        <where>
            <if test="name != null and name != ''">
                AND user_name LIKE CONCAT('%', #{name}, '%')
            </if>
            <if test="minAge != null">
                AND age >= #{minAge}
            </if>
            <if test="maxAge != null">
                AND age <= #{maxAge}
            </if>
        </where>
        ORDER BY id DESC
    </select>

</mapper>

五、通用 CRUD

插入 / 删除 / 更新

java
userMapper.insert(user);                                        // 插入
userMapper.deleteById(1001L);                                   // 按 ID 删
userMapper.deleteBatchIds(Arrays.asList(1001L, 1002L));         // 批量删
userMapper.delete(new LambdaQueryWrapper<User>()
    .eq(User::getStatus, 0));                                   // 条件删
userMapper.updateById(user);                                    // 按 ID 更新
userMapper.update(user, new LambdaUpdateWrapper<User>()         // 条件更新
    .set(User::getStatus, 1).eq(User::getId, 1001L));

查询

java
User user = userMapper.selectById(1001L);                       // 按 ID
List<User> list = userMapper.selectBatchIds(ids);              // 批量 ID
List<User> all = userMapper.selectList(null);                  // 全部
Long count = userMapper.selectCount(null);                      // 总数

// 条件查询
List<User> result = userMapper.selectList(
    new LambdaQueryWrapper<User>()
        .eq(User::getStatus, 1)
        .ge(User::getAge, 18)
        .orderByDesc(User::getCreateTime)
);

六、LambdaQueryWrapper 条件构造器

比较与模糊

java
new LambdaQueryWrapper<User>()
    .eq(User::getName, "张三")           // =
    .ne(User::getStatus, 0)              // !=
    .gt(User::getAge, 18)                // >
    .ge(User::getCreateTime, start)      // >=
    .lt(User::getBalance, 1000)          // <
    .le(User::getAge, 60)                // <=
    .between(User::getAge, 18, 35)       // 区间
    .like(User::getName, "张")            // LIKE '%张%'
    .likeLeft(User::getEmail, "@gmail")  // LIKE '%@gmail'
    .likeRight(User::getPhone, "138")    // LIKE '138%'
    .in(User::getId, ids)                 // IN
    .isNull(User::getEmail)              // IS NULL
    .isNotNull(User::getPhone)           // IS NOT NULL

排序与分组

java
    .orderByAsc(User::getAge)
    .orderByDesc(User::getCreateTime)
    .groupBy(User::getStatus)

条件组合

java
// AND
wrapper.eq(User::getStatus, 1).ge(User::getAge, 18);

// OR
wrapper.eq(User::getName, "张三").or().eq(User::getName, "李四");

// AND 嵌套: status=1 AND (name='张三' OR name='李四')
wrapper.eq(User::getStatus, 1)
    .and(w -> w.eq(User::getName, "张三").or().eq(User::getName, "李四"));

动态条件

java
new LambdaQueryWrapper<User>()
    .like(StringUtils.isNotBlank(name), User::getName, name)
    .eq(status != null, User::getStatus, status)
    .ge(minAge != null, User::getAge, minAge)
    .le(maxAge != null, User::getAge, maxAge);

七、分页

配置分页插件

java
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor interceptor() {
        MybatisPlusInterceptor i = new MybatisPlusInterceptor();
        i.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return i;
    }
}

分页查询

java
Page<User> page = new Page<>(1, 20);   // 第 1 页,每页 20 条
Page<User> result = userMapper.selectPage(page,
    new LambdaQueryWrapper<User>().ge(User::getAge, 18));

result.getTotal();       // 总记录数
result.getPages();       // 总页数
result.getCurrent();     // 当前页
result.getRecords();     // 数据列表

八、Service 层

java
public interface UserService extends IService<User> { }

@Service
public class UserServiceImpl
        extends ServiceImpl<UserMapper, User>
        implements UserService {
}

ServiceImpl 核心方法

继承 ServiceImpl<M extends BaseMapper<T>, T> 后,Service 层自动获得以下增强方法:

方法说明示例
save(T entity)保存一条记录userService.save(user)
saveBatch(Collection<T> entityList)批量保存userService.saveBatch(list)
saveOrUpdate(T entity)保存或更新userService.saveOrUpdate(user)
removeById(Serializable id)按 ID 删除userService.removeById(1L)
removeByMap(Map<String, Object> columnMap)按 Map 删除userService.removeByMap(map)
remove(Wrapper<T> queryWrapper)条件删除userService.remove(wrapper)
removeBatchIds(Collection<? extends Serializable> idList)批量删除userService.removeBatchIds(ids)
updateById(T entity)按 ID 更新userService.updateById(user)
update(Wrapper<T> updateWrapper)条件更新userService.update(wrapper)
update(T entity, Wrapper<T> updateWrapper)按实体条件更新userService.update(user, wrapper)
getById(Serializable id)按 ID 查询userService.getById(1L)
getOne(Wrapper<T> queryWrapper, boolean throwEx)查询单条userService.getOne(wrapper)
list()查询全部userService.list()
list(Wrapper<T> queryWrapper)条件查询userService.list(wrapper)
listByIds(Collection<? extends Serializable> idList)批量 ID 查询userService.listByIds(ids)
listByMap(Map<String, Object> columnMap)按 Map 查询userService.listByMap(map)
listMaps(Wrapper<T> queryWrapper)查询 Map 列表userService.listMaps(wrapper)
listObjs(Wrapper<T> queryWrapper)查询对象列表userService.listObjs(wrapper)
page(IPage<T> page, Wrapper<T> queryWrapper)分页查询userService.page(page, wrapper)
count()查询总数userService.count()
count(Wrapper<T> queryWrapper)条件查询总数userService.count(wrapper)

BaseMapper vs ServiceImpl 选型

场景推荐方式原因
简单 CRUDBaseMapper直接、轻量,无需 Service 层
复杂业务逻辑ServiceImpl封装业务逻辑,便于事务管理
批量操作ServiceImpl提供 saveBatch 等批量方法
链式操作ServiceImpl支持 Lambda 链式查询
需要事务ServiceImpl在 Service 层统一加 @Transactional

层级关系:

ServiceImpl → IService → BaseMapper

  封装业务逻辑 + 事务管理

ServiceImpl 使用示例

java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    // 1. 保存用户
    public boolean addUser(User user) {
        return save(user);
    }

    // 2. 批量保存(每 500 条提交一次)
    public boolean addUsers(List<User> users) {
        return saveBatch(users, 500);
    }

    // 3. 根据 ID 查询
    public User getUserById(Long id) {
        return getById(id);
    }

    // 4. 条件查询
    public List<User> getUsersByStatus(Integer status) {
        return list(new LambdaQueryWrapper<User>()
            .eq(User::getStatus, status));
    }

    // 5. 分页查询
    public Page<User> getUserPage(int current, int size) {
        return page(new Page<>(current, size));
    }

    // 6. 更新用户
    public boolean updateUser(User user) {
        return updateById(user);
    }

    // 7. 删除用户
    public boolean removeUser(Long id) {
        return removeById(id);
    }

    // 8. 复杂业务:带事务的查询并处理
    @Transactional
    public void transferUser(Long fromId, Long toId) {
        User fromUser = getById(fromId);
        User toUser = getById(toId);
        // ... 业务逻辑处理
    }
}

九、SQL 与 API 对照速查

原生 SQLMyBatis-Plus API说明
INSERT INTO ... VALUES ...insert(entity)单条插入
批量 INSERTsaveBatch(list)批量插入
UPDATE ... SET ... WHERE id=?updateById(entity)按 ID 更新
UPDATE ... SET ... WHERE ...update(entity, wrapper)条件更新
DELETE FROM ... WHERE id=?deleteById(id)按 ID 删除
DELETE FROM ... WHERE id IN (...)deleteBatchIds(ids)批量删除
SELECT * FROM ... WHERE id=?selectById(id)按 ID 查询
SELECT * FROM ... WHERE ...selectList(wrapper)条件查询
SELECT COUNT(*) FROM ...selectCount(wrapper)计数
SELECT ... LIMIT offset, sizeselectPage(page, wrapper)分页查询
多表 JOINXML Mapper关联查询
GROUP BY + 聚合XML Mapper统计查询

十、完整业务示例

java
@Service
public class UserService extends ServiceImpl<UserMapper, User> {

    /**
     * 分页查询用户列表
     */
    public IPage<User> queryPage(UserQueryDTO query) {
        return page(
            new Page<>(query.getPageNo(), query.getPageSize()),
            new LambdaQueryWrapper<User>()
                .like(StringUtils.isNotBlank(query.getName()),
                       User::getName, query.getName())
                .eq(query.getStatus() != null,
                       User::getStatus, query.getStatus())
                .orderByDesc(User::getCreateTime)
        );
    }

    /**
     * 批量更新用户状态
     */
    public void batchUpdateStatus(List<Long> ids, Integer status) {
        update(new LambdaUpdateWrapper<User>()
            .set(User::getStatus, status)
            .set(User::getUpdateTime, LocalDateTime.now())
            .in(User::getId, ids)
        );
    }
}

十一、常用配置速查

yaml
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  type-aliases-package: com.example.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      id-type: assign_id          # AUTO / ASSIGN_ID / ASSIGN_UUID / INPUT
      table-prefix: tb_           # 全局表前缀
      logic-delete-field: deleted # 逻辑删除字段
      logic-delete-value: 1       # 已删除值
      logic-not-delete-value: 0   # 未删除值
      update-strategy: not_null   # NOT_NULL / NOT_EMPTY / IGNORED
最近更新