MyBatis 知识点总结 一、MyBatis 简介MyBatis是一款优秀的持久层框架它支持自定义 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作可以使用简单的 XML 或注解来配置和映射原生信息将接口和 Java 的 POJOsPlain Old Java Objects普通的 Java 对象映射成数据库中的记录。二、核心组件与工作原理1. 核心组件SqlSessionFactoryBuilder用于创建 SqlSessionFactory 实例。SqlSessionFactory每个 MyBatis 应用的核心用于创建 SqlSession 实例。SqlSession包含了执行 SQL 命令所需的所有方法是 MyBatis 与数据库交互的会话。ExecutorMyBatis 执行器负责 SQL 语句的生成和查询缓存的维护。MappedStatement封装了 SQL 语句的映射信息。ParameterHandler负责将用户传递的参数转换成 JDBC Statement 所需的参数。ResultSetHandler负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 集合。2. 工作原理读取 MyBatis 配置文件mybatis-config.xml和映射文件Mapper.xml。通过 SqlSessionFactoryBuilder 构建 SqlSessionFactory。SqlSessionFactory 创建 SqlSession。SqlSession 调用 Executor 执行器执行映射文件中定义的 SQL。Executor 通过 MappedStatement 对象获取 SQL 信息通过 ParameterHandler 设置参数通过 ResultSetHandler 处理结果集。返回处理结果。三、配置文件详解1. 核心配置文件mybatis-config.xml?xml version1.0 encodingUTF-8? !DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//EN http://mybatis.org/dtd/mybatis-3-config.dtd configuration !-- 环境配置 -- environments defaultdevelopment environment iddevelopment transactionManager typeJDBC/ dataSource typePOOLED property namedriver valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/mybatis_db/ property nameusername valueroot/ property namepassword value123456/ /dataSource /environment /environments !-- 映射器配置 -- mappers mapper resourcecom/example/mapper/UserMapper.xml/ /mappers /configuration2. 映射文件Mapper.xml?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.mapper.UserMapper !-- 结果映射 -- resultMap iduserResultMap typecom.example.entity.User id propertyid columnid/ result propertyusername columnusername/ result propertyemail columnemail/ /resultMap !-- 查询语句 -- select idselectUserById resultMapuserResultMap SELECT * FROM user WHERE id #{id} /select /mapper四、动态 SQLMyBatis 的动态 SQL 功能是其核心特性之一它允许在 XML 映射文件中编写灵活的 SQL 语句根据传入参数的条件动态地拼接 SQL 片段从而避免了在 Java 代码中拼接字符串的繁琐和潜在的安全风险。1. 动态 SQL 标签if 标签作用根据条件判断是否包含 SQL 片段。属性test属性用于指定 OGNL 表达式表达式结果为 true 时标签内的 SQL 会被包含。select idfindActiveUser resultTypeUser SELECT * FROM user WHERE status ACTIVE if testusername ! null and username ! AND username #{username} /if if testemail ! null AND email #{email} /if /selectchoose, when, otherwise 标签作用实现多分支条件选择类似于 Java 中的switch-case-default语句。select idfindUserByState resultTypeUser SELECT * FROM user WHERE choose when teststate ACTIVE status ACTIVE /when when teststate INACTIVE status INACTIVE /when otherwise status IS NOT NULL /otherwise /choose /selecttrim, where, set 标签where智能处理 WHERE 子句。只有当其包含的标签有返回值时才会插入 “WHERE” 关键字。如果标签返回的内容以 “AND” 或 “OR” 开头它会自动将其去除。!-- 使用 where 标签避免 WHERE 后直接跟 AND 导致语法错误 -- select idfindUsers resultTypeUser SELECT * FROM user where if testusername ! null AND username #{username} /if if testemail ! null AND email #{email} /if /where /selectset智能处理 UPDATE 语句中的 SET 子句。它会动态地在行首添加 SET 关键字并会删掉无关的逗号。update idupdateUser UPDATE user set if testusername ! nullusername #{username},/if if testemail ! nullemail #{email},/if if teststatus ! nullstatus #{status},/if /set WHERE id #{id} /updatetrim功能更强大的标签可以自定义前缀、后缀以及要删除的前缀/后缀字符串。可用于替代where或set。!-- 用 trim 实现 where 标签的功能 -- select idfindUsers resultTypeUser SELECT * FROM user trim prefixWHERE prefixOverridesAND |OR if testusername ! null AND username #{username} /if if testemail ! null AND email #{email} /if /trim /selectforeach 标签作用遍历集合如 List、Set、数组、Map常用于构建 IN 条件或批量操作。属性collection指定要遍历的集合属性名。item遍历过程中每个元素的别名。index遍历 List 或数组时的索引遍历 Map 时的 key。open遍历开始时添加的字符串。close遍历结束时添加的字符串。separator元素之间的分隔符。!-- 构建 IN 查询 -- select idselectUsersInIds resultTypeUser SELECT * FROM user WHERE id IN foreach collectionids itemid open( separator, close) #{id} /foreach /select !-- 批量插入 -- insert idbatchInsertUsers INSERT INTO user (username, email) VALUES foreach collectionuserList itemuser separator, (#{user.username}, #{user.email}) /foreach /insertbind 标签作用创建一个变量通常用于模糊查询或字符串处理并将其绑定到上下文可以在 OGNL 表达式中使用。select idfindUsersLikeName resultTypeUser bind namepattern value% username % / SELECT * FROM user WHERE username LIKE #{pattern} /select2. 动态 SQL 原理与最佳实践原理MyBatis 在解析 XML 映射文件时会构建一个包含动态 SQL 节点如if,foreach的 SQL 源对象。执行时根据传入的参数对象使用 OGNL 表达式计算每个动态节点的test条件动态地拼接出最终的 SQL 字符串。最佳实践优先使用where和set标签避免 SQL 语法错误。在if的test条件中使用and和or进行逻辑判断注意字符串空值判断testname ! null and name ! 。对于复杂的动态 SQL可以考虑使用sql标签定义可重用的 SQL 片段再用include标签引用提高可维护性。注意性能过于复杂的动态 SQL 可能会影响 SQL 解析和缓存效率。对于固定模式的多条件查询可以考虑使用 MyBatis-Plus 等增强框架的 QueryWrapper。3. 综合示例select idselectByCondition resultTypeUser SELECT * FROM user where if testusername ! null and username ! bind nameusernamePattern value% username % / AND username LIKE #{usernamePattern} /if if teststatusList ! null and statusList.size() 0 AND status IN foreach collectionstatusList itemstatus open( separator, close) #{status} /foreach /if choose when testorderBy name ORDER BY username /when when testorderBy time ORDER BY create_time DESC /when otherwise ORDER BY id /otherwise /choose /where /select五、高级特性1. 一级缓存与二级缓存一级缓存SqlSession 级别默认开启同一个 SqlSession 中执行相同的 SQL 查询第二次会从缓存中获取。二级缓存Mapper 级别需要手动开启多个 SqlSession 共享缓存数据。2. 延迟加载懒加载与立即加载延迟加载Lazy Loading也称为懒加载是指在需要时才加载关联数据。MyBatis 默认使用立即加载但可以通过配置开启延迟加载。立即加载Eager Loading在查询主对象时同时加载所有关联对象的数据。配置延迟加载!-- 在 mybatis-config.xml 中配置 -- settings !-- 开启延迟加载 -- setting namelazyLoadingEnabled valuetrue/ !-- 开启按需加载3.4.1版本默认true -- setting nameaggressiveLazyLoading valuefalse/ /settings映射文件中的配置!-- 在 resultMap 中配置关联关系的加载方式 -- resultMap idblogResultMap typeBlog id propertyid columnid/ result propertytitle columntitle/ !-- 立即加载 -- association propertyauthor columnauthor_id javaTypeAuthor selectselectAuthorById/ !-- 延迟加载 -- collection propertycomments columnid ofTypeComment selectselectCommentsByBlogId fetchTypelazy/ /resultMap立即加载 vs 延迟加载的选择立即加载适用场景关联数据量小且大概率会被使用需要一次性获取完整对象图N1 查询问题不严重的情况延迟加载适用场景关联数据量大可能不会被使用需要优化首次查询性能避免不必要的数据库查询关联对象层级较深的情况延迟加载的实现原理MyBatis 通过动态代理技术CGLIB 或 Javassist创建关联对象的代理。当访问代理对象的属性或方法时才会触发真正的 SQL 查询。3. 插件InterceptorMyBatis 允许在映射语句执行过程中的某些点进行拦截调用可以用于分页、性能监控、SQL 日志等场景。六、常见面试题#{} 和 ${} 的区别是什么#{}预编译处理可以防止 SQL 注入。${}字符串替换直接拼接 SQL 语句存在 SQL 注入风险。MyBatis 如何实现延迟加载通过配置setting namelazyLoadingEnabled valuetrue/开启使用 CGLIB 或 Javassist 创建代理对象在真正访问关联对象时才执行查询。MyBatis 有哪些执行器SimpleExecutor每执行一次 update 或 select就开启一个 Statement 对象用完立刻关闭。ReuseExecutor执行 update 或 select以 SQL 作为 key 查找 Statement 对象存在就使用不存在就创建。BatchExecutor执行 update没有 selectJDBC 批处理不支持 select将所有 SQL 都添加到批处理中等待统一执行。