MyBatis-Plus使用详解

MyBatis-Plus

1. MyBatis-Plus简介

1.1 MyBatis 概述

MyBatis是一款优秀的半自动化ORM框架,通过XML或注解将SQL与Java对象映射,和新特性包括:

  • 灵活SQL控制:开发者直接编写SQL,精准优化数据库操作。
  • 动态SQL支持:通过,等标签实现条件拼接。
  • 解耦DAO层:分离SQL与Java代码,提升可维护性。

痛点:需手动编写大量基础CRUD代码,复杂查询需手写XML,开发效率受限。

1.2 MyBatis-Plus 的特性

MyBatis-Plus(简称MP)在MyBatis基础上进行增强,核心特性如下:

特性 说明
无侵入增强 通过继承 BaseMapper 即可获得通用 CRUD 方法,无需修改 MyBatis 原生配置。
条件构造器 提供 QueryWrapper 和 LambdaQueryWrapper,支持链式调用动态构建查询条件。
代码生成器 自动生成 Entity、Mapper、Service、Controller 层代码,支持自定义模板。
分页插件 支持物理分页,自动适配不同数据库方言(MySQL、Oracle 等)。
主键策略 内置雪花算法、UUID、自增等多种分布式 ID 生成方案。
ActiveRecord模式 实体类继承 Model 可直接操作数据库,简化 DAO 层代码。
SQL注入器 支持自定义全局方法(如逻辑删除、批量插入)。
性能分析插件 输出SQL执行时间,帮助识别慢查询。
1.2.1. 无侵入增强
  • 零侵入设计:通过继承BaseMapper接口即可获得通用CRUD方法,无需修改原有MyBatis结构。
  • 注解式开发:通过@TableName@TableField等注解实现ORM映射,告别XML配置的繁琐。
1.2.2 条件构造器(Wrapper)
  • 动态SQL生成:自动拼接WHERE条件,支持AND/OR嵌套、IN、BETWEEN等复杂逻辑。
  • 类型安全:提供QueryWrapper(普通条件)和LambdaQueryWrapper(Lambda表达式),后者通过方法引用避免硬编码字段名。
// Lambda 示例(防止字段名拼写错误)
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "John").ge(User::getAge, 18);

1.2.3 分页插件
  • 物理分页:基于数据库方言(如MYSQL的LIMIT)实现高效分页。
  • 统一API:通过Page对象封装分页参数,配合IPage返回结果集和总记录数。
Page page = new Page<>(1, 10); // 第1页,每页10条
userMapper.selectPage(page, wrapper);

1.2.4 主键生成策略
  • 内置策略:支持AUTO(数据库自增)、UUID、SNOWFLAKE(雪花算法)等。
  • 分布式友好:雪花算法生成全局唯一ID,避免单点故障(通过@TableId(type = IdType.ASSIGN_ID)启用。

1.2.5 Active Record模式
  • 实体即DAO:实体类继承Model后可直接操作自身数据,适合简单业务场景。
User user = new User().selectById(1L);
user.setName("Alice").updateById();

1.2.6 插件扩展机制
  • 内置插件:分页插件、性能分析插件、乐观锁插件(通过@Version)注解实现。
  • 自定义插件:实现 Interceptor 接口,拦截SQL执行过程(如动态替换表名)。

2. MP的底层工作原理

动态Mapper代理

  • 自动注入SQL:通过MyBatis的MapperProxy动态代理机制,在运行时为BaseMapper的方法生成实现逻辑。
  • SQL注入器:内置SqlInjector将通用方法(如SelectById)的SQL模板注入到Mapper。

条件构造器解析

  • AST抽象语法树:将Wrapper中的条件转换为抽象语法树,最终生成带占位符的SQL语句和参数列表。
  • 防止SQL注入:严格使用预编译(PreparedStatement),避免拼接字符串漏洞。

3. MyBatis-Plus快速入门

创建测试表:在MySQL中创建测试表user

CREATE TABLE user (
    id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
    age INT(11) DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
    deleted TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标记',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

搭建项目引入依赖:在 pom.xml 中添加 Spring Boot 和 MyBatis-Plus 依赖


    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
    
    
        com.baomidou
        mybatis-plus-boot-starter
        3.5.3.1
    
    
    
    
        mysql
        mysql-connector-java
        runtime
    
    
    
    
        org.projectlombok
        lombok
        true
    

创建实体类User.java,使用MyBatis-Plus注解:

@Data
@TableName("user")  // 指定表名(默认类名驼峰转下划线)
public class User {
    @TableId(type = IdType.AUTO)  // 主键自增策略
    private Long id;
    
    private String name;
    private Integer age;
    private String email;
    
    @TableLogic  // 逻辑删除标记
    private Integer deleted;
    
    @TableField(fill = FieldFill.INSERT)  // 插入时自动填充
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)  // 插入/更新时自动填充
    private LocalDateTime updateTime;
}

创建Mapper接口继承BaseMapper:

@Mapper
public interface UserMapper extends BaseMapper {
    // 无需编写 XML,自动继承通用 CRUD 方法
}

application.yml配置数据源和MyBatis-Plus

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useSSL=false&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印 SQL 日志
  global-config:
    db-config:
      logic-delete-value: 1  # 逻辑已删除值
      logic-not-delete-value: 0  # 逻辑未删除值

启动类添加@MapperScan注解扫描Mapper接口:

@SpringBootApplication
@MapperScan("com.example.mapper")  // 指定 Mapper 接口包路径
public class MybatisPlusDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusDemoApplication.class, args);
    }
    
    /**
     * 配置分页插件(必选)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

编写JUnit测试类验证CRUD功能

@SpringBootTest
@RunWith(SpringRunner.class)
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    /**
     * 测试插入
     */
    @Test
    public void testInsert() {
        User user = new User();
        user.setName("John");
        user.setAge(28);
        user.setEmail("[email protected]");
        int rows = userMapper.insert(user);
        System.out.println("插入记录数:" + rows);
    }

    /**
     * 测试分页查询
     */
    @Test
    public void testSelectPage() {
        Page page = new Page<>(1, 2);  // 第1页,每页2条
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.like("name", "J");
        IPage userPage = userMapper.selectPage(page, wrapper);
        userPage.getRecords().forEach(System.out::println);
    }

    /**
     * 测试逻辑删除
     */
    @Test
    public void testLogicDelete() {
        int rows = userMapper.deleteById(1L);
        System.out.println("逻辑删除记录数:" + rows);
    }
}

4.MyBatis-Plus核心功能详解

4.1 BaseMapper的基本CRUD

BaseMapper 提供开箱即用的通用 CRUD 方法,无需编写 SQL 或 XML。

// 插入(返回影响行数)
int insert(User user); 

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

// 按条件查询(返回一条记录)
User selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper);

// 按条件批量查询
List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper);

// 按条件更新
int update(@Param(Constants.ENTITY) User entity, 
          @Param(Constants.WRAPPER) Wrapper updateWrapper);

// 按 ID 删除
int deleteById(Serializable id); 

4.2 Wrapper查询

4.2.1 Wrapper基本方法

方法分类:

  • 条件构造:eqnegtgeltlebetweenlike
  • 逻辑组合:andornested
  • 字段选择:select
  • 聚合与排序:groupByorderByAscorderByDesc
  • 子查询:inSqlexistsnotExists

基础使用示例:

QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("age", 25)
       .like("name", "张")
       .between("create_time", "2023-01-01", "2023-12-31");
List users = userMapper.selectList(wrapper);

子查询:

wrapper.inSql("id", "SELECT id FROM user WHERE age > 30");

分组与排序:

wrapper.select("COUNT(*) as count", "age")
       .groupBy("age")
       .orderByDesc("count");

多条件拼接:

wrapper.and(w -> w.eq("status", 1).or().eq("role", "admin"))
       .or(w -> w.gt("score", 90));

4.2.2 QueryWrapper与UpdateWrapper
  • QueryWrapper:用于SELECT条件构造。
  • UpdateWrapper:用于UPDATE条件构造,支持set方法。
UpdateWrapper updateWrapper = new UpdateWrapper<>();
updateWrapper.set("email", "[email protected]")
            .eq("name", "John");
userMapper.update(null, updateWrapper);

4.2.3 Lambda Wrapper

通过Lambda表达式避免硬编码字段名,增强类型安全。

LambdaQueryWrapper lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getAge, 25)
             .like(User::getName, "张");

LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.set(User::getEmail, "[email protected]")
                   .eq(User::getId, 1L);

4.3 分页查询

4.3.1 分页插件配置

在配置类中注册分页插件:

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

4.3.2 分页查询实现
// 分页参数:当前页、每页条数
Page page = new Page<>(1, 10); 

// 条件构造
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.ge("age", 18);

// 执行分页查询
IPage userPage = userMapper.selectPage(page, wrapper);

// 获取结果
List records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal();          // 总记录数

4.4 Service层封装

4.4.1 通用Service接口
  • IService定义通用Service方法。
  • ServiceImpl, T>默认实现类。
public interface UserService extends IService {
    // 自定义扩展方法
}

@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
}

// 使用示例
userService.saveBatch(userList);          // 批量插入
userService.lambdaUpdate()
           .set(User::getEmail, "[email protected]")
           .eq(User::getAge, 25)
           .update();                    // Lambda 更新

4.5 常用注解

4.5.1 @TableName
  • 作用:实体类与表名映射
  • 属性:value (表名),schema(数据库schema)
4.5.2 @TableId
  • vlaue:主键字段名(默认id)
  • type:主键生成策略
public enum IdType {
    AUTO,       // 数据库自增
    NONE,       // 无策略
    INPUT,      // 手动输入
    ASSIGN_ID,  // 雪花算法
    ASSIGN_UUID // UUID
}
4.5.3 @TableField
  • value:字段名映射
  • exist:是否为表字段(默认为true)
  • fille:字段自动填充策略
public enum FieldFill {
    DEFAULT,        // 不填充
    INSERT,         // 插入时填充
    UPDATE,         // 更新时填充
    INSERT_UPDATE   // 插入和更新时填充
}
4.5.4 @TableLogic
  • 逻辑删除标记:自动在查询中追加 WHERE deleted=0
4.5.5 @EnumValue
  • 枚举值映射:标记枚举类中与数据库存储值对应的字段
4.5.6 @Version
  • 乐观锁实现:通过版本号控制并发更新

4.6 绑定Mapper.xml

在Mapper接口中定义方法,并在XML中编写复杂SQL:

// UserMapper.java
List selectComplexUsers(@Param("role") String role);

// UserMapper.xml

4.7 Active Record模式

实体类继承Model,可直接调用CRUD方法,无需通过Mapper。

public class User extends Model {
    // ... 字段定义
}

// 直接操作实体
User user = new User();
user.setName("Alice");
user.insert();          // 插入

User result = user.selectById(1L); // 查询

5. 适用场景分析

  1. 快速 CRUD 开发:适用于后台管理系统、基础数据模块,减少重复代码。

  2. 复杂动态查询:通过 Wrapper 动态构建查询条件,替代手写 XML 中的  标签。

  3. 分页标准化:统一分页逻辑,避免各业务模块独立实现分页。

  4. 微服务环境:雪花算法生成分布式 ID,适应高并发、分布式部署场景。

你可能感兴趣的:(mybatis,java,开发语言,junit,spring,boot,spring)