)
目录ORM框架核心知识体系#{}与${}、SQL注入、动态SQL、懒加载一、ORM框架的核心定位二、#{} 与 ${} 的本质区别参数处理机制1. 底层执行机制1#{}预编译参数化处理2${}静态字符串拼接2. 多维度核心对比3. 选型原则三、SQL注入防范原理1. SQL注入攻击的本质2. #{} 防注入的底层原理3. ${} 的注入风险与安全方案4. 易踩坑的边界场景四、动态SQL的实现与原理1. MyBatis动态SQL核心标签体系2. 底层执行原理3. 动态SQL与参数占位符的协作五、懒加载延迟加载原理1. 核心概念与适用场景2. 底层实现机制动态代理3. MyBatis核心配置与触发逻辑4. 优缺点与适用边界六、知识体系串联与实践原则ORM框架核心知识体系#{}与${}、SQL注入、动态SQL、懒加载本文以业界最典型的MyBatis半自动化ORM为核心载体系统化梳理参数占位符、注入防护、动态SQL、懒加载四大模块的底层原理与实践边界形成完整的知识闭环。一、ORM框架的核心定位ORMObject-Relational Mapping对象关系映射的核心目标是解决面向对象编程语言与关系型数据库的数据模型不匹配问题通过封装JDBC操作实现Java对象与数据库表的映射让开发者以操作对象的方式操作数据库同时兼顾SQL灵活性。其中#{} / ${}是ORM参数绑定的两种核心机制决定SQL的编译方式与安全边界动态SQL是ORM实现SQL灵活拼接的核心能力解决多条件查询、动态表/列名等场景懒加载是ORM的性能优化机制解决关联查询的N1问题与不必要的数据加载。二、#{} 与 ${} 的本质区别参数处理机制1. 底层执行机制1#{}预编译参数化处理本质JDBCPreparedStatement的参数占位符属于预编译机制。执行流程MyBatis解析SQL时将#{xxx}统一替换为?占位符数据库驱动对SQL进行预编译SQL的语义、执行计划在此时已固定执行阶段通过PreparedStatement.setXxx()方法传入参数参数仅作为纯数据处理驱动会自动完成类型转换与特殊字符转义。关键特性参数不会改变SQL的语法结构仅作为值参与执行。2${}静态字符串拼接本质SQL语句的字符串替换属于编译前的文本拼接。执行流程在SQL解析阶段直接将${xxx}替换为变量的实际取值生成完整的SQL字符串替换完成后再将完整SQL交给JDBCStatement或PreparedStatement执行整个过程参数与SQL文本直接融合参数内容会直接影响SQL结构。关键特性参数是SQL文本的一部分可改变SQL语义与执行逻辑。2. 多维度核心对比对比维度#{} 预编译占位符${} 字符串替换处理时机SQL预编译阶段编译后参数传入SQL解析阶段编译前文本替换底层对象基于PreparedStatement基于字符串拼接最终可由Statement执行SQL注入风险无参数化查询天然防注入极高直接拼接恶意参数可篡改SQL类型处理自动匹配Java类型与JDBC类型自动转义特殊字符纯文本替换需手动处理类型与引号适用场景普通参数查询条件、插入/更新值动态表名、动态列名、排序字段、SQL关键字性能表现支持SQL执行计划缓存重复执行性能更高每次生成不同SQL无法复用执行计划语法细节字符串参数无需手动加单引号字符串参数必须手动加单引号${name}3. 选型原则绝大多数业务参数优先使用 #{}这是安全与性能的默认选择仅当需要动态修改SQL结构本身时表名、列名、排序方向、表分区等才使用${}且必须配合安全校验。三、SQL注入防范原理1. SQL注入攻击的本质攻击者通过在输入参数中嵌入恶意SQL片段篡改原有SQL的语义结构从而执行非授权操作如拖库、删表、越权查询等。典型攻击示例传入参数name OR 11拼接后SQL变为SELECT * FROM user WHERE name OR 11条件恒成立直接泄露全表数据。2. #{} 防注入的底层原理#{} 防注入的核心是SQL语义与数据分离依赖JDBC预编译机制实现语义固化SQL预编译阶段执行计划就已生成?占位符的位置只能传入数据不能改变SQL的语法结构自动转义数据库驱动会对参数中的特殊字符单引号、分号、注释符等进行转义处理确保参数仅作为字符串/数值生效不会被解析为SQL指令参数隔离参数通过二进制协议单独传输而非拼接在SQL文本中从传输层避免注入。3. ${} 的注入风险与安全方案${} 直接拼接SQL文本完全不具备防注入能力必须通过业务层手段兜底白名单校验对动态表名、列名、排序字段做枚举校验只允许预设的合法值传入正则过滤拦截参数中的SQL关键字、;、--、drop、union等但存在绕过风险仅作辅助参数映射前端传索引/编码后端映射为真实表/列名避免直接暴露数据库元数据。4. 易踩坑的边界场景模糊查询禁止写LIKE %${name}%正确写法为LIKE CONCAT(%, #{name}, %)IN 条件禁止拼接${ids}使用foreach标签 #{item}实现动态排序ORDER BY ${sortField}必须加白名单不能直接接收前端参数。四、动态SQL的实现与原理动态SQL指根据不同的业务条件动态拼接生成不同的SQL语句解决多条件组合查询、批量操作等场景下SQL硬编码问题。1. MyBatis动态SQL核心标签体系标签作用典型场景if条件判断满足则拼接SQL片段多条件查询姓名、年龄、状态可选choose/when/otherwise分支选择类似switch-case多条件互斥场景where智能拼接WHERE关键字自动去除多余的AND/OR多条件查询的WHERE子句set智能拼接SET关键字自动去除多余逗号动态更新字段trim自定义前缀/后缀可替代where/set复杂SQL片段的前后缀处理foreach遍历集合生成IN、批量插入等SQLIN查询、批量插入/更新sql/includeSQL片段抽取与复用公共字段、公共条件复用2. 底层执行原理MyBatis动态SQL的核心是基于OGNL表达式的SQL解析引擎完整流程如下配置解析阶段XMLStatementBuilder解析Mapper.xml中的SQL节点识别动态标签动态节点处理将SQL拆分为多个SqlNode节点静态文本、if节点、foreach节点等形成节点树条件求值执行SQL时传入参数对象通过OGNL表达式引擎计算每个动态标签的条件是否成立SQL拼接遍历SqlNode树将满足条件的节点拼接成完整的SQL字符串参数替换拼接完成后再对SQL中的#{}替换为?、${}做文本替换最终生成可执行SQL。3. 动态SQL与参数占位符的协作动态标签if/foreach等负责SQL结构的动态生成决定哪段SQL生效#{}/${}负责参数值的绑定决定数据如何传入最佳实践动态结构用标签实现参数值用#{}传入仅在标签无法解决的结构动态场景下使用${}。五、懒加载延迟加载原理1. 核心概念与适用场景懒加载Lazy Loading也叫延迟加载指在查询主对象时不立即加载关联的对象数据仅在真正使用关联对象时才触发SQL查询。核心价值避免不必要的关联查询解决关联查询的N1问题提升查询性能典型场景一对多、多对多关联如查询用户时不立即查询其所有订单调用user.getOrders()时才查询。2. 底层实现机制动态代理MyBatis懒加载的核心技术是字节码动态代理默认使用CGLIB代理也可配置JDK动态代理原理如下结果映射阶段查询主对象后处理关联属性association/collection时不直接实例化关联对象代理对象创建通过ProxyFactory为关联属性创建代理对象代理对象内部持有主对象的上下文与待执行的关联查询SQL方法拦截当代码调用关联对象的getter方法如user.getOrders()时代理对象的MethodInterceptor拦截方法调用触发加载拦截后调用ResultLoader执行预设的关联SQL查询真实数据并赋值给代理对象后续调用直接返回已加载的数据。3. MyBatis核心配置与触发逻辑配置项默认值作用lazyLoadingEnabledfalse全局开启/关闭懒加载aggressiveLazyLoadingfalse开启后调用主对象的equals、hashCode、toString等方法会触发全量懒加载关闭后仅调用getter时触发lazyLoadTriggerMethodsequals,clone,hashCode,toString指定触发懒加载的方法列表fetchType-局部指定单个关联的加载策略lazy/eager优先级高于全局配置4. 优缺点与适用边界优点减少不必要的数据库查询降低内存开销提升主查询速度缺点多次查询会增加数据库交互次数N1问题不适合批量数据处理适用场景关联数据使用概率低、主查询性能要求高、单条数据查询为主的场景不适用场景批量数据导出、列表页关联数据必展示的场景建议用联表查询替代。六、知识体系串联与实践原则安全底线参数绑定默认用#{}${}仅用于SQL结构动态化且必须加白名单校验灵活与规范平衡动态SQL解决结构变化避免硬编码但禁止过度动态导致SQL不可维护性能取舍懒加载是“时间换空间”的优化策略需结合业务数据访问模式选型避免N1性能反噬底层本质四大能力最终都围绕ORM的核心矛盾——对象模型与关系模型的映射效率、开发效率与运行效率的平衡。