@Repository
public interface AbstractPDMWorkflowEntityMapper {
@SelectProvider(type = SqlProvider.class,method = "getEntityByProvider")
AbstractPDMWorkflowEntity getEntityByProcessId(@Param("tableName") String tableName, @Param("processId") Long processId);
@ResultMap("AbstractPDMWorkflowEntityResult")
@Select("select * from ${tableName} where id=#{entityId}")
AbstractPDMWorkflowEntity getEntityByIdAndFormDefinition(@Param("tableName") String tableName, @Param("entityId") Long entityId);
@Update("update ${tableName} set process_type='N',form_code=#{formCode},applier_No=#{applierNo},applier_Name=#{applierName},process_instance_id=#{processInstanceId} where id=#{id}")
void updateAbstractPDMWorkflowProcess(AbstractPDMWorkflowEntity abstractPDMWorkflowEntity);
@Update("update ${tableName} set business_state=#{businessState},pass_date=#{passDate} where id=#{id}")
void updateAbstractPDMWorkflowStatus(AbstractPDMWorkflowEntity abstractPDMWorkflowEntity);
class SqlProvider {
public String getEntityByProvider(@NonNull Map<String, Object> params){
// 获取动态传入的表名(注意:${} 属于字符串直接替换,需防范 SQL 注入风险)
String tableName = (String) params.get("tableName");
return new SQL() {{
// 1. 拼接 SELECT 字段
SELECT("d.*");
SELECT("fd.table_name");
SELECT("fd.mapper_name");
SELECT("bs.business_State_en");
// 2. 拼接 FROM 主表(动态表名)
FROM(tableName + " d");
// 3. 拼接第一个 LEFT JOIN(包含多个 ON 条件)
JOIN("form_definition fd ON d.form_code = fd.code AND fd.preassignparticipant = true AND fd.entitystate = 0");
// 4. 拼接第二个 LEFT JOIN
JOIN("business_state_convert bs ON d.business_state = bs.business_state");
// 5. 拼接 WHERE 条件(#{processId} 会自动被 MyBatis 解析为预编译参数,防止注入)
WHERE("d.process_instance_id = #{processId}");
}}.toString();
}
}
}
@SelectProvider 和xml 和 @Select 区别, 执行速度, 更建议哪种?
在 MyBatis 中,@Select、@SelectProvider 和 XML 映射文件是三种常见的 SQL 编写方式。它们在灵活性、维护性和执行性能上有着明显的区别。
⚖️ 三种方式的核心区别
| 维度 | @Select (静态注解) | @SelectProvider (动态注解) | XML 映射文件 |
|---|---|---|---|
| 适用场景 | 极其简单的单表、固定 SQL 查询 | 中等复杂度的动态 SQL(如多条件筛选、动态排序) | 复杂 SQL(多表联查、复杂子查询)、需要高度可维护性的业务 |
| 动态 SQL 能力 | 弱(需配合 <script> 标签,字符串拼接极难维护) |
强(通过 Java 代码和 SQL Builder 灵活构建) | 极强(原生支持 <if>, <foreach>, <choose> 等标签,结构清晰) |
| 代码维护性 | 简单直观,但 SQL 复杂时接口会变得混乱 | 逻辑在 Java 类中,可读性较好,但复杂 SQL 仍需脑补还原 | SQL 与 Java 代码完全解耦,便于 DBA 审核、优化和统一管理 |
| 功能支持 | 仅支持简单结果映射,不支持 SQL 片段复用 | 同 @Select
|
支持复杂的 ResultMap 映射、SQL 片段(<sql>)复用、多数据库适配 |
🚀 执行速度对比
在性能方面,XML 映射文件通常略优于注解方式(包括 @Select 和 @SelectProvider)。
- XML 方式:在高并发、高频调用的场景下,XML 方式具有明显的性能优势(实测吞吐量更高、CPU 占用更低)。这主要得益于 MyBatis 框架对 XML 中定义的 MappedStatement 进行了深度的解析优化和高效的执行计划缓存机制。
- @SelectProvider 方式:由于它需要在运行时通过反射调用 Java 方法来动态生成 SQL 字符串,在高并发查询时,这个动态生成和解析的过程会带来一定的性能开销,导致 QPS(每秒查询率)相对较低。
- @Select 方式:虽然 SQL 在编译期就已经确定,加载速度较快,但在复杂和高频场景下的整体执行效率依然不如经过深度优化的 XML 方式。
💡 最终建议:到底选哪种?
在实际的企业级项目开发中,强烈建议采用“混合开发,以 XML 为主”的策略。具体的选型建议如下:
- 极度简单的单表 CRUD:
可以直接使用 @Select、@Insert 等静态注解,或者引入 MyBatis-Plus 这类增强工具,通过 Wrapper 甚至无需编写 SQL,极大提升开发效率。 - 中等复杂的动态查询:
如果业务逻辑主要是多条件的动态拼接,且你希望保持代码的强类型安全,使用 @SelectProvider 配合 MyBatis 自带的 SQL 构建器是一个非常优雅的选择。 - 复杂业务与核心 SQL(强烈推荐 XML):
对于涉及多表 Join、复杂报表、多层子查询,或者对性能有极致要求的场景,请直接使用 XML 映射文件。将 SQL 集中放在 XML 中不仅结构最清晰、最容易维护,还能享受到 MyBatis 框架最好的性能优化。
- 避坑指南:在混合使用时请注意,MyBatis 默认会优先加载 XML 中的 SQL 定义。如果同一个 Mapper 接口方法同时在 XML 和注解中定义了 SQL,XML 中的定义会覆盖注解,为了避免混淆,建议尽量保持单一项目中 SQL 编写风格的统一。