MyBatis-Plus05:IService接口基本用法

一、Service接口提供的方法

1-1、removeByIdsremoveBatchByIds

核心区别:SQL 执行方式:

方法 生成的 SQL 执行次数 性能特点
removeByIds DELETE FROM table WHERE id IN (?, ?, ?) 1 次 SQL 快,适合中小批量(< 1000)
removeBatchByIds 多条 DELETE FROM table WHERE id = ? N 次 SQL(每个 ID 一条) 慢,但可绕过 IN 子句限制

示例:假设 ID 列表为 [1, 2, 3]

removeByIds(Arrays.asList(1,2,3))

sql 复制代码
DELETE FROM user WHERE id IN (1, 2, 3);

一次执行,高效。

removeBatchByIds(Arrays.asList(1,2,3))

sql 复制代码
DELETE FROM user WHERE id = 1;
DELETE FROM user WHERE id = 2;
DELETE FROM user WHERE id = 3;

三次独立 SQL,效率低。


1、为什么要有 removeBatchByIds?------解决 IN 的限制

虽然 removeByIds 更高效,但在某些数据库或场景下存在限制:

  • MySQLIN 列表长度理论上可达数万,但过长会影响性能或触发 max_allowed_packet 限制。
  • OracleIN 子句最多支持 1000 个元素(硬限制!超过会报错)。
  • SQL Server / DB2:也有类似限制。

此时,如果你要删 1500 条 Oracle 记录:

  • removeByIds(list) → ❌ 报错:ORA-01795: maximum number of expressions in a list is 1000
  • removeBatchByIds(list) → ✅ 虽慢但能跑通(逐条删)

💡 实际上,MP 的 removeBatchByIds 内部还会做分批次提交(默认每 1000 条一批),避免事务过大。

2、补充:removeBatchByIds 的"批"不是 JDBC Batch!

注意:虽然叫 "Batch",但它不是 JDBC 的批处理(addBatch/executeBatch) ,而是指"分批循环执行单条 DELETE"。

如果想用真正的 JDBC Batch 提升性能,MP 目前不直接支持 (需自定义 SQL 或用 SqlRunner)。

3、小结

场景 推荐方法
一般批量删除(ID 数 < 500) removeByIds(高效)
Oracle 删除 >1000 条 removeBatchByIds(绕过 IN 限制)
需要事务控制 + 大量删除 ⚠️ 考虑分页删除 + removeByIds(如每次删 500 条)
追求极致性能(万级删除) 🔧 自定义 SQL + foreach 或物理分区删除

1-2、Service接口的实现

  • 自定义接口需要extends IService接口;
  • 自定义实现类,需要extends ServiceImpl实现类;

【对比】:

Mapper接口,没有实现类;因为MyBatis 通过 JDK 动态代理(Dynamic Proxy)在运行时自动生成了 Mapper 接口的实现类

1-3、Mybatis 动态代理,详细原理分解

1. MyBatis 的核心机制:MapperProxy

当你调用 userMapper.selectById(1) 时,实际上执行的是 MyBatis 内部生成的一个 代理对象(Proxy)

  • MyBatis 在启动时(Spring 容器初始化阶段),会扫描所有被 @Mapper 注解标记的接口,或通过 @MapperScan 扫描的包。
  • 对每个 Mapper 接口,MyBatis 使用 JDK 动态代理 创建一个实现了该接口的代理类。
  • 这个代理类的逻辑由 MapperProxy 类统一处理。

📌 关键类:org.apache.ibatis.binding.MapperProxy


2. 代理做了什么?

当调用 userMapper.selectById(1) 时,流程如下:

  1. 调用被代理对象的方法 → 触发 MapperProxy.invoke()
  2. MapperProxy 根据 方法名 + 参数类型,去 MyBatis 的 MappedStatement 注册表中查找对应的 SQL 语句
    • 对于 MP 的 BaseMapper 方法(如 selectById),MP 已经在启动时自动注册了通用 SQL。
    • 对于自定义方法(如 selectByName),MyBatis 会从 XML 或 @Select 注解中解析 SQL。
  3. 执行 SQL,封装结果,返回。

3. MyBatis-Plus 的增强

MP 在 MyBatis 基础上进一步自动化:

  • BaseMapper<T> 中的 CRUD 方法(如 insert, deleteById, selectList 等)无需你写 SQL
  • MP 在应用启动时,通过 反射 + 泛型分析,自动为每个实体生成对应的通用 SQL(比如根据表名、字段名拼装)。
  • 这些 SQL 被注册到 MyBatis 的 Configuration 中,供 MapperProxy 调用。

💡 本质:MP 把"模板化 SQL" 自动生成并注册,而 MyBatis 负责代理调用。


4. Spring 是如何管理这个代理 Bean 的?

  • 当你使用 @MapperScan("com.example.mapper") 时,MyBatis-Spring 提供了一个 MapperScannerConfigurer
  • 它会把指定包下的所有接口,注册为 Spring Bean,Bean 的实例就是 MyBatis 生成的代理对象。
  • 所以你可以用 @Autowired 正常注入。

最终:UserMapper 在 Spring 容器中是一个 由 MyBatis 生成的代理对象,不是 null,也不是你写的类。

5、验证:打印 Mapper 实例的类名

java 复制代码
System.out.println(userMapper.getClass().getName());

输出可能是:com.sun.proxy.$Proxy89

6、总结一句话:

MyBatis-Plus(基于 MyBatis)在运行时通过 JDK 动态代理,为 Mapper 接口自动生成了代理实现类,无需开发者手动编写。

补充说明:

  • 这个"代理实现类"不是 MP 单独做的 ,而是 MyBatis 的核心机制,MP 是在此基础上增强了 CRUD 能力。
  • 所以准确说是:MyBatis 负责生成代理,MP 负责填充通用 SQL 逻辑

1-4、Service接口-小结


二、Controller中使用@Autowired注入Service接口而非实现类

在Spring MVC的Controller中使用@Autowired注入Service接口而非实现类,主要有以下几个原因:

1. 面向接口编程

这是面向对象设计的核心原则之一。依赖接口而非具体实现,可以降低代码耦合度,提高系统的可维护性和可扩展性。

2. 依赖倒置原则(DIP)

高层模块(Controller)不应该依赖低层模块(ServiceImpl),两者都应该依赖抽象(Service接口)。这样可以让代码更加灵活,易于修改。

3. 便于切换实现

如果业务需求变化,需要更换Service的实现方式,只需要:

  • 创建新的实现类
  • 修改配置或使用@Primary@Qualifier指定实现
  • Controller代码无需修改
java 复制代码
// Controller中的代码始终不变
@Autowired
private UserService userService;

// 可以轻松切换实现
@Service
public class UserServiceImpl implements UserService { }

@Service
@Primary  // 优先使用这个实现
public class UserServiceImplV2 implements UserService { }

4. 便于单元测试

测试时可以轻松mock接口,而不需要依赖具体实现:

java 复制代码
@SpringBootTest
class UserControllerTest {
    @MockBean
    private UserService userService;  // 轻松mock接口
    
    @Autowired
    private UserController controller;
}

5. 支持AOP和代理

Spring的很多功能(如事务管理、缓存)是通过动态代理实现的。注入接口可以让Spring更灵活地创建代理对象。

6. 符合开闭原则

对扩展开放,对修改关闭。新增功能时只需增加新的实现类,而不修改现有代码。


注意: 即使注入的是接口,Spring在运行时注入的仍然是实现类的实例(或其代理对象),只是通过接口类型来引用它。

相关推荐
ruleslol8 小时前
MyBatis-Plus04:自定义SQL
mybatis-plus
识君啊4 天前
MyBatis-Plus 逻辑删除导致唯一索引冲突的解决方案
java·spring boot·mybatis·mybatis-plus·唯一索引·逻辑删除
独断万古他化12 天前
【MyBatis-Plus 进阶】注解配置、条件构造器与自定义 SQL的复杂操作详解
sql·mybatis·mybatis-plus·条件构造器
子沫20201 个月前
使用mybatis-plus、mybatis插入数据库时加密,查询数据库时解密,自定义TypeHandler 加解密使用
数据库·mybatis·mybatis-plus
silence2502 个月前
MyBatis-Plus 报错 Invalid bound statement(insert)?其实是 SqlSessionFactoryBean 踩坑了
mybatis·mybatis-plus
爱学习的小可爱卢2 个月前
JavaEE进阶——MyBatis-Plus新手完全攻略
java·mybatis-plus
言一木2 个月前
mybatis-plus分表实现案例
springboot·mybatis-plus·水平分表
YDS8292 个月前
MyBatis-Plus精讲 —— 从快速入门到项目实战
java·后端·spring·mybatis·mybatis-plus
m0_564264183 个月前
IDEA DEBUG调试时如何获取 MyBatis-Plus 动态拼接的 SQL?
java·数据库·spring boot·sql·mybatis·debug·mybatis-plus