Mybatis的一些常见面试题
程序员文章站
2022-06-09 20:41:05
...
整理的Mybatis的一些面试题!
1. 什么是mybatis?
(1)mybatis是一个半自动的ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不
在需要花费精力去编写处理加载驱动、创建连接、创建statement等繁杂的过程了,我们只需要直接编写原生的sql
它可以严格的控制sql执行性能,灵活度高。
(2)Mbatis可以使用XML或注解的方式来配置和银蛇原生信息,将pojo映射成数据库中的记录,避免了几乎所有的JD
BC代码和手动设置参数以及获取结果集。
(3)通过XML文件或注解的方式将要执行的各种statement配置起来,并通过JAVA对象和statement中的SQL的动态参
数进行映射,生成最终执行的sql语句,最后用mybatis框架执行sql并将结果映射为JAVA对象并返回(从执行sql到
返回result的过程)
2.mybatis的优点?
(1)基于SQL语句编写,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在xml里,接触sql
与程序代码的耦合,便于统一管理,提供XML标签,支持编写动态SQL语句,并可重用。
(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量的冗余代码,不需要手动开关连接
(3)很好的与各种数据库兼容,(因为mybatis使用JDBC来连接的,所以说只要JDBC支持的数据库mybatis都支持)
(4)能够与spring很好的集成。
(5)提供映射标签,支持对象与数据库的ORM字段关系映射,提供对象关系映射标签,支持对象关系组件维护
3.mybatis框架的缺点?
(1)SQL语句的编写工作量大,尤其是字段多,关联多表是对开发人员编写SQl语句的工地有一定的要求。
(2)SQl语句依赖于数据库,导致数据库一致性差,不能随意更换数据库。
4.mybatis框架适用场合?
(1)mybatis专注于SQl本身,是一个足够灵活的DAO层解决方案。
(2)对性能的要求很高或者需求、变化比较多的项目,如果互联网项目,mybatis将是不错的选择。
5.mybatis与Hibernate有哪些不同?
(1)mybatis它不完全是一个ORM框架,因为Mybatis需要自己编写sql语句。
而hibernate是一个全自动的ORm框架,在sql编写的方面来说,没有mybatis灵活
(2)mybatis直接编写原生sql,可以严格控制sql执行能力,灵活度高,非常适合对关系数据模型要求不高的软件
开发,因为这类软件需求变化频繁,一旦需求变化 要求迅速输出结果,但是灵活的前提下mybatisis无法做到数据
库无关性,如果需要实现支持多种数据库的软件则需要自定义的多套sql映射文件,工作量大
(3)hibernate对象关系映射能力强,数据库无关性好,对关系模型要求高的软件,如果用hibernate开发可以节
省很多代码,提高效率
6.#{} 和${}的区别是什么?
#{}是预编译处理,${}是字符串替换。
mybatis中在处理#{}时会将sql中的#{}替换为? 占位符,调用preparedStatement的set方法来赋值。
mybatis在处理${}的时候,就是把${}直接替换为变量的值
使用#{}可以有效防止sql注入提高系统安全性
7.当实体类中的属性名和表中的字段明白褨怎么办?
1. 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性一致。
2. 通过映射字段名和实体类属性名一一对应的关系
<resultMap type="实体类" id="别名">
<id property="实体类属性名" column="数据库id字段"/>
<result property="实体类属性名" column="数据库字段名"/>
</resultMap>
8.mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
1.使用标签之一定义数据库列名和对象属性名之间的映射关系
2.使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,mybatis通过反射创建对象,同时使用反射给对象的属性之一赋值并返回,
那些找不到映射关系的属性是无法完成赋值的。
9.xml映射文件中,除了常见的select|insert|delete|标签外还有那些标签?
<resultMap></resultMap>
<parameterMap></parameterMap>
<sql></sql>
<include></include>
<selectkey></selectKey>
其中sql标签为sql片段标签,通过include标签引入sql片段的
selectKey标签为不支持自增的主键生成策略标签
加上动态标签的九个
if标签:你们能判断,我也能判断!
<select id="count" resultType="java.lang.Integer">
select count(*) from user where <if test="id != null">id = #{id}</if> and username = 'xiaoming';
</select>
如果传入的 id 不为空, 那么才会 SQL 才会拼接 id = #{id}
如果传入的 id 为 null,那么最终的 SQL 语句就变成了 select count(*) from user where and username = ‘xiaoming’。这语句就会有问题这时候
where 标签就该隆重登场了
where标签:有了我,SQL 语句拼接条件神马的都是浮云!
<select id="count" resultType="java.lang.Integer">
select count(*) from user
<where>
<if test="id != null">id = #{id}</if>
and username = 'xiaoming';
</where>
</select>
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。并且,若语句的开头
为 AND 或OR,where 元素也会将它们去除。还可以通过 trim 标签去自定义这种处理规则
trim标签:我的地盘,我做主!
trim 标签一般用于拼接、去除 SQL 的前缀、后缀
trim 标签中的属性
属性 描述
prefix 拼接前缀
suffix 拼接后缀
prefixOverrides 去除前缀
suffixOverrides 去除后缀
<select id="count" result="java.lang.Integer">
select count(*) from user
<trim prefix ="where" prefixOverrides="and |or">
<if test="id != null">id = #{id}</if>
<if test="username != null"> and username = #{username}</if>
</trim>
</select>
如果 id 或者 username 有一个不为空,则在语句前加入 where。如果 where 后面紧随 and 或 or 就会自动
会去除
如果 id 或者 username 都为空,则不拼接任何东西
set标签:信我,不出错!
<update id="UPDATE" parameterType="User">
update user
<set>
<if test="name != null">name = #{name},</if>
<if test="password != null">password = #{password},</if>
<if test="age != null">age = #{age},</if>
</set>
</update>
foreach标签:你有 for,我有 foreach
<foreach> 标签中的属性
属性 描述
index 下标
item 每个元素名称
open 该语句以什么开始
close 该语句以什么结尾
separator 在每次迭代之间以什么作为分隔符
collection 参数类型
choose标签:我选择了你,你选择了我!
<select id="count" resultType="Blog">
select count(*) from user
<choose>
<when test="id != null">
and id = #{id}
</when>
<when test="username != null">
and username = #{username}
</when>
<otherwise>
and age = 18
</otherwise>
</choose>
</select>
当 id 和 username 都不为空的时候, 那么选择二选一(前者优先)
如果都为空,那么就选择 otherwise 中的
如果 id 和 username 只有一个不为空,那么就选择不为空的那个
sql标签:相当于 Java 中的代码提重,需要配合 include 使用
<sql id="table"> user </sql>
include标签:相当于 Java 中的方法调用
<select id="count" resultType="java.lang.Integer">
select count(*) from <include refid=“table(sql 标签中的 id 值)” />
</select>
bind标签:对数据进行再加工
<select id="count" resultType="java.lang.Integer">
select count(*) from user
<where>
<if test="name != null">
<bind name="name" value="'%' + username + '%'"
name = #{name}
</if>
</select>
10.mybatis动态sql有什么用?执行原理?
mybatis动态sql可以在xml映射文件内,以标签的形式编写sql,执行原理是根据表达式的值,完成逻辑判断并动态
拼接sql的功能
11.使用mybatis的mapper接口调用时有哪些要求?
1.mapper接口方法名和xml定义的每个sql的id相同
2.mapper接口方法的输入参数类型和xml中定义的每个sql的parmeterType的类型相同。
3.接口方法输出的参数类型和xml中的sql的resultType的类型相同
4.xml文件中的namespace及实际mapper接口的路径。
12.mybatis是如何进行分页了?分页插件的原理是什么?
mybatis使用RowBounds对象进行分页的,他是针对ResultSet结果集执行的内存分页而非物理分页,可以在sql内
直接书写带有物理分页的参数来完成物理分页功能也可以使用分页插件来完成物理分页
分页插件的基本原理是使用mybatis提供的插件接口实现自定义插件,在插件的拦截方法内拦截执行sql,然后重写
sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
13.mybatis实现一对多有几种方式怎么操作
有两种方式 :联合查询和嵌套查询
联合查询:是几个表联合查询,只查询一次通过在resultMap里面的collection节点配置一对多的类就可
以完成
嵌套查询:是先查一个表,根据这个表里的结果的外键id再去另外一个表里边查询数据,也是通过配置
collection,但另外一个表的查询通过select节点配置
<!-- /一对多的第一种写法,一般考虑到性能问题,不会这么实现 -->
<resultMap type="dcc.domain.Teacher" id="teacherMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="students" ofType="dcc.domain.Student" column="id">
<id column="sid" property="id"/><!-- 这里的column对应的是下面查询的别名,而不是表字段名 -->
<result column="sname" property="name"/><!-- property对应JavaBean中的属性名 -->
<result column="className" property="className"/>
</collection>
</resultMap>
<!-- 查询所有的老师级各自的所有学生 -->
<select id="getTeachers" parameterType="dcc.domain.Teacher" resultMap="teacherMap">
SELECT
t.id,
t.NAME,
t.class_Name,
s.id AS sid,
s. NAME AS sname,
s.class_name as className
FROM
teacher t
LEFT JOIN student s ON t.id = s.teacher_id
</select>
<!-- //一对多的第二种写法/ -->
<resultMap type="dcc.domain.Teacher" id="teacherMaps">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="class_name" property="className"/>
<collection property="students" ofType="dcc.domain.Student" select="getStudents" column="{id=id,className=class_name}" >
</collection>
</resultMap>
<!-- 查询所有的老师级各自的所有学生 -->
<select id="getAllTeacher" parameterType="dcc.domain.Teacher" resultMap="teacherMaps">
SELECT
t.id,
t.NAME,
t.class_name
FROM
teacher t
</select>
<select id="getStudents" resultType="dcc.domain.Student">
select
s.id,
s. NAME,
s.class_name as className
from student s
where teacher_id = #{id}
and class_name = #{className}
14.mybatis是否支持延迟加载?如果支持他的实现原理是什么?
1.mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,
collection指的是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false
2.原理:
使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。比如
调用a.getb().getName(),拦截器方法invoke()发现a.getb()为null值,会单独发送事先保存好的查询关联
b对象的sql语句,把b查询上来然后调用a.setB(b),于是a的对象的属性b就有值了,然后接着
调用a.getb().getName(),这就是延迟加载的原理。
15.mybatis的一级、二级缓存?
一级缓存:
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个
SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper
方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询
的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而
不会再次发送SQL到数据库。
二级缓存:
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,
MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在
映射XML文件配置就可以开启缓存了<cache/>,如果我们配置了二级缓存就意味着:
映射语句文件中的所有select语句将会被缓存。
映射语句文件中的所欲insert、update和delete语句会刷新缓存。
缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,
不干扰其他 调用者或线程所做的潜在修改。
对于缓存数据更新机制,当某一个作用域(一级缓存session/二级缓存namespace)进行了C|U|D操作后,默
认该作用域下的所有select中的缓存将被clear掉并重新更新,如果开启二级缓存,则只根据配置判断是否clear。
开启二级缓存:
mybatis配置文件中添加
<srting name="cacheEnabled" value="true"/>
在mapper.xml文件中他年假
开启二级缓存
<cache/>
16.什么是mybatis的接口绑定?有哪些实现方式
接口绑定,就是在mybatis中任意定义接口,然后把接口里边的方法和sql语句绑定,我们直接调用接口方法就可以,
这样比起原来的sqlsession提供的方法,有了更加灵活的选择和设置
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@select、@update等注解,里边包含
sql语句来绑定,另外一种就是通过xml里边写的sql来绑定。在这种情况下要制定xml映射文件里边的namespace必
须为接口的全路径名,当sql语句比较简单的时候,用注解绑定,当sql语句比较复杂的时候,用xml来绑定,一般
用xml来绑定的比较多
17.使用mybatis的mapper.xml接口调用是有哪些要求?
1.mapper接口方法名和mapper.xml中定义的每一个sql的id相同。
2.mapper接口方法的输入参数类型和mapper.xml中定义的每一个sql的parameterType的类型相同