手写XML映射结构清晰但繁琐,代码生成易致结构失控;运行时动态生成XML违反MyBatis生命周期,引发异常与内存泄漏;注解+Provider适合轻量SQL,但无法替代resultMap;编译期生成Java元数据或构建期生成XML更合理。

代码生成 XML 映射逻辑容易导致结构失控
手写 XML 映射(如 MyBatis 的 、Hibernate 的 hbm.xml)虽繁琐,但结构清晰、可读性强;而用代码(如 Java 注解 + 模板引擎或反射遍历 POJO)自动生成,常因字段命名不规范、嵌套层级变化、泛型擦除等问题,输出的 XML 出现 缺失、property 与 column 不匹配、重复定义 等问题。
- POJO 中字段为
userId,但数据库列为user_id,自动生成未启用下划线转驼峰规则时,直接失效 - 存在
List字段,但未标注@OneToMany或未配置泛型实际类型,生成器可能跳过块 - 继承关系(如
BaseEntity)被所有子类共享,代码生成易把父类字段重复写入每个子映射,违反 DRY 且引发 SQL 列冲突
运行时动态生成 XML 映射不可用于 MyBatis 启动期解析
MyBatis 在 SqlSessionFactory 构建阶段即完成 XML 解析与 MappedStatement 注册,此时所有 必须是静态资源或已加载的字符串。若在 DAO 调用时才用 StringBuilder 拼出 XML 并调用 configuration.addMapper(),会触发 java.lang.IllegalStateException: Mapped Statements collection already loaded。
- 试图用
XMLConfigBuilder解析运行时字符串需手动构造XPathParser和完整上下文,绕过 MyBatis 生命周期,极易破坏缓存、插件链、事务一致性 - Spring Boot 中通过
@MapperScan自动注册的接口,无法被运行时生成的 XML 映射绑定——接口无对应声明,MapperRegistry查找不到实现 - 即使强行注入,每次查询都重生成 XML,会导致
Configuration.mappedStatements内存泄漏(Key 为动态 UUID,无法复用)
注解驱动生成(如 @SelectProvider)更适合轻量映射场景
相比硬编码 XML 字符串,用注解配合 Provider 类返回动态 SQL 字符串,能保留 Java 编译检查、IDE 自动补全和单元测试能力,同时规避 XML 解析开销。但它只替代 SQL 片段,不生成完整 XML 结构(如 ),仍需手动维护映射元信息。
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plan Old Java Objects,普通的 Java 对象)映射成数据库中的记录。有需要的朋友可以下载看看
public class UserSqlProvider {
public String selectUsers(UserQuery query) {
return new SQL(){{
SELECT("id, user_name as userName, email");
FROM("t_user");
if (query.getStatus() != null) {
WHERE("status = #{status}");
}
}}.toString();
}
}
-
@SelectProvider返回的是纯 SQL 字符串,resultType或resultMap仍需显式指定,无法自动推导字段到 POJO 属性的映射关系 - 多表关联结果需手动写
SELECT u.id, u.user_name, o.order_no并配resultMap,生成器若尝试从 SQL 解析列别名并映射,会误判order_no属于User类 - 适合单表 CRUD 或固定 JOIN 场景;一旦涉及动态列投影(如按权限返回不同字段)、嵌套聚合(
GROUP_CONCAT拆解为 List),仍需手写 resultMap
真正需要“生成”的其实是映射元数据,不是 XML 文本本身
XML 只是 MyBatis 早期为兼容非注解环境设计的序列化载体。现在更合理的做法是:用代码生成器(如 MyBatis Generator、JOOQ Codegen)在编译期输出 Java 类 + 注解,或生成内存中 ResultMap 对象实例,再注册进 Configuration。这样既避免 XML 手写错误,又不破坏框架生命周期。
- MyBatis Generator 的
context.generateJavaClient输出@Select方法,搭配@Results注解,本质是把 XML 逻辑转为 Java 元数据 - JOOQ 生成的
RecordMapper实现类,直接在 JVM 中完成字段到对象赋值,完全绕过 XML 解析阶段 - 若坚持 XML 输出,应限定为构建期(Maven/Gradle 插件)一次性生成,并作为资源文件纳入 classpath,而非运行时拼接
最常被忽略的一点:XML 映射的调试成本远高于 Java 注解——断点打不到 内部,异常堆栈只显示 “Could not find result map”,却无法定位是哪个字段的 javaType 写错还是 jdbcType 缺失。









