找回密码
 立即注册
首页 业界区 业界 MyBatis 动态 SQL 与缓存机制深度解析

MyBatis 动态 SQL 与缓存机制深度解析

田雅宁 2025-6-27 21:01:47
在Java持久层技术体系中,MyBatis凭借其灵活的SQL映射和强大的动态SQL能力,成为企业级应用开发的首选框架。本文从动态SQL核心语法、缓存实现原理、性能优化及面试高频问题四个维度,结合源码与工程实践,系统解析MyBatis的核心特性与最佳实践。
一、动态SQL核心语法与实现原理

1.1 动态SQL标签体系

标签作用示例场景条件判断,按需拼接SQL片段动态查询(如多条件筛选)类似于Java的switch语句,多选一执行单条件查询(不同条件互斥)智能处理WHERE子句,自动剔除多余的AND/OR动态WHERE条件智能处理UPDATE语句,自动剔除多余的逗号动态更新(部分字段更新)遍历集合,生成批量SQL批量插入、IN条件查询自定义前缀、后缀处理,可替代、高级SQL片段处理1.2 动态SQL执行流程

1.png

关键步骤解析:


  • SQL节点解析

    • XML配置中的动态标签(如)被解析为SqlNode对象(如IfSqlNode)。

  • OGNL表达式计算

    • 使用OGNL(Object Graph Navigation Language)计算动态条件(如#{username} != null)。

  • 参数绑定

    • 通过TypeHandler将Java对象转换为JDBC类型(如String → VARCHAR)。

1.3 高级应用:自定义SQL提供器

1. 使用@Provider注解
  1. @SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  2. List<User> selectByCondition(Map<String, Object> params);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  3. // 自定义SQL提供器<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  4. public class UserSqlProvider {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  5. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  public String selectByCondition(Map<String, Object> params) {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  6. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  SQL sql = new SQL();<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  7. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  sql.SELECT("*").FROM("users");<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  8. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  if (params.containsKey("username")) {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  9. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  sql.WHERE("username = #{username}");<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  10. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  11. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  if (params.containsKey("age")) {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  12. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  sql.WHERE("age >= #{age}");<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  13. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  14. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  return sql.toString();<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  15. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  16. }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
复制代码
2. 流式SQL构建(SQL类)
  1. String sql = new SQL()<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  2. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  .SELECT("id", "username")<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  3. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  .FROM("users")<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  4. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  .WHERE("status = 'ACTIVE'")<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  5. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  .ORDER_BY("create_time DESC")<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  6. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  .toString();<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
复制代码
二、缓存机制深度解析

2.1 一级缓存(本地缓存)

1. 核心特性


  • 作用域:SqlSession级别(同一个会话内共享)。
  • 生命周期:与SqlSession一致,会话关闭时缓存清空。
  • 实现类:PerpetualCache(基于HashMap)。
2. 源码关键逻辑
  1. public class DefaultSqlSession implements SqlSession {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  2. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  private final Executor executor;<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  3. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  @Override<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  4. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  public <T> T selectOne(String statement, Object parameter) {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  5. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  List<T> list = this.selectList(statement, parameter);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  6. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  // 一级缓存逻辑在Executor中实现<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  7. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  return list.isEmpty() ? null : list.get(0);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  8. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  9. }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  10. public class BaseExecutor implements Executor {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  11. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  private final PerpetualCache localCache;<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  12. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  @Override<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  13. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  14. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  BoundSql boundSql = ms.getBoundSql(parameter);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  15. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  16. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  17. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  18. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  @Override<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  19. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  20. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  // 先从一级缓存获取<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  21. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  List<E> list = (List<E>) localCache.getObject(key);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  22. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  if (list != null) {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  23. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  24. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  return list;<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  25. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  } else {<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  26. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  // 缓存未命中,执行数据库查询<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  27. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  28. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  return list;<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  29. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  30. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
  31. }<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
复制代码
2.2 二级缓存(全局缓存)

1. 核心特性


  • 作用域:namespace级别(跨会话共享)。
  • 配置方式
    1. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
    复制代码
  • 默认实现:PerpetualCache(可替换为Redis、Ehcache等)。
2. 缓存配置参数

参数作用eviction缓存淘汰策略(LRU/FIFO/SOFT/WEAK)flushInterval刷新间隔(毫秒,默认不刷新)size缓存最大容量(元素个数)readOnly是否只读(true则返回缓存对象的引用,性能更高)2.3 缓存工作流程

2.png

关键注意点:


  • 缓存失效
    增删改操作(INSERT/UPDATE/DELETE)默认会清空所在namespace的二级缓存。
  • 嵌套查询
    嵌套查询(、)可能导致二级缓存失效(取决于useCache属性)。
三、缓存集成与性能优化

3.1 第三方缓存集成(Redis示例)

1. 添加依赖
  1. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  org.mybatis.caches<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  mybatis-redis<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  1.0.0-beta2<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
复制代码
2. 配置Redis缓存
  1. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  host=127.0.0.1<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  port=6379<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  timeout=2000<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
复制代码
3.2 性能优化策略

1. 合理使用缓存级别


  • 一级缓存:默认开启,适合短期高频查询(如同一请求内多次查询相同数据)。
  • 二级缓存:需显式配置,适合全局共享且读多写少的数据(如字典表、配置信息)。
2. 缓存参数调优
  1. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
复制代码
3. 避免缓存穿透与雪崩


  • 缓存穿透
    查询不存在的数据导致每次都访问数据库,可通过布隆过滤器或缓存空值解决。
  • 缓存雪崩
    大量缓存同时失效导致瞬间数据库压力剧增,可通过设置随机过期时间避免。
四、面试高频问题深度解析

4.1 基础概念类问题

Q:MyBatis动态SQL与Hibernate Criteria API的区别?
A:
维度MyBatis动态SQLHibernate Criteria APISQL控制完全手动控制,灵活性高通过API生成,灵活性低学习成本较低(熟悉XML标签即可)较高(需掌握对象化查询API)性能接近原生SQL,性能优化空间大可能生成冗余SQL,优化难度高适用场景复杂SQL场景(如多表关联、嵌套查询)简单增删改查场景Q:MyBatis一级缓存与二级缓存的区别?
A:
特性一级缓存二级缓存作用域SqlSession级别Namespace级别生命周期会话关闭后失效应用启动到关闭默认开启是否缓存共享同一个会话内共享跨会话共享实现类PerpetualCache可自定义(如RedisCache)4.2 实现原理类问题

Q:MyBatis如何实现动态SQL的条件判断?
A:

  • 通过OGNL表达式计算条件(如#{username} != null)。
  • 解析为对应的SqlNode实现类(如IfSqlNode)。
  • 在SQL执行时动态决定是否包含该SQL片段。
Q:二级缓存的嵌套查询会导致什么问题?如何解决?
A:

  • 问题:嵌套查询默认不使用二级缓存,可能导致重复查询数据库。
  • 解决方案

    • 设置useCache="true"和flushCache="false"。
    • 使用的嵌套映射替代嵌套查询。

4.3 实战调优类问题

Q:如何解决MyBatis缓存与数据库一致性问题?
A:

  • 更新策略

    • 增删改操作后强制刷新缓存(默认行为)。
    • 设置合理的缓存过期时间(如5分钟)。

  • 读写分离场景

    • 主库写操作后立即刷新缓存。
    • 从库读操作使用缓存,通过数据库主从同步保证最终一致性。

Q:MyBatis动态SQL中标签与标签的区别?
A:


  • 自动添加WHERE关键字,并剔除多余的AND/OR。

  • 可自定义前缀、后缀处理,如:
    1. <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  ...<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>  
    复制代码
    更灵活,可替代标签。
总结:动态SQL与缓存的最佳实践

动态SQL设计原则


  • 简洁优先:避免过度复杂的动态SQL,优先使用、等基础标签。
  • 参数校验:在Java代码中进行参数校验,避免在动态SQL中处理复杂逻辑。
  • SQL片段复用:使用标签定义公共SQL片段,通过复用。
缓存使用策略


  • 读多写少场景:启用二级缓存,如字典表、配置信息。
  • 写操作频繁场景:禁用二级缓存,避免频繁刷新影响性能。
  • 分布式环境:使用Redis等分布式缓存替代默认实现,保证跨节点缓存一致性。
通过系统化掌握MyBatis动态SQL与缓存机制的核心原理及最佳实践,面试者可在回答中精准匹配问题需求,例如分析“如何优化复杂查询性能”时,能结合动态SQL优化与缓存策略,展现对持久层技术的深度理解与工程实践能力。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册