目录
- 一、 快速入门
- 1. 创建工程与引入依赖
- 2. 配置数据库连接
- 3. 编写实体类
- 4. 创建 Mapper 接口
- 5. 测试 CRUD
- 二、核心功能详解
- 1. 条件构造器
- 1. 常用 Wrapper 方法示例
- 2. 使用示例:QueryWrapper
- 3. 使用示例:UpdateWrapper
- 4. 推荐使用:LambdaWrapper
- 2. 常用注解
- 3. 主键策略
- 三、 Service 层封装
- 1. 创建 Service 接口
- 2. 创建 Service 实现类
- 3. 使用 Service CRUD 方法
- 四、 高级特性
- 1. 代码生成器
- 2. 分页查询
- 1. 配置分页插件
- 2. 进行分页查询
- 3. 逻辑删除
- 1. 数据库添加字段
- 2. 实体类添加注解
- 3. 配置(可选,新版 MP 可能不需要)
- 4. 自动填充(审计)
- 1. 实体类标记字段
- 2. 实现 MetaObjectHandler 接口
- 5. 乐观锁
- 1. 数据库添加字段
- 2. 实体类添加注解
- 3. 配置乐观锁插件
- 五、 总结
一、 快速入门
1. 创建工程与引入依赖
创建一个 Spring Boot 工程,并在 pom.xml 中引入 MyBatis Plus 的起步依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version> <!-- 请注意使用最新版本 -->
</dependency>
2. 配置数据库连接
在 application.yml 中配置数据库连接等信息。如果需要查看 MP 执行的 SQL 语句,可以配置日志实现。
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 输出SQL日志到控制台
3. 编写实体类
创建实体类,并使用注解与数据库表进行映射。例如,对应 user 表的 User 类:
import lombok.Data;
import com.baomidou.mybatisplus.annotation.*;
@Data // Lombok注解,自动生成getter/setter等方法
@TableName("user") // 指定关联的数据表名,如果类名与表名符合驼峰转下划线规则,可省略
public class User {
@TableId(type = IdType.AUTO) // 指定主键及生成策略(AUTO-数据库自增)
private Long id;
private String name;
private Integer age;
private String email;
// 如果实体类字段名与数据库列名不一致,可用 @TableField 指定
// @TableField("user_name")
// private String userName;
// 如果实体类中存在非数据库字段,需要使用 @TableField(exist = false)
// @TableField(exist = false)
// private String nonDatabaseField;
}
4. 创建 Mapper 接口
创建 Mapper 接口,并使其继承 MyBatis Plus 提供的 BaseMapper 接口。继承后,无需编写 XML,即可获得大量单表 CRUD 方法。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@Repository // 可加注解,交由Spring管理
public interface UserMapper extends BaseMapper<User> {
// 此时无需编写任何方法,就已具备了基本的增删改查功能
}
5. 测试 CRUD
在测试类中注入 UserMapper,即可测试基本的增删改查操作。
@SpringBootTest
class MybatisPlusDemoApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void testSelect() {
// 查询所有用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
// 根据ID查询
User user = userMapper.selectById(1L);
System.out.println(user);
}
@Test
void testInsert() {
User newUser = new User();
newUser.setName("张三");
newUser.setAge(30);
newUser.setEmail("zhangsan@example.com");
int result = userMapper.insert(newUser); // result是受影响的行数
System.out.println("插入成功,受影响行数:" + result);
}
// 其他CRUD操作类似,可调用 deleteById, updateById 等方法
}
二、核心功能详解
1. 条件构造器
条件构造器(Wrapper)是 MP 中用于构建复杂查询条件的强大工具,你可以用它来替代手写 WHERE 子句。
1. 常用 Wrapper 方法示例
方法名 | 说明 | 示例 |
---|---|---|
eq | 等于 | wrapper.eq(“name”, “张三”) // name = ‘张三’ |
ne | 不等于 | wrapper.ne(“age”, 20) // age <> 20 |
gt | 大于 | wrapper.gt(“age”, 18) // age > 18 |
ge | 大于等于 | wrapper.ge(“age”, 20) // age >= 20 |
lt | 小于 | wrapper.lt(“age”, 65) // age < 65 |
le | 小于等于 | wrapper.le(“age”, 65) // age <= 65 |
between | 介于 | wrapper.between(“age”, 18, 30) // age between 18 and 30 |
like | 模糊匹配 | wrapper.like(“name”, “张”) // name like ‘%张%’ |
likeLeft | 左模糊匹配 | wrapper.likeLeft(“name”, “三”) // name like ‘%三’ |
likeRight | 右模糊匹配 | wrapper.likeRight(“name”, “张”) // name like ‘张%’ |
isNull | 为NULL | wrapper.isNull(“email”) // email is null |
isNotNull | 不为NULL | wrapper.isNotNull(“email”) // email is not null |
in | 包含 | wrapper.in(“age”, Arrays.asList(18,20,22)) // age in (18,20,22) |
and | AND连接 | wrapper.and(w -> w.eq(“name”, “李四”).gt(“age”, 25)) |
or | OR连接 | wrapper.or().eq(“name”, “张三”).or().eq(“name”, “李四”) |
orderByAsc | 升序排序 | wrapper.orderByAsc(“age”) |
orderByDesc | 降序排序 | wrapper.orderByDesc(“create_time”) |
select | 指定查询列 | wrapper.select(“id”, “name”, “email”) // 只查询特定列 |
2. 使用示例:QueryWrapper
@Test
void testQueryWrapper() {
// 构建查询条件:年龄大于20且姓名中包含"张"的用户,按年龄降序
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 20)
.like("name", "张")
.orderByDesc("age");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
3. 使用示例:UpdateWrapper
@Test
void testUpdateWrapper() {
// 构建更新条件:将年龄大于25的用户的邮箱更新为特定值
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.gt("age", 25)
.set("email", "senior@example.com"); // 直接设置值
// 注意:这里传入的User实体可以为null,因为更新条件都在wrapper里
int result = userMapper.update(null, wrapper);
System.out.println("更新了 " + result + " 条记录");
}
4. 推荐使用:LambdaWrapper
使用 LambdaQueryWrapper 和 LambdaUpdateWrapper 可以通过方法引用来指定字段,避免硬编码字段名,从而在编译阶段就能发现错误,更加安全可靠。
@Test
void testLambdaQueryWrapper() {
// 使用方法引用,避免拼写错误
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.gt(User::getAge, 20)
.like(User::getName, "张")
.orderByDesc(User::getAge);
List<User> users = userMapper.selectList(lambdaWrapper);
users.forEach(System.out::println);
}
2. 常用注解
MyBatis Plus 提供了一系列注解来简化实体类与数据库表之间的映射配置。
注解 | 作用 | 示例 |
---|---|---|
@TableName | 指定实体类对应的数据库表名 | @TableName(“sys_user”) |
@TableId | 指定主键字段,并可设置主键策略 | @TableId(value = “id”, type = IdType.AUTO) |
@TableField | 指定普通字段映射 | @TableField(“user_name”) @TableField(exist = false) // 非表字段 |
@Version | 用于乐观锁(后续讲解) | @Version private Integer version; |
@TableLogic | 用于逻辑删除(后续讲解) | @TableLogic private Integer deleted; |
3. 主键策略
@TableId 注解的 type 属性用于指定主键生成策略。
public class User {
@TableId(value = "id", type = IdType.AUTO) // 数据库自增
private Long id;
// ...
}
常见的 IdType 枚举:
- AUTO:数据库ID自增
- ASSIGN_ID:使用雪花算法生成Long类型的ID(默认策略)
- INPUT:用户手动输入ID
三、 Service 层封装
MyBatis Plus 不仅增强了 Mapper 层,也对 Service 层提供了强大的支持。
1. 创建 Service 接口
创建接口并继承 MP 提供的 IService 接口。
public interface UserService extends IService<User> {
// 可以在此定义自定义方法
// User findUserByCustomMethod(String name);
}
2. 创建 Service 实现类
创建实现类,继承 MP 提供的 ServiceImpl,并实现自定义的 Service 接口。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 如果 UserService 接口有自定义方法,需要在这里实现
// @Override
// public User findUserByCustomMethod(String name) { ... }
}
3. 使用 Service CRUD 方法
在 Controller 或其他地方注入你的 Service,即可使用其内置的大量方法。
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users")
public List<User> getAllUsers() {
// 直接调用 IService 中提供的 list 方法
return userService.list();
}
@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) {
// 调用 getById 方法
return userService.getById(id);
}
// 保存、更新、删除等方法调用类似
// userService.save(user);
// userService.updateById(user);
// userService.removeById(id);
}
四、 高级特性
1. 代码生成器
MyBatis Plus 内置了代码生成器(MyBatis-Plus Generator),可以根据数据库表结构自动生成 Entity、Mapper、Service、Controller 等代码,极大提升开发效率。
// 示例代码片段,具体需参考官方文档配置
FastAutoGenerator.create("jdbc:mysql://localhost:3306/your_db", "username", "password")
.globalConfig(builder -> builder.outputDir("D://code")) // 指定输出目录
.packageConfig(builder -> builder.parent("com.your.company")) // 指定父包名
.strategyConfig(builder -> builder.addInclude("user", "order")) // 指定要生成的表
.execute();
2. 分页查询
使用 MyBatis Plus 的分页功能需要先配置分页插件,然后即可使用其分页 API。
1. 配置分页插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
2. 进行分页查询
@Test
void testSelectPage() {
// 创建分页对象,参数:当前页,每页大小
Page<User> page = new Page<>(1, 5);
// 执行分页查询,第二个参数是查询条件(Wrapper),为null则表示无条件
userMapper.selectPage(page, null);
// 从page对象获取分页数据
System.out.println("总记录数: " + page.getTotal());
System.out.println("当前页记录: " + page.getRecords());
System.out.println("当前页码: " + page.getCurrent());
System.out.println("总页数: " + page.getPages());
System.out.println("每页大小: " + page.getSize());
System.out.println("是否有下一页: " + page.hasNext());
System.out.println("是否有上一页: " + page.hasPrevious());
}
3. 逻辑删除
逻辑删除并非真正物理删除数据,而是通过更新一个字段来标记数据已被删除。
1. 数据库添加字段
在表中增加一个 deleted(或自定义)字段,通常为 int 或 tinyint 类型。
2. 实体类添加注解
public class User {
// ... 其他字段 ...
@TableLogic
// @TableField(fill = FieldFill.INSERT) // 可选,配合自动填充设置默认值
private Integer deleted; // 0-未删除,1-已删除
}
3. 配置(可选,新版 MP 可能不需要)
在 application.yml 中配置逻辑删除的全局值(根据 MP 版本,有时可省略)。
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 # 已删除的值
logic-not-delete-value: 0 # 未删除的值
配置后,调用 userMapper.deleteById(id) 将会自动更新 deleted 字段为 1。查询时,MP 会自动带上 deleted = 0 的条件。
4. 自动填充(审计)
自动填充功能可用于自动处理一些通用字段的赋值,如数据的创建时间(create_time)和更新时间(update_time)。
1. 实体类标记字段
public class User {
// ... 其他字段 ...
@TableField(fill = FieldFill.INSERT) // 插入时填充
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时填充
private Date updateTime;
}
2. 实现 MetaObjectHandler 接口
创建一个处理器,实现插入和更新时的填充逻辑。
@Component // 记得声明为Spring组件
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时,为 createTime 和 updateTime 设置当前时间
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时,为 updateTime 设置当前时间
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
5. 乐观锁
乐观锁用于解决并发更新问题,通常通过一个版本号(version)字段实现。
1. 数据库添加字段
在表中增加一个 version 字段,通常为 int 类型。
2. 实体类添加注解
public class User {
// ... 其他字段 ...
@Version
// @TableField(fill = FieldFill.INSERT) // 可选,配合自动填充设置默认值
private Integer version;
}
3. 配置乐观锁插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
使用流程:
- 查询数据时,获取当前的 version 值(例如 version=1)。
- 更新数据时,将 version 作为条件:UPDATE user SET …, version = 2 WHERE id = ? AND version = 1。
- 如果此时该数据已被其他线程修改(即数据库中的 version 已不为 1),则本次更新返回的影响行数为 0,可根据此结果判断更新是否成功。
五、 总结
MyBatis Plus 通过丰富的功能和简洁的 API,极大地提升了基于 MyBatis 的开发效率。核心在于:
- 简单 CRUD:通过继承 BaseMapper 和 IService,无需编写 SQL 和 XML。
- 复杂查询:利用强大的 Wrapper,特别是 LambdaWrapper,安全地构建查询条件。
- 高效开发:利用代码生成器、分页、逻辑删除、自动填充、乐观锁等特性,处理常见开发场景。
希望这份详解能帮助你更好地理解和使用 MyBatis Plus。如果想深入了解某个特定功能,建议查阅其官方文档。