MyBatis-Plus使用详解
·
MyBatis-Plus
1. MyBatis-Plus简介
1.1 MyBatis 概述
MyBatis是一款优秀的半自动化ORM框架,通过XML或注解将SQL与Java对象映射,和新特性包括:
- 灵活SQL控制:开发者直接编写SQL,精准优化数据库操作。
- 动态SQL支持:通过<if>,<foreach>等标签实现条件拼接。
- 解耦DAO层:分离SQL与Java代码,提升可维护性。
痛点:需手动编写大量基础CRUD代码,复杂查询需手写XML,开发效率受限。
1.2 MyBatis-Plus 的特性
MyBatis-Plus(简称MP)在MyBatis基础上进行增强,核心特性如下:
| 特性 | 说明 |
|---|---|
| 无侵入增强 | 通过继承 BaseMapper<T> 即可获得通用 CRUD 方法,无需修改 MyBatis 原生配置。 |
| 条件构造器 | 提供 QueryWrapper 和 LambdaQueryWrapper,支持链式调用动态构建查询条件。 |
| 代码生成器 | 自动生成 Entity、Mapper、Service、Controller 层代码,支持自定义模板。 |
| 分页插件 | 支持物理分页,自动适配不同数据库方言(MySQL、Oracle 等)。 |
| 主键策略 | 内置雪花算法、UUID、自增等多种分布式 ID 生成方案。 |
| ActiveRecord模式 | 实体类继承 Model<T> 可直接操作数据库,简化 DAO 层代码。 |
| SQL注入器 | 支持自定义全局方法(如逻辑删除、批量插入)。 |
| 性能分析插件 | 输出SQL执行时间,帮助识别慢查询。 |
1.2.1. 无侵入增强
- 零侵入设计:通过继承BaseMapper<T>接口即可获得通用CRUD方法,无需修改原有MyBatis结构。
- 注解式开发:通过
@TableName、@TableField等注解实现ORM映射,告别XML配置的繁琐。
1.2.2 条件构造器(Wrapper)
- 动态SQL生成:自动拼接WHERE条件,支持AND/OR嵌套、IN、BETWEEN等复杂逻辑。
- 类型安全:提供QueryWrapper(普通条件)和LambdaQueryWrapper(Lambda表达式),后者通过方法引用避免硬编码字段名。
// Lambda 示例(防止字段名拼写错误)
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "John").ge(User::getAge, 18);
1.2.3 分页插件
- 物理分页:基于数据库方言(如MYSQL的LIMIT)实现高效分页。
- 统一API:通过Page<T>对象封装分页参数,配合IPage<T>返回结果集和总记录数。
Page<User> 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<T>后可直接操作自身数据,适合简单业务场景。
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 依赖
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
创建实体类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<User> {
// 无需编写 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("john@example.com");
int rows = userMapper.insert(user);
System.out.println("插入记录数:" + rows);
}
/**
* 测试分页查询
*/
@Test
public void testSelectPage() {
Page<User> page = new Page<>(1, 2); // 第1页,每页2条
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "J");
IPage<User> 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<T> 提供开箱即用的通用 CRUD 方法,无需编写 SQL 或 XML。
// 插入(返回影响行数)
int insert(User user);
// 按 ID 查询
User selectById(Long id);
// 按条件查询(返回一条记录)
User selectOne(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
// 按条件批量查询
List<User> selectList(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
// 按条件更新
int update(@Param(Constants.ENTITY) User entity,
@Param(Constants.WRAPPER) Wrapper<User> updateWrapper);
// 按 ID 删除
int deleteById(Serializable id);
4.2 Wrapper查询
4.2.1 Wrapper基本方法
方法分类:
- 条件构造:
eq,ne,gt,ge,lt,le,between,like - 逻辑组合:
and,or,nested - 字段选择:select
- 聚合与排序:
groupBy,orderByAsc,orderByDesc - 子查询:
inSql,exists,notExists
基础使用示例:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 25)
.like("name", "张")
.between("create_time", "2023-01-01", "2023-12-31");
List<User> 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<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("email", "new@example.com")
.eq("name", "John");
userMapper.update(null, updateWrapper);
4.2.3 Lambda Wrapper
通过Lambda表达式避免硬编码字段名,增强类型安全。
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getAge, 25)
.like(User::getName, "张");
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.set(User::getEmail, "new@example.com")
.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<User> page = new Page<>(1, 10);
// 条件构造
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 18);
// 执行分页查询
IPage<User> userPage = userMapper.selectPage(page, wrapper);
// 获取结果
List<User> records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal(); // 总记录数
4.4 Service层封装
4.4.1 通用Service接口
- IService<T>:定义通用Service方法。
ServiceImpl<M extends BaseMapper<T>, T>:默认实现类。
public interface UserService extends IService<User> {
// 自定义扩展方法
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
// 使用示例
userService.saveBatch(userList); // 批量插入
userService.lambdaUpdate()
.set(User::getEmail, "new@example.com")
.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<User> selectComplexUsers(@Param("role") String role);
// UserMapper.xml
<select id="selectComplexUsers" resultType="User">
SELECT * FROM user
WHERE role = #{role}
AND create_time > NOW() - INTERVAL 7 DAY
</select>
4.7 Active Record模式
实体类继承Model<T>,可直接调用CRUD方法,无需通过Mapper。
public class User extends Model<User> {
// ... 字段定义
}
// 直接操作实体
User user = new User();
user.setName("Alice");
user.insert(); // 插入
User result = user.selectById(1L); // 查询
5. 适用场景分析
-
快速 CRUD 开发:适用于后台管理系统、基础数据模块,减少重复代码。
-
复杂动态查询:通过 Wrapper 动态构建查询条件,替代手写 XML 中的
<if>标签。 -
分页标准化:统一分页逻辑,避免各业务模块独立实现分页。
-
微服务环境:雪花算法生成分布式 ID,适应高并发、分布式部署场景。
更多推荐



所有评论(0)