MyBatis面试题(史上最全+不断升级)
Posted 架构师-尼恩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis面试题(史上最全+不断升级)相关的知识,希望对你有一定的参考价值。
文章很长,建议收藏起来慢慢读! 总目录 博客园版 为您奉上珍贵的学习资源 :
-
免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
-
免费赠送 经典图书:《Java高并发核心编程(卷1)》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
-
免费赠送 经典图书:《Java高并发核心编程(卷2)》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
-
免费赠送 经典图书:《Netty Zookeeper Redis 高并发实战》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
-
免费赠送 经典图书:《SpringCloud Nginx高并发核心编程》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
-
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
推荐:尼恩Java面试宝典(史上最全 + 不断升级)详请点击此链接
尼恩Java面试宝典,35个最新pdf,含2000多页,不断更新、持续迭代 具体详情,请点击此链接
MyBatis简介
聊聊:MyBatis是什么?
Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语
句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直
接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了
几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
聊聊:Mybatis优缺点
优点
与传统的数据库访问技术相比,ORM有以下优点:
- 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在
XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并
可重用
- 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库
MyBatis都支持)
- 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系
组件维护
- 能够与Spring很好的集成
缺点
- SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定
要求
- SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
聊聊:为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
-
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
-
而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动 ORM映射工具。
聊聊:传统JDBC开发存在什么问题?
-
频繁创建数据库连接对象、释放,容易造成系统资源浪费,影响系统性能。
可以使用连接池解决这 个问题。但是使用jdbc需要自己实现连接池。
-
sql语句定义、参数设置、结果集处理存在硬编码。
实际项目中sql语句变化的可能性较大,一旦发 生变化,需要修改java代码,系统需要重新编译,重新发布。不好维护。
-
使用preparedStatement向占有位符号传参数存在硬编码,
因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
-
结果集处理存在重复代码,处理麻烦。
如果可以映射成Java对象会比较方便。
聊聊:MyBatis 的好处是什么?
答:
1)MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的 维护带来了很大便利。
2)MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象, 大大简化了 Java 数据库编程的重复工作。
3)因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活 控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复 杂查询。
基础:MyBatis之XML映射文件详解
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。
由于它的异常强大,映射器的 XML 文件就显得相对简单。
在MyBatis开发中,涉及到主要开发要素是:Dao接口类,Mapper映射文件,以及PO类。
它们之间的关系如下:
一个Mapper映射文件的例子
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mb.dao.StudentMapper"> <!-- 配置方式一:通过结果集映射的方式进行查询 --> <select id="getStudentList" resultMap="StudentTeacherMap"> select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id; </select> <resultMap id="StudentTeacherMap" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <!-- association表示关联对象:javaType表示关联对象的java类型, 因为关联对象只有一个,而不是集合,所以直接配置关联对象对应属性即可--> <association property="teacher" javaType="Teacher"> <!-- 因为结果集中没有teacher的id,所以这里就不用配置id了 --> <result property="name" column="tname"/> </association> </resultMap> <!-- 配置方式二:通过子查询的方式进行查询,这种方式的缺点是,因为每个查询都要单独配置,所以不能直接使用完整的SQL去调试 --> <select id="getStudentList2" resultMap="StudentTeacherMap2"> select * from student; </select> <select id="getTeacherById" resultType="Teacher"> select * from teacher where id = #tid; </select> <resultMap id="StudentTeacherMap2" type="Student"> <result property="id" column="id"/> <result property="name" column="id"/> <!-- association表示关联对象: column表示要传入子查询的字段,javaType表示关联对象的java类型, select表示子查询,这里表示将查询到的column="tid"传入子查询select="getTeacherById"作为参数#tid的值进行查询 --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacherById"/> </resultMap> </mapper>
Mapper映射文件的顶级元素
映射器(mapper)的XML文件,有几个顶级元素:
- select – 映射查询语句
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- sql – 可被其他语句引用的可重用语句块。
- cache – 给定命名空间的缓存配置。
- cache-ref – 其他命名空间缓存配置的引用。
- resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
1、select元素
1. 最基本的查询
<select id="getUserById" resultType="MemberUser" parameterType="int"> select ID,NAME,PERSONMOBILE,ADDRESS,AGE FROM MEMBER_USER WHERE ID = #id </select>
上述配置类似于:
// Similar JDBC code, NOT MyBatis… String selectMember = "select ID,NAME,PERSONMOBILE,ADDRESS,AGE FROM MEMBER_USER WHERE ID=?"; PreparedStatement ps = conn.prepareStatement(selectMember); ps.setInt(1,id);
2. select 元素有很多属性允许你配置,来决定每条语句的作用细节
示范:
<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10000" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
详细说明:
属性 描述 id 在命名空间中唯一的标识符,可以被用来引用这条语句。 parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。 resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。 flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。 useCache 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。 timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 fetchSize 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。 statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。 databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。 2、insert、update、delete元素
1. 数据变更语句 insert,update 和 delete 的实现非常接近,参考如下配置:
<!--Oracle的实现自增长主键的方式--> <insert id="insertUser" parameterType="MemberUser"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select SEQ_MEMBER_USER.nextval from DUAL </selectKey> INSERT INTO MEMBER_USER (ID, NAME, PERSONMOBILE, ADDRESS, AGE) VALUES(#id, #name, #personMobile, #address, #age) </insert> <update id="updateUser" parameterType="MemberUser"> update MEMBER_USER set NAME = #name, PERSONMOBILE = #personMobile, ADDRESS = #address, AGE = #age where id = #id </update> <delete id="deleteUser" parameterType="int"> delete from MEMBER_USER where ID = #id </delete>
2 .Insert, Update 和 Delete 的属性:
属性 描述 id 命名空间中的唯一标识符,可被用来代表这条语句。 parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。 timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 mysql 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 3. 关于insert元素
如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了。示范:
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username,password,email,bio) values (#username,#password,#email,#bio) </insert>
如果是Oracle数据库,则用上面示范代码即可。selectKey 元素描述如下:
<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
selectKey 的属性:
属性 描述 keyProperty selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 keyColumn 匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 resultType 结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。 order 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。 statementType 与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。 3、sql元素
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。
1. 简单示范:
<!-- 用来定义可重用的SQL代码段 --> <sql id="selectProdSQL"> PRODID,PRODSERIAL,PRODNAME,CATEGORYNAME,PRODSPEC,PRODPRICE,PRODDESC,PRODIMAGE,ISNEW,ISRECOMMEND,ISSHOW,USERNAME,HANDLETIME,PRODTHUMBNAIL </sql> <!--以单个对象方式返回--> <select id="getProductById" resultType="Product" parameterType="int"> select <include refid="selectProdSQL"/> FROM PRODUCT WHERE PRODID = #prodId </select>
2. 进阶用法:
<sql id="userColumns"> $alias.id,$alias.username,$alias.password </sql>
这个 SQL 片段可以被包含在其他语句中,例如:
<select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2 </select>
4、resultMap元素
ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们 的关系。resultmap构成元素:
1. id:
>> 一般对应数据库中改行的主键ID,设置此项可以提高Mybatis的性能
2. result
>> 映射到JavaBean的某个“简单类型”属性
3. association
>> 映射到javaBean的某个“复杂类型”属性,比如:javabean类
4. collection
>> 映射到javabean的某个“复杂类型”属性,比如:集合
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性--> <resultMap id="唯一的标识" type="映射的pojo对象"> <id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" /> <result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/> <association property="pojo的一个对象属性" javaType="pojo关联的pojo对象"> <id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/> <result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/> </association> <!-- 集合中的property须为oftype定义的pojo对象的属性--> <collection property="pojo的集合属性" ofType="集合中的pojo对象"> <id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" /> <result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" /> </collection> </resultMap>
正常情况,它用来作为将数据库字段与PO类的字段进行映射,比如:
<resultMap id="BaseResultMap" type="com.meikai.shop.entity.TShopSku"> <id column="ID" jdbcType="BIGINT" property="id" /> <result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" /> <result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" /> </resultMap>
基础:OGNL 表达式(Object-Graph Navigation Language 对象-图形导航语言)
OGNL是Object-Graph Navigation Language(对象图导航语言)的缩写,
它是一种功能强大的表达式语言。strusts/MyBatis大量使用了 OGNL表达式。
OGNL 表达式的作用:可以
存取对象的属性
和调用对象的方法,通过OGNL 表达式可以迭代获取对象的结构图MyBatis中使用了OGNL,MyBatis中可以使用OGNL的地方有两处:
- 动态SQL表达式中
- $param参数中
上面这两处地方在MyBatis中处理的时候都是使用OGNL处理的。
MyBatis常用OGNL表达式
e1 or e2
e1 and e2
e1 == e2
,e1 eq e2
e1 != e2
,e1 neq e2
e1 lt e2
:小于e1 lte e2
:小于等于,其他gt(大于),gte(大于等于)e1 in e2
e1 not in e2
e1 + e2
,e1 * e2
,e1/e2
,e1 - e2
,e1%e2
!e
,not e
:非,求反e.method(args)
调用对象方法e.property
对象属性值e1[ e2 ]
按索引取值,List
,数组和Map
@class@method(args)
调用类的静态方法@class@field
调用类的静态字段值
上述内容只是合适在MyBatis中使用的OGNL表达式,完整的表达式点击这里。
1、语法:#
# : 是指上下文(环境)对象
OGNL 表达式获取属性举例:
对象person:id:10,age:18,name:小明;
若上下文(环境)的对象是person,通过# 可以直接获取到对象的属性值
#id 相当于 perosn.getId() #age 相当于 person.getAge() #name 相当于 person.getName() 1234
OGNL 在mybatis框架中的应用:
/* User 类*/ @Data public class User private Long id; private String name; private BigDecimal salary; /* 测试类 */ User user = new User(); user.setId(2L); SqlSession session = factory.openSession(); //4、进行数据库操作(CRUD) User user = session.selectOne("com.shan.hello.UserMapper.get", user);//将user作为上下文对象(javaBean类型)传入 /* 映射文件 */ <select id="get" parameterType="java.lang.Long" resultType="com.shan.hello.User"> select * from t_user where id = #id //上下文是javaBean类型,OGNL 表达式格式必须为 #属性名 </select> 12345678910111213141516171819
OGNL 的上下文对象类型【获取属性值】:
(1)javaBean对象,例如上面的User,则 OGNL 表达式格式必须为 #属性名
(2)map 对象,OGNL 表达式格式为 #key
(3)简单类型对象(基本类型、String类型),OGNL 表达式格式为 #随便写,不过一般写得见名知意,增加代码的阅读性
简单类型举例子:
/* 测试类 */ SqlSession session = factory.openSession(); //4、进行数据库操作(CRUD) User user = session.selectOne("com.shan.hello.UserMapper.get", 2L);//将2L作为上下文对象(简单类型)传入 /* 映射文件 */ <select id="get" parameterType="java.lang.Long" resultType="com.shan.hello.User"> select * from t_user where id = #id//上下文是javaBean类型,OGNL 表达式格式可以写成#aa,不过增强代码阅读性也会写成#id </select> 123456789
EL 表达式 $
1、语法:$属性名
2、OGNL 表达式的作用:通过 $属性名 直接获取属性值,属性的内容。
在MyBatis中,OGNL 表达式 # 和 EL 表达式 $ 的异同
1、sql语句分别使用# 和 $的实际情况:
(1)使用 #:
<!-- 映射文件 ---> <select id="login" resultType="Client"> select id, username, password from client where username = #username and password = #password ; </select> <!-- # 的实际作用:现在转成?的占位符,然后再把值设置进去【假设外界传入的值username="小明",password="1"】 --> select id, username, password from client where username = ? and password = ?; PreparedStatement.setString(1, "小明"); PreparedStatement.setString(2,"1");
(2)使用 $:
<!-- 映射文件 ---> <select id="login" resultType="Client"> select id, username, password from client where username = $username and password = $password ; </select> <!-- $ 的实际作用:直接把值设置进去【假设外界传入的值username="小明",password="1"】 --> select id, username, password from client where username = "小明" and password = "1";
2、# 和 $ 的异同:
-
相同:都可以获取对象的信息。
-
不同:
使用# 传递的参数,会先转成占位符?,再通过设置占位符参数的方式设置值【会给值用单引号引起来】,不会导致sql注入问题,比较安全。
使用$ 传递的参数,直接把解析出来的数据作为sql语句的一部分。可能会出现sql注入安全问题,比较不安全。
聊聊:和$的区别
-
是占位符,预编译处理;$是拼接符,字符串替换,没有预编译处理。
-
Mybatis在处理#时,#传入参数是以字符串传入,会将SQL中的#替换为?号,调用 PreparedStatement的set方法来赋值。
-
# 可以有效的防止SQL注入,提高系统安全性;$ 不能防止SQL 注入
-
# 的变量替换是在DBMS 中;$ 的变量替换是在 DBMS 外
聊聊:#和$的区别是什么?
答:
1)#是预编译处理,$是字符串替换。
2)Mybatis 在处理#时,会将 sql 中的#替换为?号,调用 PreparedStatement 的 set 方法来赋值;
3)Mybatis 在处理 时,就是把 时,就是把 时,就是把替换成变量的值。
4)使用#可以有效的防止 SQL 注入,提高系统安全性。
聊聊:模糊查询like语句该怎么写
1 ’%$question%’ 可能引起SQL注入,不推荐
2 “%”#question“%” 注意:
因为#…解析成sql语句时候,会在变量外侧自动加单引号’ ',所 以 这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果。
3 CONCAT(’%’,#question,’%’) 使用CONCAT()函数,(推荐)
4 使用bind标签(不推荐)
<select id="listUserLikeUsername" resultType="com.jourwon.pojo.User"> <bind name="pattern" value="'%' + username + '%'" /> select id,sex,age,username,password from person where username LIKE pattern </select>
聊聊:在mapper中如何传递多个参数
方法1:顺序传参法
public User selectUser(String name, int deptId); <select id="selectUser" resultMap="UserResultMap"> select * from user where user_name = #0 and dept_id = #1 </select>
-
#里面的数字代表传入参数的顺序。
-
这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
方法2:@Param注解传参法
public User selectUser(@Param("userName") String name, int @Param("deptId") deptId); <select id="selectUser" resultMap="UserResultMap"> select * from user where user_name = #userName and dept_id = #deptId </select>
-
#里面的名称对应的是注解@Param括号里面修饰的名称。
-
这种方法在参数不多的情况还是比较直观的,(推荐使用)。
方法3:Map传参法
public User selectUser(Map<String, Object> params); <select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap"> select * from user where user_name = #userName and dept_id = #deptId </select>
-
#里面的名称对应的是Map里面的key名称。
-
这种方法适合传递多个参数,且参数易变能灵活传递的情况。(推荐使用)。
方法4:Java Bean传参法
public User selectUser(User user); <select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap"> select * from user where user_name = #userName and dept_id = #deptId </select>
-
#里面的名称对应的是User类里面的成员属性。
-
这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方 便,推荐使用。(推荐使用)。
聊聊:Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪 些映射形式?
-
第一种是使用 标签,逐一定义列名和对象属性名之间的映射关系。
-
第二种是使用sql列的别名功能,将列别名书写为对象属性名
比如T_NAME AS NAME,对象属性 名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那 些找不到映射关系的属性,是无法完成赋值的
聊聊:Xml映射文件中,除了常见的select|insert|updae|delete标 签之外,还有哪些标签?
还有很多其他的标签,
、 (被弃用) 、 、 、 ,
加上动态sql的9个标签
trim|where|set|foreach|if|choose|when|otherwise|bind等,
其中 为sql片段标签,通 过 标签引入sql片段, 为不支持自增的主键生成策略标签
聊聊:Mybatis 是如何将 sql 执行结果封装为目标对象并返回的? 都有哪些映射形式?
第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。
第二 种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是 name,小写,但是列名不区分大小写,Mybatis 会忽略列名大小 写,智能找到与之对应对象属性名,
你甚至可以写成 T_NAME AS NaMe,Mybatis 一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象 的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
聊聊:Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理吗?
-
Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态 拼接sql的功能,
Mybatis提供了9种动态sql标签 trim|where|set|foreach|if|choose|when|otherwise|bind。
-
其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此 来完成动态sql的功能。
聊聊: 一级、二级缓存
1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将 清空。
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存 储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如Ehcache。
要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
3 ) 对 于 缓 存 数 据 更 新 机 制 , 当 某 一 个 作 用 域 ( 一 级 缓 存 Session/ 二 级 缓 存 Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将 被 clear。
聊聊:Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,
’association 指的就是一对一,collection 指的就是一对多查询。
在 Mybatis 配置文 件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截 器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是 null 值,
那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调 用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的 调用。
这就是延迟加载的基本原理。
聊聊:Mybatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面?
虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。
原理是,Mybatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到, 尚不存在,
此时,Mybatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签, 包含 B 标签,待所有标签解析完毕,Mybatis 会重新解析那些被标记为未解析的标 签,
此时再解析 A 标签时,B 标签已经存在,A 标签也就可以正常解析完成了。
聊聊: Mybatis 的 Xml 映射文件和 Mybatis 内部数据结构之间的映射关系?
Mybatis 将所有 Xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内 部。
在 Xml 映射文件中,标签会被解析为 ParameterMap 对象,
其每个子元素会被解析为 ParameterMapping 对象。标签会被解析为 ResultMap 对象,其每个子元素会被解析为 ResultMapping 对象。
每一个、 、、标签均会被解析为 MappedStatement 对象,
标签 内的 sql 会被解析为 BoundSql 对象。
聊聊:接口绑定有几种实现方式,分别是怎么实现的?
答:接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上 @Select@Update 等注解里面包含 Sql 语句来绑定,另外一种就是通过 xml 里面写 SQL 来绑 定,在这种情况下,要指定 xml 映射文件里面的 namespace 必须为接口的全路径名.
聊聊:什么情况下用注解绑定,什么情况下用 xml 绑定?
答:当 Sql 语句比较简单时候,用注解绑定;当 SQL 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多
聊聊:MyBatis 实现一对一有几种方式?具体怎么操作的?
答:有联合查询和嵌套查询,
联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面 配置 association 节点配置一对一的类就可以完成;
嵌套查询是先查一个表,根据这个表里面 的结果的外键 id,去再另外一个表里面查询数据,也是通过 association 配置,但另外一个表的 查询通过 select 属性配置。
聊聊:MyBatis 里面的动态 Sql 是怎么设定的?用什么语法?
答:
MyBatis 里面的动态 Sql 一般是通过 if 节点来实现,
通过 OGNL 语法来实现,但是如果要 写的完整,必须配合 where,trim 节点,where 节点是判断包含节点有内容就插入 where,
否则不 插入,trim 节点是用来判断如果动态语句是以 and 或 or 开始,那么会自动把这个 and 或者 or 取掉。
聊聊:Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
答:
第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。
第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对 象属性名一般是 name,小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,Mybatis 一样可以正常工 作。
有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
聊聊:当实体类中的属性名和表中的字段名不一样,如果将查询的结果封装到指定 pojo?
答:
1)通过在查询的 sql 语句中定义字段名的别名。
2)通过来映射字段名和实体类属性名的一一对应的关系。
聊聊:通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, Dao 的工作原理,是否可以重
载?
答:不能重载,因为通过 Dao 寻找 Xml 对应的 sql 的时候全限名+方法名的保存和寻找策
略。接口工作原理为 jdk 动态代理原理,运行时会为 dao 生成 proxy,代理对象会拦截接口
方法,去执行对应的 sql 返回数据。
聊聊:Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
答:
不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;
如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。
原因就 是 namespace+id 是作为 Map<String, MappedStatement>的 key 使用的,
如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。
有了 namespace,自然 id 就 可以重复,namespace 不同,namespace+id 自然也就不同。
聊聊:Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么?
答:
Mybatis 有三种基本的 Executor 执行器,SimpleExecutor、ReuseExecutor、 BatchExecutor。
1)SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对 象,用完立刻关闭 Statement 对象。
2)ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象, 而是放置于 Map
3)BatchExecutor:完成批处理。
聊聊:Mybatis 中如何指定使用哪一种 Executor 执行器?
答:
在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。
聊聊:Mybatis 执行批量插入,能返回数据库主键列表吗?
答:能,JDBC 都能,Mybatis 当然也能。
聊聊:如何获取自动生成的(主)键值?
答:配置文件设置 usegeneratedkeys 为 true
聊聊:Mybatis 是如何进行分页的?分页插件的原理是什么?
答:
1)Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用 Mybatis 的分页插件。
2)分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦 截待执行的 sql,然后重写 sql。
举例:
select * from student,
拦截 sql 后重写为:
select t.* from (select * from student)t limit 0,10
聊聊:Mybatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
答:
1)Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑 判断和动态拼接 sql 的功能。
2)Mybatis 提供了 9 种动态 sql 标签:
trim|where|set|foreach|if|choose|when|otherwise|bind。
3)其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼 接 sql,以此来完成动态 sql 的功能。
聊聊:MyBatis 的好处是什么?
答:
1)MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的 维护带来了很大便利。
2)MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象, 大大简化了 Java 数据库编程的重复工作。
3)因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活 控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复 杂查询。
聊聊:什么情况下用注解绑定,什么情况下用 xml 绑定?
答:当 Sql 语句比较简单时候,用注解绑定;当 SQL 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多
聊聊:MyBatis 里面的动态 Sql 是怎么设定的?用什么语法?
答:
MyBatis 里面的动态 Sql 一般是通过 if 节点来实现,通过 OGNL 语法来实现,
但是如果要 写的完整,必须配合 where,trim 节点,
where 节点是判断包含节点有内容就插入 where,否则不 插入,
trim 节点是用来判断如果动态语句是以 and 或 or 开始,那么会自动把这个 and 或者 or 取掉。
聊聊:Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
答:
不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;
如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。
原因就 是 namespace+id 是作为 Map<String, MappedStatement>的 key 使用的,
如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。
有了 namespace,自然 id 就 可以重复,namespace 不同,namespace+id 自然也就不同。
聊聊:resultType resultMap 的区别?
答:
1)类的名字和数据库相同时,可以直接设置 resultType 参数为 Pojo 类
2)若不同,需要设置 resultMap 将结果名字和 Pojo 名字进行转换
聊聊:使用 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 接口的类路径。
参考文献
https://www.cnblogs.com/nayitian/p/15075090.html
https://blog.csdn.net/weixin_45630258/article/details/122784096
https://www.cnblogs.com/wolf-lifeng/p/11153569.html
https://www.w3cschool.cn/mybatis/f4uw1ilx.html
https://www.cnblogs.com/kenhome/p/7764398.html
https://www.cnblogs.com/Charles-Yuan/p/9900279.html
以上是关于MyBatis面试题(史上最全+不断升级)的主要内容,如果未能解决你的问题,请参考以下文章