MyBatis-Plus 从入门到精通全攻略
🚀MyBatis-Plus 从入门到精通:CRUD、条件构造器、分页、多表联查、性能优化
摘要:MyBatis-Plus 是国内 Java 开发者的 ORM 框架首选,被誉为"开发效率神器"。本文基于 2026 最新版,从快速入门到高级应用,全面讲解 CRUD、条件构造器、分页插件、多表查询和性能优化等核心知识点。通俗易懂,简洁明了,助你快速掌握企业级开发必备技能!

一、 为什么选择 MyBatis-Plus?
🚀 核心优势
| 特性 | MyBatis | MyBatis-Plus |
|---|---|---|
| 基础 CRUD | 需手写 SQL | 内置通用 Mapper,零 SQL |
| 条件构造 | 手动拼接 WHERE | LambdaQueryWrapper,类型安全 |
| 分页支持 | 需配置插件 | 内置分页插件,开箱即用 |
| 代码生成 | 无 | 内置代码生成器,一键生成 |
| 性能分析 | 无 | 内置性能分析插件 |
| 学习成本 | 中等 | 低(兼容 MyBatis) |
核心理念:只做增强,不做改变。完全兼容 MyBatis,引入 MP 后原有代码无需修改。
二、 快速入门:5 分钟上手
1. 引入依赖(2026 最新版)
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(简化实体类) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
⚠️ 注意:Spring Boot 3.x 使用
mybatis-plus-spring-boot3-starter,Spring Boot 2.x 使用mybatis-plus-boot-starter。
2. 配置数据源
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis-Plus 配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志
global-config:
db-config:
id-type: auto # 主键自增
logic-delete-field: deleted # 全局逻辑删除字段
logic-delete-value: 1 # 已删除
logic-not-delete-value: 0 # 未删除
3. 创建实体类
@Data
@TableName("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;
}
4. 创建 Mapper 接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承 BaseMapper 后,自动拥有 CRUD 方法,无需编写 XML
}
5. 启动类添加注解
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描 Mapper 接口
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
6. 测试 CRUD
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
// ✅ 插入
@Test
void insert() {
User user = new User();
user.setName("张三");
user.setAge(25);
user.setEmail("zhangsan@example.com");
int rows = userMapper.insert(user);
System.out.println("影响行数: " + rows);
System.out.println("生成的ID: " + user.getId());
}
// ✅ 查询单个
@Test
void selectById() {
User user = userMapper.selectById(1L);
System.out.println(user);
}
// ✅ 更新
@Test
void updateById() {
User user = new User();
user.setId(1L);
user.setAge(26);
int rows = userMapper.updateById(user);
System.out.println("影响行数: " + rows);
}
// ✅ 删除
@Test
void deleteById() {
int rows = userMapper.deleteById(1L);
System.out.println("影响行数: " + rows);
}
}
三、 常用注解详解
1. 实体类注解
| 注解 | 作用 | 示例 |
|---|---|---|
@TableName |
指定表名 | @TableName("sys_user") |
@TableId |
指定主键 | @TableId(type = IdType.ASSIGN_ID) |
@TableField |
指定字段 | @TableField("user_name") |
@TableLogic |
逻辑删除 | @TableLogic |
@Version |
乐观锁 | @Version |
2. 主键策略
@TableId(type = IdType.AUTO) // 数据库自增
@TableId(type = IdType.ASSIGN_ID) // 雪花算法(默认)
@TableId(type = IdType.ASSIGN_UUID) // UUID
@TableId(type = IdType.INPUT) // 手动输入
3. 自动填充
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
4. 字段忽略与加密
@TableField(exist = false) // 非表字段
private String confirmPassword;
@TableField(select = false) // 查询时不返回
private String password;
@TableField(typeHandler = EncryptTypeHandler.class) // 自定义类型处理器(加密)
private String idCard;
四、 条件构造器:强大且灵活
MyBatis-Plus 的核心武器,告别手写 SQL WHERE 子句。
1. QueryWrapper(基础用法)
@Test
void testQueryWrapper() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 25) // age = 25
.like("name", "张") // name LIKE '%张%'
.between("age", 20, 30) // age BETWEEN 20 AND 30
.orderByDesc("create_time"); // ORDER BY create_time DESC
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
2. LambdaQueryWrapper(推荐⭐)
类型安全,重构友好,避免硬编码字段名。
@Test
void testLambdaQueryWrapper() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getAge, 25) // age = 25
.like(User::getName, "张") // name LIKE '%张%'
.between(User::getAge, 20, 30) // age BETWEEN 20 AND 30
.orderByDesc(User::getCreateTime); // ORDER BY create_time DESC
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
3. 常用条件方法
| 方法 | SQL 等价 | 示例 |
|---|---|---|
eq |
= |
.eq(User::getAge, 25) |
ne |
!= |
.ne(User::getStatus, 0) |
gt |
> |
.gt(User::getAge, 18) |
ge |
>= |
.ge(User::getAge, 18) |
lt |
< |
.lt(User::getAge, 60) |
le |
<= |
.le(User::getAge, 60) |
like |
LIKE '%值%' |
.like(User::getName, "张") |
likeRight |
LIKE '值%' |
.likeRight(User::getName, "张") |
in |
IN |
.in(User::getId, 1, 2, 3) |
notIn |
NOT IN |
.notIn(User::getId, 1, 2, 3) |
between |
BETWEEN |
.between(User::getAge, 20, 30) |
isNull |
IS NULL |
.isNull(User::getEmail) |
isNotNull |
IS NOT NULL |
.isNotNull(User::getEmail) |
groupBy |
GROUP BY |
.groupBy(User::getAge) |
orderByAsc |
ORDER BY ASC |
.orderByAsc(User::getAge) |
orderByDesc |
ORDER BY DESC |
.orderByDesc(User::getCreateTime) |
4. 动态条件查询
@Test
void testDynamicQuery() {
String name = "张";
Integer minAge = 20;
Integer maxAge = 30;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// ✅ 条件为真时才拼接
wrapper.like(StringUtils.isNotBlank(name), User::getName, name)
.ge(minAge != null, User::getAge, minAge)
.le(maxAge != null, User::getAge, maxAge)
.orderByDesc(User::getCreateTime);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
5. 复杂条件组合
@Test
void testComplexQuery() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// (age > 20 OR email IS NOT NULL) AND name LIKE '%张%'
wrapper.and(w -> w.gt(User::getAge, 20)
.or()
.isNotNull(User::getEmail))
.like(User::getName, "张");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
6. UpdateWrapper(条件更新)
@Test
void testUpdateWrapper() {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getAge, 25)
.set(User::getAge, 26)
.set(User::getEmail, "newemail@example.com");
int rows = userMapper.update(null, wrapper);
System.out.println("影响行数: " + rows);
}
五、 分页插件:开箱即用
1. 配置分页插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInterceptor.setMaxLimit(500L); // 单页最大限制条数
paginationInterceptor.setOverflow(false); // 溢出总页数后是否处理
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}
2. 基础分页查询
@Test
void testPage() {
// 当前页: 1, 每页大小: 10
Page<User> page = new Page<>(1, 10);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.ge(User::getAge, 20)
.orderByDesc(User::getCreateTime);
Page<User> result = userMapper.selectPage(page, wrapper);
System.out.println("总记录数: " + result.getTotal());
System.out.println("总页数: " + result.getPages());
System.out.println("当前页: " + result.getCurrent());
System.out.println("每页大小: " + result.getSize());
System.out.println("数据列表: " + result.getRecords());
}
3. 封装分页响应
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult<T> {
private Long total; // 总记录数
private Long pages; // 总页数
private Long current; // 当前页
private Long size; // 每页大小
private List<T> records; // 数据列表
public static <T> PageResult<T> of(Page<T> page) {
return new PageResult<>(
page.getTotal(),
page.getPages(),
page.getCurrent(),
page.getSize(),
page.getRecords()
);
}
}
4. Service 层分页
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public PageResult<User> getUserPage(Integer pageNum, Integer pageSize, String name) {
Page<User> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(name), User::getName, name)
.orderByDesc(User::getCreateTime);
Page<User> result = this.page(page, wrapper);
return PageResult.of(result);
}
}
5. Controller 层调用
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/page")
public Result<PageResult<User>> getUserPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name) {
PageResult<User> result = userService.getUserPage(pageNum, pageSize, name);
return Result.success(result);
}
}
六、 多表查询:灵活应对复杂场景
虽然 MyBatis-Plus 主打单表操作,但多表查询依然可以通过以下方式实现。
方案一:使用 @Select 注解(简单场景)
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
@Select("SELECT o.*, u.name AS user_name, u.email AS user_email " +
"FROM orders o " +
"LEFT JOIN user u ON o.user_id = u.id " +
"WHERE o.id = #{orderId}")
OrderWithUserVO selectOrderWithUser(@Param("orderId") Long orderId);
}
方案二:XML 映射文件(推荐⭐)
1. 创建 VO 类
@Data
public class OrderWithUserVO {
private Long orderId;
private String orderNo;
private BigDecimal amount;
private LocalDateTime orderTime;
// 用户信息
private Long userId;
private String userName;
private String userEmail;
}
2. 编写 XML
<!-- mapper/OrderMapper.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.demo.mapper.OrderMapper">
<resultMap id="OrderWithUserMap" type="com.example.demo.vo.OrderWithUserVO">
<id property="orderId" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
<result property="orderTime" column="order_time"/>
<result property="userId" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="userEmail" column="user_email"/>
</resultMap>
<select id="selectOrderWithUser" resultMap="OrderWithUserMap">
SELECT
o.id AS order_id,
o.order_no,
o.amount,
o.order_time,
u.id AS user_id,
u.name AS user_name,
u.email AS user_email
FROM orders o
LEFT JOIN user u ON o.user_id = u.id
WHERE o.id = #{orderId}
</select>
<select id="selectOrderListWithUser" resultMap="OrderWithUserMap">
SELECT
o.id AS order_id,
o.order_no,
o.amount,
o.order_time,
u.id AS user_id,
u.name AS user_name,
u.email AS user_email
FROM orders o
LEFT JOIN user u ON o.user_id = u.id
<where>
<if test="orderNo != null and orderNo != ''">
AND o.order_no LIKE CONCAT('%', #{orderNo}, '%')
</if>
<if test="userName != null and userName != ''">
AND u.name LIKE CONCAT('%', #{userName}, '%')
</if>
</where>
ORDER BY o.order_time DESC
</select>
</mapper>
3. Mapper 接口
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
OrderWithUserVO selectOrderWithUser(@Param("orderId") Long orderId);
List<OrderWithUserVO> selectOrderListWithUser(
@Param("orderNo") String orderNo,
@Param("userName") String userName
);
}
方案三:多次查询 + 内存组装(高性能场景)
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserMapper userMapper;
/**
* 查询订单列表并关联用户信息
* 性能优于 JOIN,适合大数据量场景
*/
public List<OrderWithUserVO> getOrderListWithUser() {
// 1. 查询订单列表
List<Order> orders = orderMapper.selectList(null);
if (orders.isEmpty()) {
return Collections.emptyList();
}
// 2. 提取所有用户ID
List<Long> userIds = orders.stream()
.map(Order::getUserId)
.distinct()
.collect(Collectors.toList());
// 3. 批量查询用户信息
List<User> users = userMapper.selectBatchIds(userIds);
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
// 4. 内存组装
return orders.stream().map(order -> {
OrderWithUserVO vo = new OrderWithUserVO();
BeanUtils.copyProperties(order, vo);
User user = userMap.get(order.getUserId());
if (user != null) {
vo.setUserName(user.getName());
vo.setUserEmail(user.getEmail());
}
return vo;
}).collect(Collectors.toList());
}
}
性能对比:
- JOIN 查询:1 次 SQL,但数据冗余大,网络传输量大。
- 多次查询:N+1 次 SQL,但可通过批量查询优化,适合缓存场景。
七、 性能优化:让查询飞起来
1. 开启 SQL 日志(开发环境)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2. 防止全表更新与删除
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 防止全表更新与删除
BlockAttackInnerInterceptor blockAttackInterceptor = new BlockAttackInnerInterceptor();
interceptor.addInnerInterceptor(blockAttackInterceptor);
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
执行 UPDATE user SET age = 20 时会抛出异常,必须加 WHERE 条件。
3. 使用索引优化
@Test
void testIndexOptimization() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// ✅ 走索引:等值查询
wrapper.eq(User::getEmail, "test@example.com");
// ✅ 走索引:左模糊查询
wrapper.likeRight(User::getName, "张"); // LIKE '张%'
// ❌ 不走索引:右模糊查询
wrapper.likeLeft(User::getName, "三"); // LIKE '%三'
// ❌ 不走索引:函数包裹字段
// YEAR(create_time) = 2024
List<User> users = userMapper.selectList(wrapper);
}
4. 只查询需要的字段
@Test
void testSelectFields() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName, User::getEmail) // 只查询指定字段
.eq(User::getAge, 25);
List<User> users = userMapper.selectList(wrapper);
}
生成的 SQL:
SELECT id, name, email FROM user WHERE age = 25
5. 批量操作
@Test
void testBatchInsert() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
User user = new User();
user.setName("用户" + i);
user.setAge(20 + i % 10);
user.setEmail("user" + i + "@example.com");
userList.add(user);
}
// ✅ 批量插入(默认 1000 条一批)
boolean success = userService.saveBatch(userList);
System.out.println("批量插入结果: " + success);
}
6. 缓存优化
一级缓存(默认开启)
同一 SqlSession 内,相同查询直接从缓存读取。
二级缓存(谨慎使用)
mybatis-plus:
configuration:
cache-enabled: true # 开启二级缓存
@CacheNamespace
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
⚠️ 注意:分布式环境下不建议使用二级缓存,推荐使用 Redis。
7. 慢查询监控
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 性能分析插件(仅开发环境)
PerformanceInnerInterceptor performanceInterceptor = new PerformanceInnerInterceptor();
performanceInterceptor.setMaxTime(1000L); // 最大执行时间 1000ms
performanceInterceptor.setFormat(true); // 格式化 SQL
interceptor.addInnerInterceptor(performanceInterceptor);
return interceptor;
}
}
超过 1000ms 的 SQL 会抛出异常,帮助定位慢查询。
八、 高级特性
1. 乐观锁
@Data
@TableName("product")
public class Product {
@TableId
private Long id;
private String name;
private Integer stock;
@Version // 乐观锁版本号
private Integer version;
}
@Test
void testOptimisticLocker() {
// 1. 查询商品
Product product = productMapper.selectById(1L);
System.out.println("版本号: " + product.getVersion());
// 2. 修改库存
product.setStock(product.getStock() - 1);
// 3. 更新(自动携带版本号条件)
int rows = productMapper.updateById(product);
System.out.println("影响行数: " + rows); // 成功返回 1,失败返回 0
}
生成的 SQL:
UPDATE product SET stock = 99, version = 2
WHERE id = 1 AND version = 1
2. 逻辑删除
@Data
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
@TableLogic // 逻辑删除字段
private Integer deleted;
}
@Test
void testLogicDelete() {
// 删除(实际是更新 deleted = 1)
int rows = userMapper.deleteById(1L);
System.out.println("影响行数: " + rows);
// 查询(自动添加 deleted = 0 条件)
User user = userMapper.selectById(1L); // 返回 null
}
生成的 SQL:
-- 删除
UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0
-- 查询
SELECT * FROM user WHERE id = 1 AND deleted = 0
3. 枚举自动转换
@Getter
public enum GenderEnum {
MALE(1, "男"),
FEMALE(2, "女"),
UNKNOWN(0, "未知");
@EnumValue // 存入数据库的值
private final Integer code;
@JsonValue // 返回给前端的值
private final String desc;
GenderEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
@Data
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
private GenderEnum gender; // 自动转换
}
4. 代码生成器
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create(
"jdbc:mysql://localhost:3306/mp_demo",
"root",
"123456"
)
.globalConfig(builder -> {
builder.author("YourName") // 作者
.outputDir("src/main/java") // 输出目录
.commentDate("yyyy-MM-dd"); // 注释日期格式
})
.packageConfig(builder -> {
builder.parent("com.example.demo") // 父包名
.moduleName("system") // 模块名
.entity("entity")
.mapper("mapper")
.service("service")
.controller("controller");
})
.strategyConfig(builder -> {
builder.addInclude("user", "order", "product") // 需要生成的表
.addTablePrefix("t_", "sys_"); // 过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
一键生成 Entity、Mapper、Service、Controller,开发效率提升 10 倍!
九、 最佳实践总结
✅ 推荐做法
- 优先使用 LambdaQueryWrapper:类型安全,重构友好。
- 单表操作用 MP,多表查询用 XML:发挥各自优势。
- 开启逻辑删除:避免误删数据,便于数据恢复。
- 使用乐观锁:高并发场景下保证数据一致性。
- 批量操作用 saveBatch/updateBatchById:减少数据库交互次数。
- 只查询需要的字段:减少网络传输,提升性能。
- 生产环境关闭 SQL 日志:避免性能损耗和信息泄露。
❌ 避免做法
- 不要滥用 JOIN:大数据量场景考虑多次查询 + 内存组装。
- 不要使用右模糊查询:
LIKE '%值'不走索引。 - 不要在循环中查询数据库:改为批量查询。
- 不要滥用二级缓存:分布式环境下使用 Redis。
- 不要忘记加 WHERE 条件:开启防全表更新插件。
十、 总结
MyBatis-Plus 作为 ORM 框架的首选,凭借开发效率高、学习成本低、功能强大等优势,成为企业必用的技术栈。
核心知识点回顾:
- 快速入门:5 分钟上手,继承 BaseMapper 即可拥有 CRUD。
- 常用注解:
@TableName、@TableId、@TableField、@TableLogic、@Version。 - 条件构造器:LambdaQueryWrapper 类型安全,动态条件灵活拼接。
- 分页插件:开箱即用,支持多种数据库。
- 多表查询:XML 映射文件 + 多次查询 + 内存组装。
- 性能优化:索引优化、字段裁剪、批量操作、缓存策略。
🌟 最后的话:工具的价值在于提升效率,但前提是理解其原理。掌握 MyBatis-Plus 的核心机制,才能在实际项目中游刃有余。
如果你觉得这篇文章对你有帮助,欢迎点赞👍、收藏⭐、评论💬,你的支持是我创作的最大动力!
#MyBatis-Plus #ORM框架 #Java开发 #SpringBoot #后端开发 #开发效率
更多推荐

所有评论(0)