什么是mybatis
- mybatis框架是一个开源的数据持久性层框架
- 它的内部封装了通过JDBC访问数据库的操作,支持普通的SQL查询、存储过程和高级映射,几乎消除了所有的JDBC代码和参数的手工设置以及结果集的检索。
- MyBatis作为持久层框架,其主要思想是将程序中的大量SQL语句剥离出来,配置在配置文件当中,实现SQL的灵活配置。
- 这样做的好处是将SQL与程序代码分离,可以在不修改代码的情况下,直接在配置文件当中修改SQL。
什么是ORM
ORM(Object Relational Mapping),对象关系映射是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
MyBatis框架的优缺点及其适用的场景
优点:
- 与JDBC相比,减少了50%以上的代码量。
- MyBatis是易学的持久层框架,小巧并且简单易学。
- MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML文件里,从程序代码中彻底分离,降低耦合度,便于统一的管理和优化,并可重用。
- 提供XML标签,支持编写动态的SQL,满足不同的业务需求。
- 提供映射标签,支持对象与数据库的ORM字段关系映射。
缺点: - SQL语句的编写工作量较大,对开发人员编写SQL的能力有一定的要求。
- SQL语句依赖于数据库,导致数据库不具有好的移植性,不可以随便更换数据库。
mybatis工作原理
图解
执行流程
- 读取mybatis配置文件:mybatis-config.xml(加载运行环境和配置文件)
- 构造会话工厂SqlSessionFactory
- SqlSessionFactory创建SqlSession对象(包含了执行SQL语句的所有方法)
- Executor执行器(操作数据库的接口),同时负责查询缓存的维护
- Executor接口的执行方法中有一个MappedStatement类型的参数,里面封装了映射信息
- 输入参数映射
- 输出结果映射
mybatis是否支持延迟加载
什么是延迟加载
结论:mybatis是支持延迟加载的,但默认是不开启
在mybatis-config.xml开启全局延迟加载配置
<setting name="lazyLoadingEnabled" value="true"/>
延迟加载配置
#开启懒加载
mybatis.configuration.lazy-loading-enabled=true
在mybatis中,一对一关联的 association 和一对多的collection可以实现懒加载(fetchType属性)。resultMap可以实现高级映射,所以Mybatis在实现懒加载时要使用 resultMap,不能使用 resultType。
开启局部延迟加载配置
<collection fetchType="lazy"/>
<association fetchType="lazy"/>
延迟加载的原理
- 使用CGLIB创建目标对象的代理对象
- 当调用目标方法user.getOrderList方法时,进入拦截器invoke方法,若user.getOrderList方法为null时,执行sql查询order列表
- 查询到order列表,然后去调用user.setOrderList方法给orderList属性赋值,最后完成了user.getOrderList方法的调用
一二级缓存
一级缓存:基于PerpetualCache的HashMap本地缓存,作用域为Session。当Session进行flush或close后,该Session中的所有缓存将被清除。一级缓存默认是开启的。
二级缓存:也是基于PerpetualCache的HashMap本地缓存,作用域为namespace和mapper的作用域,不依赖于sql session。
二级缓存配置:默认是关闭的,需要在Mybatis的配置文件中配置
<settings>
<setting name="cacheEnabled" value="true"/> <!--Mybatis的二级缓存配置-->
</settings>
然后在mapper中添加cache标签
<mapper namespace="com.snow.xml.SnowOracle">
<cache></cache> <!--Mybatis的二级缓存配置-->
</mapper>
二级缓存注意事项
- 对于缓存数据更新机制,当某个作用域(一级缓存(session)、二级缓存(namespace)的数据进行了增删查操作后,默认该作用域下的所有select中的缓存将被清除)
- 二级缓存需要缓存的数据需要实现serializable接口
- 只有会话提交或会话关闭后,一级缓存中的数据才会转移到二级缓存中
Mybatis 中的一级缓存与二级缓存
# 和${}的区别
- #{}是预编译处理,${}是字符串替换
- 处理#{}时,会将sql中的#{}整体替换为占位符(即:?),调用PreparedStatement的set方法来赋值;
- 在处理 $ { } 时,就是把 ${ } 替换成变量的值。
#示例
#{ } 被解析成预编译语句,预编译之后可以直接执行,不需要重新编译sql。
//sqlMap 中如下的 sql 语句
select * from user where name = #{name};
//解析成为预编译语句;编译好SQL语句再取值
select * from user where name = ?;
$ {}示例
${ } 仅仅为一个字符串替换,每次执行sql之前需要进行编译,存在 sql 注入问题。
select * from user where name = '${name}'
//传递的参数为 "ruhua" 时,解析为如下,然后发送数据库服务器进行编译。取值以后再去编译SQL语句。
select * from user where name = "ruhua";
总结:使用 #{} 可以有效的防止SQL注入,提高系统安全性:
预编译是提前对SQL语句进行预编译,而后再调用SQL,注入的参数就不会再进行SQL编译。而SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译时SQL时轻而易举的通过,从而导致数据泄露。而预编译机制则可以很好的防止SQL注入。
更多的mybatis面试题