MyBatis-Plus [Java库]

Marimo_z
2025-04-19 / 0 评论 / 10 阅读 / 正在检测是否收录...
官方文档:MyBatis-Plus

一、核心特性

  1. 无侵入性:仅增强MyBatis功能,不改变原有结构。
  2. 自动化CRUD:内置通用BaseMapper接口,单表操作无需手写SQL。
  3. 动态条件构造:通过QueryWrapperLambdaQueryWrapper链式调用生成复杂查询条件。
  4. 内置插件:分页插件、逻辑删除、性能分析等开箱即用。
  5. 代码生成器:一键生成实体类、Mapper、Service、Controller代码。

二、快速入门

1. 添加依赖(Spring Boot)

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2. 配置数据源

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

3. 实体类与Mapper接口

  • 核心注解

    1. @TableName:指定实体类对应的表名。
    2. @TableId:标识主键字段,支持多种生成策略:

      • IdType.AUTO:数据库自增
      • IdType.ASSIGN_ID:雪花算法生成ID(默认)。
    3. @TableField:处理字段映射,适用于:

      • 字段名与数据库列名不一致
      • 非数据库字段(exist = false
      • 关键字冲突(使用转义符)。
  • 实体类:使用注解映射表结构

    @Data
    @TableName("user") // 表名注解
    public class User {
        @TableId(type = IdType.AUTO) // 主键自增
        private Long id;
        private String name;
        @TableField("user_age") // 字段名映射
        private Integer age;
    }
  • Mapper接口:继承BaseMapper获得基础CRUD方法

    public interface UserMapper extends BaseMapper<User> {}

三、CRUD方法

方法名作用返回值
insert(entity)插入记录影响行数
deleteById(id)按ID删除影响行数
updateById(entity)按ID更新影响行数
selectById(id)按ID查询实体对象
selectList(wrapper)条件查询列表实体列表
selectPage(page, wrapper)分页查询IPage 分页对象
selectMaps(wrapper)返回Map结构结果Map列表

1. 插入操作

// 插入一条记录(返回影响行数)
int insert(User entity);

// 示例:插入用户
User user = new User();
user.setName("张三");
user.setAge(25);
int rows = userMapper.insert(user); 
Long generatedId = user.getId(); // 获取自增ID(需配置主键策略)

2. 删除操作

// 按ID删除(返回影响行数)
int deleteById(Serializable id); 

// 按条件删除(返回影响行数)
int delete(@Param(Constants.WRAPPER) Wrapper<User> wrapper); 

// 批量删除(返回影响行数)
int deleteBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);

// 示例1:按ID删除
userMapper.deleteById(1L);

// 示例2:删除年龄大于30的用户
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.gt(User::getAge, 30);
userMapper.delete(wrapper);

3. 更新操作

// 按ID更新(返回影响行数)
int updateById(@Param(Constants.ENTITY) User entity);

// 按条件更新(返回影响行数)
int update(@Param(Constants.ENTITY) User entity, 
          @Param(Constants.WRAPPER) Wrapper<User> updateWrapper);

// 示例1:更新用户信息
User user = new User();
user.setId(1L);
user.setName("李四");
userMapper.updateById(user);

// 示例2:条件更新(年龄+1)
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.setSql("age = age + 1"); // 直接写SQL表达式
userMapper.update(null, wrapper);

4. 查询操作

// 按ID查询
User selectById(Serializable id);

// 批量ID查询
List<User> selectBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);

// 按条件查询单条记录
User selectOne(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);

// 条件查询总数
Long selectCount(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);

// 条件查询列表
List<User> selectList(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);

// 示例:查询名字包含"张"的用户
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张");
List<User> users = userMapper.selectList(wrapper);

5. 高级查询方法

  • 返回 Map 结构
// 查询结果为 Map 列表
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);

// 示例:获取用户ID和姓名映射
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName);
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
// 结果示例:[{id=1, name="张三"}, {id=2, name="李四"}]
  • 返回特定字段
// 查询指定字段(返回 Object 列表)
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);

// 示例:查询所有用户ID
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId);
List<Object> ids = userMapper.selectObjs(wrapper); // [1, 2, 3...]

6. 自定义 SQL 方法

在 Mapper 接口中定义自定义方法:

public interface UserMapper extends BaseMapper<User> {
    // 注解方式(简单SQL)
    @Select("SELECT * FROM user WHERE age > #{age}")
    List<User> selectByAge(@Param("age") Integer age);

    // XML方式(复杂SQL)
    List<User> selectComplexQuery(@Param("params") Map<String, Object> params);
}

XML 映射文件 (UserMapper.xml):

<select id="selectComplexQuery" resultType="User">
    SELECT * FROM user 
    WHERE name LIKE CONCAT(#{params.namePrefix}, '%')
    AND status = #{params.status}
</select>

四、条件构造器

1. QueryWrapper:基础条件构造器

特点:基于字符串字段名,直接操作数据库列名
适用场景:快速构建简单查询条件

// 查询年龄>20且名字包含"张"的用户,按年龄降序排列
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "age") // 指定查询字段
       .gt("age", 20)
       .like("name", "张")
       .orderByDesc("age");

List<User> users = userMapper.selectList(wrapper);

关键方法

  • eq("column", value):等于
  • ne("column", value):不等于
  • gt("column", value):大于
  • ge("column", value):大于等于
  • lt("column", value):小于
  • le("column", value):小于等于
  • between("column", v1, v2):区间
  • like("column", value):模糊匹配
  • in("column", Collection):IN 查询

注意事项

  • 字段名硬编码易出错,推荐仅在简单场景使用
  • 字段名与数据库列名严格一致(除非配置了全局策略)

2. LambdaQueryWrapper:类型安全条件构造器

特点:通过 Lambda 表达式引用实体字段,编译期类型检查
优势:避免字段名拼写错误,支持 IDE 智能提示

// 查询邮箱不为空且状态为启用的用户
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.select(User::getId, User::getUsername)
            .isNotNull(User::getEmail)
            .eq(User::getStatus, 1)
            .orderByAsc(User::getCreateTime);

List<User> users = userMapper.selectList(lambdaWrapper);

方法对照

  • eq(User::getField, value)eq("field", value)
  • like(User::getField, value)like("field", value)

嵌套条件示例

wrapper.and(w -> w.lt(User::getAge, 40).or().isNull(User::getEmail))
       .or().between(User::getScore, 60, 90);

生成 SQL:

WHERE (age < 40 OR email IS NULL) OR score BETWEEN 60 AND 90

3. UpdateWrapper:更新条件构造器

核心作用:构建更新操作的条件和字段设置

示例1:直接设置值

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("balance", 1000)  // 设置字段值
             .set("vip_level", 2)
             .eq("id", 1L);

userMapper.update(null, updateWrapper);

示例2:使用 Lambda 表达式 + SQL 片段

LambdaUpdateWrapper<User> lambdaUpdate = new LambdaUpdateWrapper<>();
lambdaUpdate.setSql("balance = balance - 200") // 直接写SQL表达式
           .in(User::getId, Arrays.asList(1L, 3L, 5L))
           .eq(User::getStatus, 1);

userMapper.update(null, lambdaUpdate);

特殊方法

  • set("column", value):设置更新字段
  • setSql("sql片段"):直接拼接 SQL 语句(慎用,需防 SQL 注入)

4. 复杂条件组合

逻辑运算符灵活组合,支持多层级嵌套条件

场景:查询(年龄<18 或 年龄>60)且姓"王"的用户

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(User::getName, "王")
       .and(wq -> wq.lt(User::getAge, 18).or().gt(User::getAge, 60));

// 生成SQL:
// WHERE (name LIKE '王%') AND (age < 18 OR age > 60)

条件优先级控制:通过括号明确逻辑关系

wrapper.nested(wq -> wq.eq(...).or().eq(...)) // 嵌套条件生成括号
       .or().eq(...)

5. 最佳实践与调试技巧

调试技巧

  1. 开启 SQL 日志打印:

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  2. 打印完整条件:

    System.out.println(wrapper.getCustomSqlSegment());

性能优化

  • 避免在循环中创建 Wrapper(复用对象)
  • 复杂查询优先使用 XML 编写 SQL
  • 批量操作使用 Service 层的 saveBatch 方法

Null 值处理策略

  • 默认忽略 null 条件(需手动处理 IS NULL)
  • 强制包含 null 条件:

    wrapper.eq(Objects.nonNull(param), User::getField, param)

方法速查表

方法名作用
.exists("子查询")添加 EXISTS 条件
.notExists("子查询")添加 NOT EXISTS 条件
.apply("SQL片段")拼接自定义 SQL(需防注入)
.last("LIMIT 1")追加 SQL 末尾(分页慎用)
.having("SUM(age)>30")HAVING 条件

选择策略

  • 简单条件 → LambdaQueryWrapper(首选)
  • 复杂更新 → LambdaUpdateWrapper + setSql
  • 需要动态字段 → QueryWrapper
  • 极高灵活性 → 自定义 XML SQL + Wrapper 条件混合使用

五、高级功能

1. 分页插件

  • 配置分页拦截器

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
  • 使用分页查询

    Page<User> page = new Page<>(1, 10); // 当前页,每页条数
    userMapper.selectPage(page, queryWrapper);
    List<User> records = page.getRecords();

2. 逻辑删除

  • 配置全局逻辑删除

    mybatis-plus:
      global-config:
        db-config:
          logic-delete-field: deleted  # 删除标记字段
          logic-delete-value: 1        # 删除后的值
          logic-not-delete-value: 0    # 未删除的值
  • 操作示例:调用deleteById实际执行UPDATE user SET deleted=1 WHERE id=xx

3. 代码生成器

  • 引入依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.1</version>
    </dependency>
  • 配置生成策略:指定表名、包路径、模板引擎(如Freemarker),自动生成Entity、Mapper、Service等代码。

六、Service层封装

MyBatis-Plus提供通用Service接口IService,进一步简化业务层代码:

public interface UserService extends IService<User> {}

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}
  • 批量操作示例

    List<User> userList = new ArrayList<>();
    // 添加数据...
    userService.saveBatch(userList); // 批量插入,性能优于逐条插入。

七、最佳实践

  1. 优先使用Lambda表达式:避免字段名硬编码,提升代码可维护性。
  2. 分页与性能优化:结合rewriteBatchedStatements=true参数优化批量插入性能。
  3. 逻辑删除慎用:频繁查询可能导致性能下降,建议归档历史数据替代。
  4. SQL监控:启用性能分析插件,识别慢查询:

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

八、常见问题

  1. 字段映射失败:检查@TableField注解或开启驼峰转换:

    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: true
  2. Mapper扫描失败:在启动类添加@MapperScan("com.example.mapper")
  3. 主键冲突:确认@TableIdtype策略与数据库一致(如自增需设置AUTO)。
2

评论 (0)

取消