参考:
MyBatis结构和组件 //www.greatytc.com/p/75a6a2297c69
MyBatis运行流程、缓存和插件 //www.greatytc.com/p/4fe280c44294
MyBatis缓存
//www.greatytc.com/p/7a98dda8cd75
//www.greatytc.com/p/8801d1aa20a0
MyBatis常见面试题 https://blog.csdn.net/a745233700/article/details/80977133
一.MyBatis概述
1.MyBatis
- 支持普通SQL查询、存储过程和高级映射的持久层框架
- 消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的封装
- MyBatis可使用XML或注解进行配置,将接口和Java的POJO映射成数据库记录
- 专注于Sql本身,适用于对性能要求很高或需求变化较多的项目
2.MyBatis特点
- 优点
1)简单易学。MyBatis本身小而简单
2)灵活。MyBatis允许自由编写Sql语句
3)Sql和程序代码解耦,且提供xml标签支持编写动态sql
4)提供映射标签,支持对象与数据库的ORM字段关系映射
5)与JDBC相比,减少了50%以上的代码量 - 缺点
1)自由编写Sql语句意味较大的工作量
2)Sql语句依赖于数据库,导致数据库无法随意更换,移植性差
二.MyBatis原理
1.MyBatis功能结构
- API接口层
开发人员通过接口API操纵数据库,接口层接收请求后会调用数据处理层完成具体的数据处理 - 数据处理层
根据API接口层调用的请求完成一次数据库操作,负责具体的Sql查找、Sql解析和执行结果映射处理等 - 基础支撑层
实现连接管理、事务管理和执行结果映射处理等基础功能,为数据处理层提供基础支持
2.MyBatis运行流程
- 加载配置
从配置文件或注解获取配置信息并加载成MappedStatement
对象,此对象=传入参数的映射配置+要执行的Sql语句+结果的映射配置 - Sql解析
API接口层接收调用请求时,根据传入的Sql的Id找到对应的MappedStatement
,再根据传入的参数对象解析MappedStatement
,获得最终要执行的Sql语句和参数 - Sql执行
获取数据库连接,执行Sql解析获得的Sql语句和参数,并返回结果 - 结果映射
将数据库返回结果按映射配置转换成需要的数据类型并返回
3.MyBatis主要构件
-
SqlSession
MyBatis主要顶层API,负责和数据库交互的会话,完成增删查改操作 -
Executor
MyBatis执行器,是MyBatis调度和核心,负责Sql语句生成和查询缓存的维护 -
StatementHandler
封装了JDBC Statement,负责JDBC中对Statement的操作,如设置参数、将Statement结果集转换为List集合 -
ParameterHandler
将用户传递的参数转换为JDBC Statement所需的参数 -
ResultSetHandler
将JDBC返回的ResultSet结果集转换为List集合 -
TypeHandler
负责Java数据类型和JDBC数据类型间的映射和转换 -
MappedStatement
维护<select/update/delete/insert>节点的封装 -
SqlSource
根据用户传递的parameterObject,动态生成Sql语句,将信息封装到BoundSql对象并返回 -
BoundSql
表示动态生成的Sql语句及相应的参数信息 -
Configuration
维持MyBatis所有的配置信息
三.MyBatis缓存
1.缓存总览
- 缓存在流程的Executor使用,Executor执行查询时先判断缓存,若缓存命中,直接返回缓存中的结果,否则继续之后的步骤查询数据库
- MyBatis使用底层为HashMap的Cache对象,通过各种装饰类对其层层装饰,以实现各种功能和隔离不同功能逻辑
- MyBatis缓存分为一级缓存和二级缓存,默认开启一级缓存
- 数据查询优先级:二级缓存---一级缓存---数据库
2.一级缓存
- 缓存默认Session级别(可设为Statement),同一会话的重复Sql语句可使用缓存
- 不同Session间缓存相互独立,分布式查询可能读到脏数据
- Executor收到Sql语句后,先查询自己的localCache(HashMap),缓存命中则直接返回结果,缓存命中失败则继续查询数据库后将结果写入localCache
3.二级缓存
- 为解决跨SqlSession的缓存问题,MyBatis提供二级缓存功能
- 二级缓存使用CachingExecutor包装Executor,CachingExecutor负责与全局二级缓存交互
- CachingExecutor优先查询二级缓存,如没有匹配则委托给它包装的Executor匹配的一级缓存
- 二级缓存全局有效,划分到mapper级别(namespace定义),而非整个application使用同一缓存
- 由于不同namespace的二级缓存相互独立,多表查询时容易出现脏数据。如果将多个表的操作放在相同namespace会导致缓存颗粒度过大,频繁刷新缓存
- MyBatis Cache是基于本地的,分布式环境下必然会读取脏数据,用户自定义实现MyBatis的Cache接口开发成本较高,直接使用Redis/Memcached等分布式缓存成本更低,安全性更高
四.MyBatis插件
1.插件概述
- MyBatis插件又称拦截器,采用责任链模式,通过动态代理组织多个插件(拦截器),改变MyBatis默认行为(如Sql重写)
- 插件通过动态代理方式在已映射语句执行过程中的某一点进行拦截调用
- MyBatis插件需要实现Interceptor接口,该接口主要方法有
1)setProperties
:配置MyBatis插件的自定义属性
2)plugin
:插件用来封装目标对象的方法,通过该方法可返回目标对象本身或一个它的代理
3)interceptor
:拦截时执行的方法
4)plugin
接口
五.MyBatis与其他框架
1.MyBatis与iBatis
- iBatis是Apache的一个开源项目,该项目在2010年从Apache Software Foundation迁移到Google Code,并改名MyBatis
- MyBatis是IBatis的升级版,MyBatis在以下方面改善了IBatis功能
1)MyBatis实现接口绑定,使用更方便
2)MyBatis改进了对象关系映射(association和collection)
3)MyBatis在动态Sql语句中采用OGNL表达式取代节点配置
2.MyBatis与Hibernate
- MyBatis
1)Mybatis真正实现了Java代码和Sql分离
2)Mybatis自行编写sql,灵活度高,便于Sql优化 - Hibernate
1)Hibernate数据库无关性好,适用于需求固定的定制化软件,但学习成本高
2)Hibernate大大降低了对象和数据库的耦合度,其数据移植性远大于MyBatis
3)Hibernate在多表关联查询时表现较差
4)Hibernate效率较低,Sql自动生成,不便于人工优化,调优需要相当经验积累 - 场景选择
1)数据量大(超过千万级别的表/单次业务大批量数据(百万条)提交或读取)适用MyBatis
2)表关联度(主要业务表的关联表>20)复杂适用MyBatis
六.常见面试题
1.SqlSession创建过程和相关对象生命周期?
SqlSessionFactoryBuilder
---SqlSessionFactory
---SqlSession
---SqlMapper
-
SqlSessionFactoryBuilder
在构建SqlSessionFactory
后即可销毁 -
SqlSessionFactory
存在于整个MyBatis应用中,类似数据库连接池,不断创建SqlSession
-
SqlSession
类似于数据库连接,在完成一次业务请求后回收 -
SqlMapper
由SqlSession
创建,生命周期SqlMapper
<=SqlSession
2.#{}和${}的区别?
- 前者’#{}‘是预编译处理,MyBatis预编译时会将#{}替换为?号,调用PreparedStatement的set方法赋值
- 后者是xml文件的字符串替换,MyBatis会把${}替换成变量的值
- 使用#{}可防止Sql注入,提高安全性
3.实体类的属性名和表中字段名不一样该如何映射?
- 在Sql语句中定义与实体类属性名一样的别名
- 通过<resultMap>映射字段名和实体类属性名
<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
<!–用id属性来映射主键字段–>
<id property=”id” column=”order_id”>
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>
4.每个xml映射文件都有一个Dao接口与之对应,Dao接口的工作原理是什么?Dao接口里的方法在参数不同时是否可以重载?
- Dao接口即Mapper接口:映射的xml文件的namespace是接口的全限定名;映射文件中MappedStatement的id是接口的方法名;接口方法的参数就是传递给sql的参数
- 调用接口方法时,通过接口全限定名+方法名定位唯一的MappedStatement
- 由于仅使用接口全限定名和方法名定位MappedStatement,故不同参数的接口方法不能重载
- 此外,不同的xml配置文件,如果设置不同的namespace,则id可重复,否则不可重复
5.MyBatis是如何进行分页的?分页插件的原理是什么?
- MyBatis使用RowBounds对象针对ResultSet结果集执行内存分页,也可以在Sql内直接书写带有物理分页的参数实现物理分页
- 分页插件实现MyBatis提供的插件接口,在插件的拦截方法里拦截并重写待执行的sql
6.MyBatis动态Sql是做什么的?其执行原理是什么?
- MyBatis允许在xml映射文件中以标签形式编写动态Sql,完成逻辑判断和动态拼接Sql的功能
- MyBatis提供 trim | where | set | foreach | if | choose | when | otherwise | bid 9种动态sql标签
- 执行原理为
1)使用OGNL从sql参数对象中计算表达式的值
2)根据表达式值动态拼接Sql
7.MyBatis是否支持延迟加载?其实现原理是什么?
- 延迟加载又称懒加载,用于处理N+1性能问题
- N+1问题指映射集合(resultMap)级联时,我们需要的数据少于数据库查出的数据,浪费性能且加重数据库负担
- MyBatis仅支持association关联对象(1对1)和collection关联集合(1对多)的延迟加载,配置文件中参数LazyLoadingEnabled=true|false设置是否开启延迟加载
- MyBatis通过动态代理实现延迟加载
参考:https://my.oschina.net/wenjinglian/blog/1857581?from=singlemessage
8.使用MyBatis的mapper接口调用时有哪些要求?
- Mapper接口方法名和mapper.xml定义的sql的id相同
- Mapper接口方法的输入参数类型和mapper.xml定义的sql的parameterType类型相同
- Mapper接口方法的输出参数类型和mapper.xml定义的sql的resultType类型相同
- Mapper.xml的namespace即是mapper接口的类路径
9.mapper有哪几种编写方式?
- 接口实现类继承SqlSessionDaoSupport类
- 使用org.mybatis.spring.mapper.MapperFactoryBean
- 使用mapper扫描器
10.mapper如何传递多个参数?
- xml使用#{0}接收dao层第一个参数,#{1}接收第二个参数,以此类推
- 使用@param注解