一、MyBatis-Plus 基础介绍

1.1 框架简介

MyBatis 是单表增删改查需要重复编写大量 XML 与 SQL,MyBatis-Plus 在 MyBatis 基础上做无侵入增强,无需修改原有 MyBatis 代码即可接入,一键实现单表全部数据库操作,大幅提升开发效率。

  1. 复杂关联:自定义 Mapper XML 编写原生关联 SQL;
  2. 简单关联:Service 层多次单表查询,业务组装数据。

1.2 底层执行流程

  1. SpringBoot 启动扫描实体类;
  2. 反射读取@TableName@TableId等注解,解析表名、字段、主键、逻辑删除标识;
  3. 自动生成 insert/update/delete/select 标准 SQL 注入 MyBatis 容器;
  4. 开发者直接调用 BaseMapper 内置方法完成数据库交互。

二、SpringBoot 快速整合 MyBatis-Plus 实战

2.1 环境准备

JDK8+、SpringBoot2.x、MySQL8.0、Maven 项目

2.2 数据库表初始化

sql

DROP TABLE IF EXISTS user;
CREATE TABLE user
(
    id BIGINT NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL COMMENT '姓名',
    age INT NULL COMMENT '年龄',
    email VARCHAR(50) NULL COMMENT '邮箱',
    deleted INT DEFAULT 0 COMMENT '逻辑删除标记',
    version INT DEFAULT 0 COMMENT '乐观锁版本号',
    PRIMARY KEY (id)
);
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com');

2.3 Maven 核心依赖

xml

<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Lombok简化实体类 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<!-- MyBatis-Plus启动器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

注意:不可同时引入原生 MyBatis 依赖,会造成冲突。

2.4 application.yml 配置

yaml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/boot?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印执行SQL
  global-config:
    db-config:
      id-type: assign_id # 默认雪花算法主键
      logic-delete-field: deleted
      logic-delete-value: 1 # 已删除
      logic-not-delete-value: 0 # 正常
  mapper-locations: classpath:mapper/*.xml # 自定义XML路径

2.5 实体类编写

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @TableLogic
    private Integer deleted;
    @Version
    private Integer version;
}

2.6 Mapper 接口与启动类

Mapper 继承BaseMapper自动获得全部 CRUD 方法:

@Mapper
public interface UserMapper extends BaseMapper<User> {}

启动类使用@MapperScan统一扫描 Mapper,无需每个接口加@Mapper

@SpringBootApplication
@MapperScan("com.mp.mapper")
public class MpApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpApplication.class, args);
    }
}

2.7 基础测试查询

@SpringBootTest
class MpTest {
    @Autowired
    private UserMapper userMapper;
    @Test
    void testSelectAll(){
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
}

运行控制台输出完整 SQL,查询全部数据,基础环境搭建完成。

三、MyBatis-Plus 核心注解详解

3.1 @TableName

绑定实体与数据库表,实体类名和表名不一致时必须使用,核心属性 value 指定表名。

3.2 @TableId 主键注解

标记主键字段,type 控制主键生成策略,局部注解优先级高于全局配置:

  1. AUTO:数据库自增,适合单体项目;
  2. ASSIGN_ID:雪花算法,Long/String 类型,分布式推荐默认策略;
  3. ASSIGN_UUID:32 位 UUID 字符串;
  4. INPUT:手动传入主键;
  5. NONE:跟随全局配置。

雪花算法原理:1 位符号位 + 41 位时间戳 + 10 位机器 ID+12 位序列号,分布式全局唯一。

3.3 @TableField

普通字段注解,常用场景:

  1. value:实体属性与数据库字段名不一致时映射;
  2. exist=false:标记该属性不属于数据库字段;
  3. select=false:查询时不返回该字段;
  4. fill:自动填充(INSERT/UPDATE/INSERT_UPDATE)。

3.4 @TableLogic

逻辑删除字段标记,开启后所有查询自动拼接where deleted=0,delete 操作转为 update 更新删除标记,避免数据物理丢失。

3.5 @Version

乐观锁版本号注解,用于并发更新,更新时自动携带版本条件,防止数据覆盖丢失。

四、BaseMapper 通用 CRUD 操作

4.1 新增 insert ()

插入实体,使用雪花算法自动生成 ID 并回填至实体对象:

@Test
void testInsert(){
    User user = new User();
    user.setName("张三");
    user.setAge(22);
    user.setEmail("zs@qq.com");
    int rows = userMapper.insert(user);
    System.out.println("受影响行数:"+rows);
    System.out.println("自动生成ID:"+user.getId());
}

4.2 更新操作

  1. updateById:根据主键更新实体非 null 字段;
User user = new User();
user.setId(1L);
user.setName("张三修改");
userMapper.updateById(user);
  1. update (entity, wrapper):自定义条件批量更新数据。

4.3 查询常用方法

// 根据主键单查
User user = userMapper.selectById(1L);
// 批量ID查询
List<User> list = userMapper.selectBatchIds(Arrays.asList(1,2));
// Map等值多条件查询
Map<String,Object> map = new HashMap<>();
map.put("name","Jack");
List<User> userList = userMapper.selectByMap(map);
// 查询总条数
Integer total = userMapper.selectCount(null);

4.4 删除操作

  1. 物理删除(未开启逻辑删除):deleteById、deleteBatchIds、deleteByMap;
  2. 逻辑删除(全局配置 +@TableLogic):执行 deleteById 不会删除数据,SQL 变为update user set deleted=1 where id=? and deleted=0

五、条件构造器 Wrapper(复杂查询核心)

Wrapper 分为四类,企业开发优先使用 Lambda 系列,杜绝字段硬编码:

  1. QueryWrapper:普通字符串条件查询;
  2. LambdaQueryWrapper:Lambda 表达式查询(推荐);
  3. UpdateWrapper:自定义更新条件;
  4. LambdaUpdateWrapper:Lambda 更新条件。

常用查询示例

// 1. 普通Wrapper:年龄20~30,姓名不为空
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name").between("age",20,30).orderByDesc("id");
List<User> list = userMapper.selectList(wrapper);

// 2. LambdaWrapper(无硬编码,推荐)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.ge(User::getAge, 20).likeRight(User::getEmail, "t");
List<User> lambdaList = userMapper.selectList(lambdaWrapper);

常用方法:eq 等于、ne 不等于、gt 大于、ge 大于等于、lt 小于、le 小于等于、between 区间、like 模糊查询、orderByDesc 倒序。

六、分页插件

MP 内置物理分页,无需引入 PageHelper,只需配置拦截器。

6.1 分页配置类

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // MySQL分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

6.2 分页代码使用

@Test
void testPage(){
    // 参数1:当前页码,参数2:每页条数
    Page<User> page = new Page<>(1,2);
    Page<User> pageResult = userMapper.selectPage(page, null);
    // 分页数据
    List<User> records = pageResult.getRecords();
    // 分页信息
    System.out.println("总条数:"+pageResult.getTotal());
    System.out.println("总页数:"+pageResult.getPages());
    System.out.println("是否下一页:"+pageResult.hasNext());
}

底层自动执行两条 SQL:count 统计总数 + limit 分页查询。

七、乐观锁解决并发更新丢失

7.1 使用场景

多线程同时修改同一条数据,无锁会出现后提交的数据覆盖先提交数据,造成数据丢失。乐观锁通过 version 版本号实现无锁并发控制。

7.2 完整实现

  1. 数据库添加 version 字段,默认值 0;
  2. 实体 version 字段添加@Version
  3. 拦截器添加乐观锁插件:
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
  1. 并发测试逻辑:
// 两个线程读取同一条数据
Product p1 = productMapper.selectById(1L);
Product p2 = productMapper.selectById(1L);
// 线程1更新成功,version自增1
p1.setPrice(p1.getPrice()+500);
productMapper.updateById(p1);
// 线程2携带旧version更新,匹配不到数据,更新行数为0,更新失败
p2.setPrice(p2.getPrice()-300);
int rows = productMapper.updateById(p2);
if(rows == 0){
    // 业务重试:重新查询最新数据再更新
}

自动生成更新 SQL:update product set price=?,version=version+1 where id=? and version=?

八、Service 层通用封装 IService

MP 在 Mapper 之上封装业务层通用接口,命名区分 Mapper:Mapper 用 select/insert,Service 用 list/get/save/remove。

8.1 代码结构

// Service接口
public interface UserService extends IService<User> {}
// 实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}

8.2 Service 常用方法

@Autowired
private UserService userService;
userService.save(user); // 新增
userService.saveBatch(list); // 批量新增
User user = userService.getById(1L); // 根据id查询
List<User> list = userService.list(wrapper); // 条件查询
IPage<User> page = userService.page(new Page<>(1,5), wrapper); // 分页查询
userService.saveOrUpdate(user); // 存在更新,不存在新增

九、自定义 SQL 处理复杂多表查询

单表操作使用 MP 内置方法,多表联查、复杂聚合 SQL 兼容原生 MyBatis XML。

  1. Mapper 接口定义自定义方法
public interface UserMapper extends BaseMapper<User> {
    List<User> selectByAge(Integer age);
}
  1. resources/mapper 下创建 UserMapper.xml

xml

<mapper namespace="com.mp.mapper.UserMapper">
    <select id="selectByAge" resultType="com.mp.pojo.User">
        select * from user where age = #{age}
    </select>
</mapper>

十、代码生成器 AutoGenerator

一键生成 controller、service、mapper、entity 全套代码,大幅减少重复编码。

10.1 新增依赖

xml

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

10.2 核心生成工具类

public class MpGenerator {
    // 数据源配置
    public static DataSourceConfig dataSource(){
        DataSourceConfig ds = new DataSourceConfig();
        ds.setDbType(DbType.MYSQL);
        ds.setUrl("jdbc:mysql:///boot?serverTimezone=Asia/Shanghai");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
    // 全局配置
    public static GlobalConfig globalConfig(){
        GlobalConfig gc = new GlobalConfig();
        String path = System.getProperty("user.dir");
        gc.setOutputDir(path+"/src/main/java");
        gc.setAuthor("作者");
        gc.setServiceName("%sService");
        return gc;
    }
    // 包配置
    public static PackageConfig packageConfig(){
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.mp");
        pc.setController("controller");
        pc.setService("service");
        pc.setMapper("mapper");
        pc.setEntity("pojo");
        return pc;
    }
    // 策略配置
    public static StrategyConfig strategy(){
        StrategyConfig sc = new StrategyConfig();
        sc.setEntityLombokModel(true);
        sc.setLogicDeleteFieldName("deleted");
        sc.setVersionFieldName("version");
        sc.setNaming(NamingStrategy.underline_to_camel);
        sc.setInclude("user"); // 指定生成表
        return sc;
    }
    public static void main(String[] args) {
        AutoGenerator ag = new AutoGenerator();
        ag.setDataSource(dataSource());
        ag.setGlobalConfig(globalConfig());
        ag.setPackageInfo(packageConfig());
        ag.setStrategy(strategy());
        ag.execute();
    }
}

运行 main 方法自动生成整套分层代码。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐