【java面试题-Mybatis篇-2024】

##java面试题大全

详细面试题-持续更新中-点击跳转

点赞、收藏、加关注
·
·

Mybatis面试题

1、什么是 Mybatis

  • Mybatis 是一个 半ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高
  • MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
  • 通过 XML 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 Mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返回 result 的过程)

2、Mybaits 的优缺点

优点:

  • 简单易学,容易上手(相比较于 Hibernate)
  • 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用
  • 与 JDBC 相比,减少了50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接
  • 很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)
  • 能够与Spring很好的集成
  • 提供了许多第三方插件。(分页插件,逆向工程)
  • 提供映射标签,支持对象与数据库的 orm 字段关系映射

缺点:

  • SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求
  • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

3、MyBatis 框架适用场合

  • MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案
  • 对性能的要求很高,或者需求变化较多的项目,如互联网项目

4、Hibernate 与 MyBatis 有哪些不同

  • Hibernate 是全自动 ORM 框架;Mybatis 是半自动
  • Hibernate 数据库移植性远大于 Mybatis
  • Hibernate 拥有完整的日志系统;Mybatis则欠缺一些
  • sql 直接优化上,Mybatis 要比 Hibernate 方便很多;sql的灵活度上,Mybatis 也要比 Hibernate 强很多

5、#{} 和 ${} 的区别是什么

  • #{} 是预编译处理;${} 是字符串替换
  • Mybatis 在处理 #{} 时,会将 sql 中的 #{} 替换为 ? 号,调用 PreparedStatement 的 set 方法来赋值;Mybatis在处理 ${} 时,是把 ${} 替换成变量的值
  • 使用 #{} 可以有效的防止 SQL 注入,提高系统安全性;${} 可能出现 SQL 注入问题
  • #{} 会给参数内容自动加上引号;${} 不会给参数内容加上引号

6、当实体类中的属性名和表中的字段名不一样,怎么办

  1. 通过在查询的 sql 语句中定义字段名的别名,让字段名的别名和实体类
    的属性名一致
<select id="selectorder" parametertype="int" resultetype="Order">
	select 
		order_id id, order_no orderno ,order_price price 
	form
		orders
	where 
		order_id=#{id};
</select>
  1. 通过 < resultMap > 来映射字段名和实体类属性名的一一对应的关系
<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>
 
<resultMap id="orderresultmap" type="Order">
    <!–  id 属性来映射主键字段–>
    <id property=”id” column=”order_id”>
    <!–  result 属性来映射非主键字段,property 为实体类属性名,column 为数据表中的属性–>
    <result property = “orderno” column =”order_no”/>
    <result property=”price” column=”order_price” />
</reslutMap>

7、resultType 和 resultMap 的区别是什么

resultType:
如果数据库结果集中的列名和要封装实体的属性名完全一致的话,用 resultType 属性

resultMap:
如果数据库结果集中的列名和要封装实体的属性名有不一致的情况用 resultMap 属性;且通过 resultMap 的 association 标签可以实现一对一的关系,通过 collection 标签可以实现一对多的关系

8、模糊查询 like 语句该怎么写

  1. 直接传参法
 	<!– keyword 在方法中进行 % 的拼接,传递过来的已是 %keyword% 形式 >
	<select id="getList" resultetype="Test">
		SELECT * FROM my_table
		keyword like #{keyword}	
	</select>
  1. CONCAT(‘%’,#{keyword},‘%’)(推荐)
	<select id="getList" resultetype="Test">
		SELECT * FROM my_table
		keyword like CONCAT('%',#{keyword},'%')
	</select>
  1. ‘%’ || #{name} || ‘%’
	<select id="getList" resultetype="Test">
		SELECT * FROM my_table
		keyword like '%' || #{name} || '%'
	</select>
  1. 使用 bind 标签
	<select id="getList" resultetype="Test">
		<bind name="_keywork" value="'%' + keywork + '%'" />
		SELECT * FROM my_table
		keyword like #{_keywork}
	</select>
  1. ‘%${question}%’(不推荐)
	<select id="getList" resultetype="Test">
		SELECT * FROM my_table
		keyword like '%${question}%'				
	</select>

9、Xml 映射文件,会写一个 Dao 接口与之对应,这个 Dao 接口的工作原理是什么?Dao 接口里的方法, 参数不同时,方法能重载吗

工作原理:
Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行接口方法所对应的 MappedStatement 所代表的 sql,然后将 sql 执行结果返回。Dao 接口就是常说的 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值,接口的方法名就是映射文件中 MappedStatement 的 id 值,接口方法内的参数就是传递给 sql 的参数

是否可重载:
接口里的方法是不能重载的,因为是全限名+方法名的保存和寻找策略

MappedStatement解释:
MappedStatement 维护了一条 < select | update | delete | insert > 节点的封装,包括了传入参数映射配置、执行的SQL语句、结果映射配置等信息

10、MyBatis 是如何进行分页的?分页插件的原理是什么

进行分页:
MyBatis 是使用分页插件 PageHelper 进行分页的,使用方式为:在将要执行的 sql 语句之前使用分页插件:pageHelper.starPage(pageNum,pageSize);

原理:
首先分页参数放到 ThreadLocal 中。拦截器 拦截执行的 sql,根据数据库类型添加对应的分页语句重写 sql,例如:(select * from table where a) 转换为 (select count(*) from table where a) 和 (select * from table where a limit , ) 计算出了 total 总条数、pageNum 当前第几页、pageSize 每页大小和当前页的数据,是否为首页,是否为尾页,总页数等

11、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式

第一问:
当数据库列名与对象属性名的映射后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回

第二问:
映射是将数据库列名与对象属性名一一对应

  1. < select > 标签使用 resultType 参数,传递 Java 类,sql 中 select 的字段名保持与 Java 类属性名称一致,不一致时,可以采用取别名的方式
  2. 使用 < resultMap > 标签,定义数据库列名和对象属性名之间的映射关系
  3. 在接口方法上使用 @select 注解,语句中的字段名和接口方法返回的 Java 类或集合的元素类的属性名称一致

12、Mybatis如何执行批量操作

使用 foreach 标签:
foreach 可以在 SQL 语句中进行迭代一个集合。foreach 标签的属性有 collection,item,index,open,separator,close

  • collection:接口上传过来的 list 集合或者 map 集合等
  • item:表示集合中每一个元素进行迭代时的别名,随便起的变量名
  • index:每次迭代到的位置,不常用
  • open:表示该语句以什么开始
  • separator:表示在每次进行迭代之间以什么符号作为分隔符
  • close:表示该语句以什么结束
<!-- MySQL下批量保存,foreach 遍历 mysql支持values(),(),()语法,推荐使用 --> 
<insert id="addEmpsBatch">
    INSERT INTO emp(ename,gender,email,did)
    VALUES
    <foreach collection="emps" item="emp" separator=",">
        (#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>
<!-- 这种方式需要数据库连接属性 allowMutiQueries=true 的支持
 如jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true -->  
<insert id="addEmpsBatch">
    <foreach collection="emps" item="emp" separator=";">                                 
        INSERT INTO emp(ename,gender,email,did)
        VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>

使用ExecutorType.BATCH:
Mybatis 内置的 ExecutorType 有3种,默认为 simple,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交 sql;而 batch 模式重复使用已经预处理的语句,并且批量执行所有更新语句;但batch 模式也有自己的问题,比如在 Insert 操作时,在事务没有提交之前,是没有办法获取到自增的 id,在某些情况下不符合业务的需求

//批量保存方法测试
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    for (int i = 0; i < 10000; i++) {
        mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "a","1"));
    }
    openSession.commit();
} finally {
    openSession.close();
}
<mapper namespace="com.jourwon.mapper.EmployeeMapper"
     <!--批量保存员工 -->
    <insert id="addEmp">
        insert into employee(lastName,email,gender)
        values(#{lastName},#{email},#{gender})
    </insert>
</mapper>

如何获取自动生成的主键值

在映射文件的 insert 配置标签上加上两个属性:

  • useGeneratedKeys=“true”:开启添加记录后返回主键值
  • keyProperty=“属性名”:设置返回的主键值放置到 javaBean 的指定属性中

13、在 mapper 中如何传递多个参数

直接传递:

test(Integer id,String name);
  • 在 xml 中,参数1对应 #{param1},参数2对应 #{param2}
  • 或者,参数1对应索引 #{arg0},参数2对应索引 #{arg1}

使用 @param 注解:

test(@param("id") Integer id,@param("name") String name);
  • 实现接口方法里的参数名和 xml 文件内接收参数名对应一致
  • #{id} 和 #{name}

传递 POJO 对象:

test(User user);
  • 在 xml 文件里直接用 #{POJO的属性名称} 的形式来获取传过来的值

传递 Map 对象:

test(Map<String,Object> paramMap);
  • 在 xml 文件里获取参数值就用 #{Map的key值}

14、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql

作用:
动态 sql 就是在进行 sql 操作的时候动态的根据所匹配的条件来拼接数据库执行的 sql 语句。Mybatis 动态 sql 可以在 Xml 映射文件内,以标签的形式编写动态 sql 语句

执行原理:
根据表达式的值完成逻辑判断并动态拼接 sql

9种动态 sql 标签:
trim | where | set | foreach | if | choose| when | otherwise | bind

15、Xml映射文件中,除了常见的 select | insert | updae | delete 标签之外,还有哪些标签

  1. < cache />:给定命名空间的缓存配置
  2. < cache-ref />:其他命名空间缓存配置的引用
  3. < resultMap />:是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象
  4. < parameterMap />:已废弃!老式风格的参数映射
  5. < sql />:可被其他语句引用的可重用语句块
  6. < include />:引用标签的语句
  7. < selectKey />:不支持自增的主键生成策略标签

9种动态标签:
trim | where | set | foreach | if | choose| when | otherwise | bind

16、为什么说Mybatis是半自动ORM映射工具?它与全动的区别在哪里

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以称之为半自动 ORM 映射工具

17、MyBatis 实现一对一关联查询有几种方式

联合查询:
联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面配置 association 节点配置一对一的类就可以完成

嵌套查询:
嵌套查询是先查一个表,根据这个表里面结果的外键 id,再去另外一个表里面查询数据,也是通过association 配置,另外一个表的查询通过 select 属性配置

18、MyBatis 实现一对多关联查询有几种方式

联合查询:
联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面的 collection 节点配置一对多的类就可以完成

嵌套查询:
嵌套查询是先查一个表,根据这个表里面的结果的外键 id,再去另外一个表里面查询数据,也是通过配置 collection,另外一个表的查询通过 select 属性配置

19、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,几乎所有包括 Hibernate,支持延迟加载的原理都是一样的

拓展:CGLIB:
CGLIB(Code Generation Library) 是一个开源项目。是一个强大的,高性能,高质量的代码生成类库,它可以在运行期扩展Java类与实现 Java 接口。Hibernate、MyBatis 支持它来实现 PO(Persistent Object 持久化对象)字节码的动态生成

20、Mybatis 的一级、二级缓存

一级缓存:
基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 sqlSession,当 Session flush 或 close 或 C/U/D 操作之后,该 Session 中的所有 Cache 就将清空,默认开启一级缓存

二级缓存:
与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存,属性类要实现Serializable序列化接口,且全局变量 cacheEnabled 设置为 true,且在映射文件中添加一个标签< cache />

缓存更新机制:
当某一个作用域(一级缓存 Session /二级缓存 Namespaces )的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被清除

21、什么是 MyBatis 的接口绑定?有哪些实现方式

第一问:
接口绑定,就是在 MyBatis 中定义接口,然后把接口里面的方法和 sql 语句绑定,直接调用接口方法就可以执行对应的 sql 语句

第二问:

  1. 注解绑定:在接口的方法上面加上@Select、@Update 等注解,里面包含sql 语句来绑定
  2. xml 中写 sql 来绑定:xml 映射文件里面的 namespace 必须为接口的全路径名,标签的 id 值必须为方法名

22、使用 MyBatis 的 mapper 接口调用时有哪些要求

  1. Mapper.xml 文件中的 namespace 必须是 mapper 接口的类路径
  2. Mapper 接口方法名必须和 Mapper.xml 中定义的每个 sql 标签的 id 相同
  3. Mapper 接口方法的输入参数类型必须和 Mapper.xml 中定义的每个 sql 标签的 parameterType 的类型相同
  4. Mapper 接口方法的输出参数类型必须和 Mapper.xml 中定义的每个 sql 标签的 resultType 的类型相同

23、MyBatis 编程步骤是什么样的

  1. 创建 SqlSessionFactory
  2. 通过 SqlSessionFactory 创建 SqlSession
  3. 通过 SqlSession 执行数据库操作
  4. 调用 session.commit() 提交事务
  5. 调用 session.close() 关闭会话

24、请说说 MyBatis 的工作原理

在这里插入图片描述

  1. 读取 MyBatis 配置文件:mybatis-config.xml 为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库连接信息
  2. 加载映射文件:映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句
  3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory
  4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法
  5. 加载 Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存如一级缓存、二级缓存的维护
  6. 加载 MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息
  7. 输入参数映射:转化为数据库支持的类型。输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程
  8. 操作数据库
  9. 输出结果映射:转化为 Java 支持的类型。输出结果类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程

25、MyBatis 的功能架构是怎样的

接口层:
接口层主要提供和数据库交互的方式,包括 sqlSession 和 Mapper 接口两种方式。通过 SqlSession 接口和 Mapper 接口开发人员可以通知 MyBatis 框架调用那一条 sql 命令以及 sql 命令关联参数

数据处理层:
数据处理层可以说是 MyBatis 的核心,从大的方面上讲,它要完成三个功能: 通过参数构建动态 SQL 语句、 SQL 语句的执行以及查询结果类型转换 List

基础支撑层:
支撑层用来完成 MyBaits 与数据库基本连接方式以及 sql 命令与配置文件对应。主要负责:连接方式管理、事务管理、配置方式管理、缓存管理

26、Mybatis都有哪些 Executor 执行器?它们之间的区别是什么

Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:
每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象

ReuseExecutor:
执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement> 内,供下一次使用

BatchExecutor:
执行 update(没有select,JDBC批处理不支持select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch() 完毕后,等待逐一执行 executeBatch() 批处理。与 JDBC 批处理相同

27、Mybatis 中如何指定使用哪一种 Executor 执行器

配置:
在 Mybatis 配置文件中,在设置(settings)可以指定默认的 ExecutorType 执行器类型

传参:
在 DefaultSqlSessionFactory 创建 SqlSession 的方法中传递 ExecutorType 类型参数,来指定执行器类型

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玄天灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值