一、概述
1.1 MyBatis 简介
MyBatis 是一款优秀的半自动化 的 ORM 框架,它封装了 JDBC,开发者可以通过 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs 映射成数据库中的记录。它的核心优势在于灵活的 SQL,开发者可以精确地控制每一条 SQL 语句。
1.2 MyBatis Plus 简介
MyBatis Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,旨在简化开发、提高效率。它提供了通用的 CRUD 操作、条件构造器、分页插件等功能,无需编写简单的 XML 和手动设置参数,极大地提升了开发效率。
核心关系:MyBatis Plus 建立在 MyBatis 之上,你可以像使用 MyBatis 一样使用 MP,同时享受它带来的额外便利。
二、核心区别与选型建议
特性 | MyBatis | MyBatis Plus |
---|---|---|
定位 | 半自动化 ORM 框架,专注于 SQL 本身 | MyBatis 的增强工具,自动化 CRUD |
SQL 控制 | 完全控制,手动编写所有 SQL,灵活性极高 | 大部分通用 SQL 自动生成,特殊 SQL 仍需手动 |
开发效率 | 较低,需要编写大量模板代码(如基础 CRUD 的 XML) | 极高,内置通用 Mapper 和 Service,减少大量代码 |
学习成本 | 较低,主要学习 SQL 映射和动态 SQL | 在 MyBatis 基础上,需学习条件构造器等新概念 |
适用场景 | 1. 项目 SQL 极度复杂多变 2. 对数据库操作有高度定制化需求 3. 遗留系统或团队非常熟悉 MyBatis | 1. 快速开发、业务模型简单的项目(如后台管理系统) 2. 需要快速实现大量标准 CRUD 操作 3. 希望减少模板代码,提升开发效率 |
选型建议
-
选择 MyBatis:
- 你的项目涉及大量复杂、高度优化的 SQL,特别是多表关联查询和存储过程调用。
- 团队对 MyBatis 非常熟悉,且现有项目基于 MyBatis 构建,引入新框架收益不大。
- 你希望每个 SQL 都经过精心设计和优化,对性能有极致追求。
-
选择 MyBatis Plus:
- 你需要快速搭建项目,进行敏捷开发。
- 你的业务中有大量单表的、标准的 CRUD 操作。
- 你希望代码更加简洁,避免"Simple CRUD"的重复劳动。
- (推荐) 大部分项目可以以 MyBatis Plus 为基础,对于其无法优雅处理的复杂 SQL,再使用原生 MyBatis 的方式编写。这是一种高效的混合模式。
三、Spring Boot 环境下的实战示例
我们将通过一个 User
实体来演示 MyBatis Plus 的常用功能。
3.1 环境准备
1.添加依赖 (pom.xml
)
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- MyBatis Plus 核心依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version> <!-- 请使用最新版本 -->
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2.配置数据库 (application.yml
)
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
# MyBatis Plus 配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志,方便调试
global-config:
db-config:
id-type: auto # 主键策略:数据库自增
table-prefix: t_ # 表前缀,如果表有前缀如 t_user,这里配置后实体类就不需要带前缀了
3.实体类 (User.java
)
kotlin
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("t_user") // 指定表名,如果表名和类名一致(忽略大小写和下划线)可省略
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
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; // 0-未删除,1-已删除
}
4.Mapper 接口 (UserMapper.java
)
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承 BaseMapper 后,无需编写任何方法,即可获得基本的 CRUD 功能
// 你也可以在这里定义你自己的复杂 SQL 方法
}
3.2 核心功能示例
1. 基础 CRUD
MyBatis Plus 内置了几乎所有单表 CRUD 方法。
java
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.List;
@SpringBootTest
public class BasicCRUDTests {
@Autowired
private UserMapper userMapper;
// 插入
@Test
public void testInsert() {
User user = new User();
user.setName("张三");
user.setAge(28);
user.setEmail("zhangsan@example.com");
int rows = userMapper.insert(user); // INSERT INTO t_user ...
System.out.println("影响行数: " + rows);
System.out.println("自动回填的主键ID: " + user.getId());
}
// 根据ID查询、更新、删除
@Test
public void testSelectUpdateDelete() {
// 查询
User user = userMapper.selectById(1L); // SELECT * FROM t_user WHERE id=1
System.out.println(user);
// 更新
user.setAge(30);
userMapper.updateById(user); // UPDATE t_user SET ... WHERE id=1
// 删除
userMapper.deleteById(1L); // DELETE FROM t_user WHERE id=1
}
// 批量操作
@Test
public void testBatch() {
// 批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L)); // SELECT * FROM t_user WHERE id IN (1,2,3)
// 批量删除
userMapper.deleteBatchIds(Arrays.asList(4L, 5L)); // DELETE FROM t_user WHERE id IN (4,5)
}
}
2. 动态查询(条件构造器 QueryWrapper
)
这是 MP 最强大的功能之一,可以动态构建 SQL 的 WHERE 条件。
less
@Test
public void testQueryWrapper() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 1. 简单条件: name like '%张%' AND age < 30
queryWrapper.like("name", "张").lt("age", 30);
// 2. 嵌套条件: (age < 40 AND email IS NOT NULL) OR (name = '李四')
queryWrapper.and(wq -> wq.lt("age", 40).isNotNull("email"))
.or(wq -> wq.eq("name", "李四"));
// 3. 排序、选择特定字段
queryWrapper.orderByDesc("age") // 按年龄倒序
.select("id", "name", "age"); // 只查询 id,name,age 字段
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
// 4. 使用 Lambda 表达式(推荐,避免硬编码字段名)
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(User::getName, "王")
.between(User::getAge, 20, 40)
.orderByAsc(User::getAge);
List<User> userList2 = userMapper.selectList(lambdaQueryWrapper);
}
3. 分页查询
需要先配置分页插件。
kotlin
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusPlugin;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerPlugin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusPlugin mybatisPlusPlugin() {
MybatisPlusPlugin plugin = new MybatisPlusPlugin();
plugin.addInnerPlugin(new PaginationInnerPlugin(DbType.MYSQL)); // 数据库类型
return plugin;
}
}
然后使用 Page
对象进行分页查询:
csharp
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@Test
public void testPage() {
// 参数:当前页,每页大小
Page<User> page = new Page<>(1, 10); // 查询第1页,每页10条
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.ge(User::getAge, 20); // 年龄 >= 20
// 执行查询,查询结果会自动放入 page 对象中
Page<User> result = userMapper.selectPage(page, lambdaQueryWrapper);
System.out.println("总记录数: " + result.getTotal());
System.out.println("总页数: " + result.getPages());
System.out.println("当前页数据: " + result.getRecords());
}
4. 批量插入与更新
批量插入 :使用 MyBatis Plus 自带的 saveBatch
方法(在 Service 层)。
java
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service // 需要定义一个 Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
// 继承了ServiceImpl,已经拥有了saveBatch方法
}
// --- 测试类中 ---
@Autowired
private UserServiceImpl userService;
@Test
public void testBatchInsert() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("BatchUser_" + i);
user.setAge(20 + i);
user.setEmail("batch" + i + "@example.com");
userList.add(user);
}
// 批量插入,每批1000条
boolean isSuccess = userService.saveBatch(userList, 1000); // INSERT INTO t_user (...) VALUES (...), (...), ...
System.out.println("批量插入是否成功: " + isSuccess);
}
批量更新 :同样使用 Service 的 updateBatchById
方法。
typescript
@Test
public void testBatchUpdate() {
List<User> userList = userMapper.selectList(null); // 先查出一些数据
for (User user : userList) {
user.setAge(user.getAge() + 1); // 给每个人的年龄+1
}
// 根据ID批量更新
boolean isSuccess = userService.updateBatchById(userList, 1000); // 每批1000条
System.out.println("批量更新是否成功: " + isSuccess);
}
注意 :MP 的批量操作并非真正的 SQL 批量(rewriteBatchedStatements=true
那种),而是将多个操作放在一个 Session 中提交。对于极致性能要求,仍需手动编写 foreach
方式的 SQL。
5. 多表联合查询
这是 MyBatis Plus 的弱项 。MP 官方不提供自动化的多表关联查询方案。对于此类需求,最佳实践是回退到原生 MyBatis 的方式。
步骤:
-
在
UserMapper.java
中自定义方法。 -
在对应的 XML 文件 (
UserMapper.xml
) 中编写复杂的联合查询 SQL。less// 在 UserMapper.java 接口中定义方法 public interface UserMapper extends BaseMapper<User> { // 自定义方法:查询用户及其所属部门信息(假设有部门表 t_dept) List<User> selectUserWithDept(@Param("userName") String userName); }
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"> <!-- 自定义结果映射 --> <resultMap id="UserWithDeptResultMap" type="User" autoMapping="true"> <id property="id" column="id"/> <!-- 映射用户本身字段,autoMapping=true 可自动匹配 --> <!-- 关联部门对象 --> <association property="dept" javaType="Dept" autoMapping="true"> <id property="id" column="dept_id"/> </association> </resultMap> <!-- 自定义查询 --> <select id="selectUserWithDept" resultMap="UserWithDeptResultMap"> SELECT u.*, d.id as dept_id, d.name as dept_name FROM t_user u LEFT JOIN t_dept d ON u.dept_id = d.id <where> <if test="userName != null and userName != ''"> AND u.name LIKE CONCAT('%', #{userName}, '%') </if> </where> </select> </mapper>
这样,就可以在需要复杂查询的地方使用原生 MyBatis 的强大功能,而在单表操作时享受 MyBatis Plus 的便捷。这种混合模式是绝大多数项目的最佳选择。
四、总结
- MyBatis 是核心,提供了操作数据库的灵活性和控制力。
- MyBatis Plus 是加速器,在 MyBatis 的基础上,极大地提升了开发简单 CRUD 的效率。
最终建议 :
对于基于 Spring Boot 的新项目,强烈推荐直接使用 MyBatis Plus。它解决了绝大部分单表操作的问题。当遇到它无法处理的复杂场景(如多表关联、复杂 SQL 优化)时,从容地切换回原生 MyBatis 的编写方式即可。这种组合既能保证开发效率,又能应对复杂需求,是当前 Java Web 开发中数据持久层的最优解之一。