带你整理面试过程中关于 Mybatis 底层的相关知识
Posted 南淮北安
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你整理面试过程中关于 Mybatis 底层的相关知识相关的知识,希望对你有一定的参考价值。
文章目录
一、什么是 Mybatis ?
Mybatis是一个orm类型的半自动框架,执行了对JDBC的封装,是一个持久层框架,它可以通过XML文件或者注解来配置原生信息,不在需要去做更多繁琐重复的过程,如创建连接,加载驱动!
ORM 思想:
Object Relational Mappging 对象关系映射
简单的说:
就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表。
称Mybatis是半自动ORM映射工具,是因为在查询关联对象或关联集合对象时,需要手动编写sql来完成。不像Hibernate这种全自动ORM映射工具,Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取。
Hibernate:jpa 是一种规范,规范数据库的操作,底层需要 hibernate 实现
二、Mybatis 优缺点及使用场合
优点:
- 基于SQL语句编译,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
- 与JDBC相比,减少了50%的代码,消除了JDBC大量冗余的代码,不需要手动开关连接;
- 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)
- 能够与Spring很好的集成
- 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护
缺点:
- SQL语句的编写工作量较大,尤其字段多,关联表多时,对开发人员编写SQL语句的功底有一定要求!
- SQL语句依赖于数据库,导致数据库的移植性差,不能随意更换数据库。
适用场合:MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案,对性能要求很高,或者需求变化较多的项目,如互联网项目
三、#和$的区别是什么?
(1)$
表示拼接 sql 串,通过$
可以将语句传入的内容拼接在 sql 中
<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultType="mybatis.domain.User">
select * from user where username like '%$value%'
</select>
(2)Mybatis在处理#
时,会对sql语句进行预处理,将sql中的#
替换为?
号,调用PreparedStatement
的set
方法来赋值;
关键就是可以有效的防止SQL注入,提高系统的安全性
四、mappers 映射器对应的Dao接口的工作原理?
我们的 Dao 接口并没有实现类,调用它的时候,具体是怎么执行到我们的 SQL 语句的?
配置文件配置package 标签,会将对应包路径下的所有dao 类注册到 Spring 容器中,当我们通过注解注入这个 dao 接口时,返回的是一个 Dao 接口的代理对象,这个代理对象的处理器是 MapperProxy对象,所以通过@Autowired
注入Dao接口的时候,注入的就是这个代理对象,我们调用到Dao接口的方法时,则会调用到MapperProxy对象的invoke()
方法。
代理对象会拦截接口方法,根据类的全限定名+方法名
,唯一定位到一个 MapperStatement
并调用执行器执行所代表的sql,然后将sql执行结果返回。
Mapper 接口里的方法,不能重载,因为是使用 “全限名+方法名” 的保存和寻找策略
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace确定该方法是在哪个dao接口-->
<mapper namespace="mybatis.dao.UserDao">
<!--查询所有用户-->
<!--id 表示方法名称,resultType 表示要封装到哪里去-->
<select id="findAll" resultType="mybatis.domain.User">
select * from user
</select>
<!--保存用户-->
<insert id="saveUser" parameterType="mybatis.domain.User">
insert into user(username,birthday,sex,address)
values (#username,#birthday,#sex,#address)
</insert>
</mapper>
Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法内的参数,就是传递给sql的参数。
当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。在Mybatis中,每一个SQL标签,比如
<select>、<insert>、<update>、<delete>
标签,都会被解析为一个MapperStatement对象
五、Mybatis 的一级和二级缓存
缓存:
将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。
(1)一级缓存:SqlSession 级别的缓存
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存。
一级缓存在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。
也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。
(2)二级缓存:mapper 级别的缓存
MyBatis 的二级缓存是 mapper 级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。多个 SqlSession 去操作同一个 Mapper 的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存里存的只是数据,不是对象,所以每次读取时,值的地址并不同,谁来给的是内容,相同的内容复制到了新地址中。
(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。
六、Mybatis 的 Mapper 接口调用要求
(1)Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同
(2) Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
(3)Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同
(4) Mapper.xml 文件中的 namespace 即是 mapper 接口的类路径
<!--namespace确定该方法是在哪个dao接口-->
<mapper namespace="mybatis.dao.UserDao">
<!--根据id查询-->
<select id="findById" parameterType="int" resultType="mybatis.domain.User">
select * from where id = #uid;
</select>
</mapper>
七、resultMap 和 resultType
(1)resultType
查询出来的列名和实体类中的属性名一致,该列才可以映射成功。(数据库,实体,查询字段,这些全部都得一一对应)
(2)resultMap
当建立查询的列名和实体类的属性名称不一致时可以使用 resultMap 标签可以建立对应关系,从而实现封装
同时 resultMap 可以实现将查询结果映射为复杂类型的实体类,比如在查询结果映射对象中包括实体类 和 list 实现一对一查询和一对多查询
<resultMap id="userMap" type="mybatis.domain.UserTest">
<!--主键字段的对应-->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="name"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
八、Mybatis 实现批量插入
配置useGeneratedKeys
和keyProperty
就可以批量插入并返回主键了
<insert id="batchInsertCameras" useGeneratedKeys="true" keyProperty="cameraNo">
insert into camera (chanIndex,cameraName)
values
<foreach collection="list" item="c" separator=",">
(#c.chanIndex,#c.cameraName)
</foreach>
</insert>
具体可参考:mybatis批量插入并返回主键
以上是关于带你整理面试过程中关于 Mybatis 底层的相关知识的主要内容,如果未能解决你的问题,请参考以下文章
带你整理面试过程中关于Redis 中的字典及 rehash的相关知识点