Skip to content

ORM 面试题

本页只放 ORM 面试标准回答、项目话术、常见追问和知识点跳转。MyBatis、Hibernate、JPA、缓存、动态 SQL、拦截器、事务和 SQL 优化的详细原理放到知识点页。

使用方式

mermaid
flowchart TD
    A["面试页:回答差异和风险"] --> B["知识点页:理解 SQL 执行和映射原理"]
    B --> C["项目页:数据访问、事务、性能排查"]

高频问题

面试题标准回答原理知识点
怎么判断 ORM 是否真正学懂不能只会 Mapper、Wrapper 或 Repository API。真正学懂要能从 JDBC 痛点、MyBatis Mapper 代理、MappedStatementBoundSql、动态 SQL、参数绑定、结果映射、缓存、插件、MyBatis-Plus、JPA 持久化上下文、脏检查、懒加载、N+1、事务边界、批量写入和慢 SQL 排查一路讲下来,并能说明每个环节为什么这样设计、不这样会出什么生产问题。ORM从零到精通验收清单
ORM 应该怎么从零学到生产级不能只学 Mapper 或 Repository API,要按 JDBC 痛点、参数绑定、SQL 执行、结果映射、缓存、事务、批量写入、JPA 持久化上下文、慢 SQL 排查这条链路学习。ORM从零到生产级掌握
ORM 是什么ORM 是对象关系映射,把 Java 对象和关系型数据库表之间的读写转换起来,减少手写 JDBC 的重复代码。ORM总览JPA基础
MyBatis 和 Hibernate 区别MyBatis 是半自动 ORM,SQL 主要由开发者控制,适合复杂 SQL 和性能可控场景;Hibernate 更强调实体状态、持久化上下文、脏检查和对象关系映射,CRUD 开发快,但复杂 SQL 要关注最终生成语句。MyBatis核心全过程原理Hibernate/JPA核心全过程原理
MyBatis 为什么 Mapper 接口能执行MyBatis 会为 Mapper 接口创建代理对象,调用方法时根据 namespace、方法名和参数找到 MappedStatement,动态 SQL 生成 BoundSql,再通过 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 完成执行与映射。商业场景训练营MyBatis核心全过程原理
#{}${} 区别#{} 使用预编译参数,能防 SQL 注入;${} 是字符串拼接,适合动态表名、字段名、排序字段,但必须做白名单校验。MyBatis动态SQL
MyBatis 一级缓存和二级缓存一级缓存是 SqlSession 级别,默认开启;二级缓存是 namespace 级别,需要配置。生产中二级缓存要谨慎,因为多表更新、分布式部署容易带来一致性问题。MyBatis缓存机制
N+1 查询是什么先查询一批主对象,再循环访问懒加载关联,每条主对象再触发一条 SQL,导致 SQL 次数从 1 变成 1+N。可用 DTO 投影、fetch join、批量抓取或手写 join 解决。Hibernate/JPA核心全过程原理Spring Data JPA查询
MyBatis 插件能做什么插件通过动态代理拦截 Executor、StatementHandler、ParameterHandler、ResultSetHandler,可做分页、慢 SQL、数据权限、字段加密等横切增强。MyBatis拦截器
MyBatis 动态 SQL 最后怎么执行<if><where><foreach> 等标签先根据参数生成最终 SQL 和参数映射,形成 BoundSql,后续仍然通过 PreparedStatement 执行,不是在数据库里执行 XML 标签逻辑。MyBatis核心全过程原理MyBatis动态SQL
BoundSql 是什么BoundSql 是 MyBatis 动态 SQL 处理后的运行时 SQL 对象,里面包含最终 SQL 文本、参数映射、参数对象和附加参数。排查动态 SQL 时要看 BoundSql,因为 XML 只是模板,数据库真正执行的是 BoundSql 里的 SQL。MyBatis动态SQL:动态SQL本质MyBatis核心全过程原理
MyBatis 动态 SQL 为什么要用 wheresetwhere 会在有条件时生成 where,并去掉开头多余的 and/or;set 会生成 update set 并去掉结尾多余逗号。它们能减少动态 SQL 拼接错误,但不能替代业务校验。更新和删除仍要校验关键条件,避免全表更新或删除。MyBatis动态SQL:where/set/trim
foreach 有什么坑foreach 常用于 in 查询和批量插入,但集合为空可能生成非法 SQL,集合太大会导致 SQL 很长、参数过多、优化器估算不准和网络包变大。生产中要先判断空集合,并控制批大小,超大批量考虑临时表、中间表或批任务。MyBatis动态SQL:foreach边界
动态排序为什么容易 SQL 注入排序字段属于 SQL 结构,不能用 #{} 作为字段名,很多人会用 ${} 拼接;${} 是字符串替换,如果直接接收前端输入,会改变 SQL 结构造成注入。动态排序必须在 Java 层把字段名和排序方向做白名单映射。MyBatis动态SQL:排序白名单
MyBatis 事务为什么放 Service 层Service 层代表一个完整业务动作,可能包含多个 Mapper 调用。事务放在 Mapper 层只能覆盖单条数据访问,无法保证订单、明细、日志等多个写操作一起提交或回滚。商业场景训练营MyBatis核心全过程原理事务与一致性
MyBatis 查询有数据但对象字段为空怎么办先确认 SQL 返回列,再检查列别名、驼峰映射、resultMap 的 column/property、类型处理器和多表 join 的重复列名。MyBatis核心全过程原理
MyBatis 一级缓存为什么第二次不查库一级缓存是 SqlSession 级别,默认开启。同一个 SqlSession 内相同 MappedStatement、相同 SQL、相同参数、相同分页信息可能命中本地缓存。执行写操作、commit、rollback、close 会清空一级缓存。Spring 项目中不要为了缓存而扩大事务或 SqlSession 生命周期。MyBatis缓存机制:一级缓存生命周期
MyBatis 二级缓存为什么慎用二级缓存是 Mapper namespace 级别,多个 SqlSession 可共享,但它很难感知其他 namespace、多表关联、分布式节点和外部系统对数据的修改,容易读到旧数据。强一致业务通常不建议依赖 MyBatis 二级缓存,业务缓存应显式设计 Redis 和失效策略。MyBatis缓存机制:二级缓存一致性
MyBatis-Plus 是什么MyBatis-Plus 是 MyBatis 增强工具,提供通用 CRUD、Wrapper 条件构造器、分页插件、逻辑删除、自动填充、乐观锁、代码生成等能力,但复杂 SQL 仍要回到 MyBatis 和 SQL 本身。MyBatis-Plus核心全过程原理MyBatis-Plus学习路线
MyBatis-Plus Wrapper 原理是什么Wrapper 会把 eqlikeinorderBy 等链式调用记录成 SQL 条件片段和参数,拼进 MyBatis-Plus 注入的通用 SQL 模板,最后仍然形成 MyBatis 的 BoundSql 并由 JDBC 执行。它提高简单单表查询效率,但不能替代 SQL 和索引分析。MyBatis-Plus Wrapper
QueryWrapper 和 LambdaQueryWrapper 区别QueryWrapper 使用字符串字段名,写法直观但重构不安全;LambdaQueryWrapper 使用方法引用,框架会解析实体属性再映射到数据库列名,重构更安全。但 LambdaWrapper 只是可维护性更好,不代表 SQL 执行更快。MyBatis-Plus Wrapper:两者区别
Wrapper 中 or 有什么坑如果 or 没有分组,可能破坏前面的租户、医院、逻辑删除、权限条件,导致查询范围扩大甚至越权。关键字搜索这类 (name like ? or code like ?) 应该用 and(w -> ...) 包裹,确保 SQL 语义正确。MyBatis-Plus Wrapper:and/or 分组
Wrapper 的 last 为什么危险last 会把字符串直接追加到 SQL 末尾,绕过参数绑定。如果接收前端输入,可能造成 SQL 注入或破坏 SQL 结构。生产中只允许固定常量,分页优先用分页插件,不要把 last 封装成可传任意片段的公共能力。MyBatis-Plus Wrapper:last 风险
MyBatis-Plus 分页插件原理分页插件通过 MyBatis 插件机制拦截 SQL,根据数据库方言生成 count SQL 和分页 SQL。分页慢通常要排查 count、深分页、排序索引、扫描行数和回表。MyBatis-Plus核心全过程原理
MyBatis-Plus 逻辑删除有什么坑逻辑删除会把 delete 改成 update 标记,并给查询追加未删除条件。它会影响唯一索引、历史查询、表体积和统计逻辑,不能只加注解不做表设计。MyBatis-Plus核心全过程原理
MyBatis 拦截器能拦哪些对象MyBatis 插件可以拦截 Executor、StatementHandler、ParameterHandler、ResultSetHandler。Executor 适合统计和审计入口,StatementHandler 适合分页和 SQL 改写,ParameterHandler 适合参数处理,ResultSetHandler 适合结果处理。插件是横切增强,不适合写复杂业务逻辑。MyBatis拦截器:拦截位置
分页插件为什么不能解决深分页慢分页插件只能把 SQL 改写成数据库分页语法,不能改变数据库需要扫描和跳过大量行的事实。深分页慢要从游标分页、索引、查询条件、异步导出和业务交互上优化,不能只依赖插件。MyBatis拦截器:分页插件原理MySQL大表覆盖索引优化
数据权限插件有什么风险数据权限插件会隐式改写 SQL,复杂 SQL、别名、子查询、分页 count、不同角色规则都可能出错;追加的权限条件如果没有索引支持,还会拖慢所有查询。商业项目里插件适合统一兜底,复杂权限最好显式建模并配套测试和审计。MyBatis拦截器:数据权限
JPA 和 Hibernate 关系JPA 是 Java 持久化规范,定义 Entity、EntityManager、持久化上下文、JPQL 等标准;Hibernate 是常见实现,真正负责实体状态管理、脏检查、SQL 生成和懒加载。Hibernate/JPA核心全过程原理JPA基础
Spring Data JPA 做了什么它进一步封装 JPA Repository,启动时为接口创建代理,能根据内置方法、方法名、@Query、Specification 生成查询入口,底层仍然通过 EntityManager 和 Hibernate 工作。Spring Data JPA核心全过程原理Hibernate/JPA核心全过程原理
Repository 接口为什么能执行Spring Data JPA 启动时扫描 Repository 接口,根据泛型识别实体和主键类型,为接口创建代理对象。调用方法时代理判断是内置 CRUD、方法名查询、@Query 还是自定义实现,再调用 EntityManager。Spring Data JPA核心全过程原理
方法名查询原理是什么框架会解析 findByStatusAndNameContaining 这类方法名,把操作类型、实体属性、连接词和查询关键字转换成查询模型,最后交给 Hibernate 生成 SQL。复杂条件不建议继续拉长方法名。Spring Data JPA核心全过程原理Spring Data JPA查询
Page 和 Slice 区别Page 会查总数和当前页数据,能返回总页数;Slice 通常不查总数,只判断是否有下一页。大表或信息流场景更适合 Slice 或游标分页。Spring Data JPA核心全过程原理事务与分页
JPA 为什么修改实体不用 update事务内查询出来的实体处于托管态,持久化上下文保存快照;提交前 flush 做脏检查,发现字段变化后由 Hibernate 生成 update SQL。Hibernate/JPA核心全过程原理
flush 和 commit 区别flush 是把持久化上下文的变更同步成 SQL 发给数据库;commit 是提交数据库事务。flush 后仍可回滚,commit 后事务才真正结束。Hibernate/JPA核心全过程原理
JPA 实体有哪些状态常见状态有临时态、托管态、游离态、删除态。new 出来的对象是临时态,persist 或查询返回后进入托管态,事务结束或 detach 后变成游离态,remove 后进入删除态。只有托管态实体的变化会被持久化上下文脏检查自动同步。JPA基础:实体状态Hibernate核心概念
持久化上下文是什么持久化上下文是事务内的实体管理区,保存托管实体、实体快照、待执行动作和一级缓存。它让 JPA 能做同一事务内实体复用、脏检查、延迟写入和懒加载。JPA基础:持久化上下文Hibernate核心概念
merge 为什么要谨慎merge 会把游离对象状态合并到托管对象,如果请求对象字段不完整,null 也可能覆盖数据库已有字段。生产中更推荐先按 id 查询托管实体,再调用有业务含义的方法修改指定字段。JPA基础:merge风险Hibernate核心概念:merge
JPA 懒加载为什么会失败懒加载通过代理实现,访问关联字段时需要持久化上下文还存在。事务结束后 EntityManager 关闭,再访问懒加载字段就可能出现 LazyInitializationException。解决方式是在 Service 事务内转 DTO,或使用 fetch join / EntityGraph / DTO 投影。JPA基础:懒加载Hibernate核心概念:懒加载代理
Hibernate N+1 怎么解决N+1 是先查一批主实体,再循环访问懒加载关联导致额外 N 次 SQL。可用 DTO 投影、fetch join、EntityGraph 或先批量查关联数据再组装。列表页更推荐 DTO 投影,避免加载不需要的实体图。Hibernate核心概念:N+1Spring Data JPA查询
为什么不建议返回 EntityEntity 是持久化模型,直接返回容易暴露字段、触发懒加载异常、造成循环引用和隐式 SQL。接口层应返回 DTO。Hibernate/JPA核心全过程原理Spring Data JPA实战建议
事务边界放哪里通常放在 Service 层,覆盖一个完整本地数据库业务动作。事务不能过大,也不要把慢远程调用、MQ 发送、耗时计算放进事务里。ORM事务与一致性Spring事务
Spring 事务为什么失效常见原因是同类自调用没经过代理、异常被 catch 吞掉、受检异常未配置 rollbackFor、方法不是 public、多数据源事务管理器不一致、手动连接绕过 Spring。ORM事务与一致性Spring事务
Spring Data JPA 方法名查询有什么边界方法名查询适合简单固定条件,条件一多方法名会非常长,可读性和维护性变差。复杂固定查询用 @Query,动态组合条件用 Specification 或 Querydsl,列表接口优先 DTO 投影。Spring Data JPA查询:方法名边界
JPQL 和原生 SQL 区别JPQL 面向实体名和属性名,可移植性较好;原生 SQL 面向表名和字段名,可控性更强、适合数据库特性和复杂 SQL,但可移植性差,字段映射要自己关注。Spring Data JPA查询:JPQL和原生SQL
DTO 投影为什么推荐DTO 投影只查询接口需要字段,避免 Entity 字段暴露、懒加载、循环引用和多余 SQL。列表页、详情页接口应优先返回 DTO,而不是直接返回 Entity。Spring Data JPA查询:DTO投影
Page 为什么可能慢Page 通常执行数据查询和 count 查询,大表、复杂 join、group by、distinct 场景下 count 可能很慢。不需要总数时用 Slice;复杂查询可以手写更简单的 countQuery。事务与分页:Page countSpring Data JPA查询:Page慢
Slice 和 Page 区别Page 返回当前页、总条数、总页数,通常要查 count;Slice 只返回当前片段和是否有下一页,通常不查 count,适合信息流、日志、采集记录滚动加载。事务与分页:Slice
MyBatis 和 JPA 事务差异MyBatis 通常调用 Mapper 时执行 SQL;JPA/Hibernate 会管理托管实体,提交前 flush 脏检查生成 SQL。两者最终都依赖 Spring 绑定到当前线程的数据库连接提交或回滚。ORM事务与一致性
事务里为什么不能直接发 MQMQ 不受本地数据库事务控制,可能消息发出后数据库回滚,也可能数据库提交后消息发送失败。可用 afterCommit 或本地消息表,消费端要幂等。ORM事务与一致性可靠消息最终一致性
SQL 慢怎么排查先定位慢 SQL,再看执行计划、索引命中、扫描行数、排序、分页、关联方式、锁等待和数据库资源。EXPLAIN执行计划MySQL优化

项目话术

text
在医疗数据采集平台里,采集落库和资产查询通常用 MyBatis 或 MyBatis-Plus,因为字段映射、批量写入、动态条件查询和复杂统计 SQL 比较多。事务边界放在 Service 层,批量采集要控制批大小,慢 SQL 会通过日志、执行计划和索引分析排查。对于简单后台 CRUD,也可以用 JPA 或 Spring Data JPA,但要持续关注最终 SQL。

常见追问

追问回答方向跳转
为什么不建议随便开二级缓存多表关联、分布式部署、更新路径复杂时容易读到旧数据。MyBatis缓存机制
${} 一定不能用吗不是,动态字段和排序可能需要,但必须白名单。MyBatis动态SQL
JPA 懒加载为什么容易报错实体离开持久化上下文后再访问懒加载字段,会没有 Session 可用。Hibernate/JPA核心全过程原理
批量插入怎么优化不要用 for 循环逐条 insert。MyBatis 常见方案是 foreach 拼多 values 或 ExecutorType.BATCHforeach 简单但 SQL 不能过长;BatchExecutor 更适合大量重复写入,但要定期 flush、控制内存和事务。MySQL 还要关注 rewriteBatchedStatements=trueMyBatis批量操作
foreachExecutorType.BATCH 区别foreach 是动态 SQL 在 MyBatis 侧拼成一条多 values SQL;ExecutorType.BATCH 是 JDBC 批处理,把同一 SQL 的多组参数累积后 executeBatch。前者写法简单,后者适合大批量但更考验 flush、提交、失败处理。MyBatis批量操作:两者对比
批量导入事务怎么设计不要十万条一个事务。更合理的是创建导入任务,解析校验后按 500/1000 等批大小落库,每批独立事务,记录成功数和失败明细。失败时可拆小批或单条定位,并用唯一索引或业务流水号保证幂等。MyBatis批量操作:事务边界ORM事务与一致性
批量导入部分失败怎么办先做格式校验,把明显错误行记录下来;批量 SQL 失败后降级为小批或单条定位,记录行号、原因和原始数据,支持用户修正后重传。不能只返回系统异常,也不能因为一行坏数据让整个大文件不可追踪。MyBatis批量操作:部分失败
批量写入为什么会拖垮数据库批量过大可能导致 SQL 包过大、事务锁时间变长、redo/binlog 或事务日志压力上升、连接长期占用、唯一键冲突反复回滚。优化要看批大小、事务、索引、锁等待、数据库日志和连接池,而不只是换一个 MyBatis 标签。MyBatis批量操作:线上排查

面试回答模板

text
ORM 我会先区分 MyBatis 和 Hibernate。MyBatis 更偏 SQL 可控,适合复杂查询和批量处理;Hibernate/JPA 更偏实体状态管理,CRUD 方便但要看最终 SQL。项目里我会把事务放在 Service 层,Mapper 只负责数据访问;动态 SQL 使用 #{ } 预编译,必须拼接字段时做白名单;慢 SQL 按日志、执行计划、索引、分页、关联和锁等待逐层排查。

深度验收跳转

如果面试官继续追问原理,不要只停留在“我会 MyBatis 和 JPA”。按 ORM 从零到精通验收清单 回到知识点页,把 Mapper 代理、SQL 生成、参数绑定、缓存一致性、插件拦截、JPA 脏检查、N+1、事务失效、批量导入和慢 SQL 排查讲完整。

本章小结

ORM 面试页负责“怎么说”。Mapper 代理为什么能执行、缓存为什么有一致性风险、JPA 为什么会自动更新、SQL 为什么慢,都要去知识点页看原理、流程和 Demo。