Java面试——MyBatis系列总结

Posted 张起灵-小哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试——MyBatis系列总结相关的知识,希望对你有一定的参考价值。

文章目录:

1.MyBatis是什么?

2.JDBC编程有哪些缺陷?MyBatis又是如何改进的?

3.MyBatis与Hibernate的区别在哪?

4.MyBatis的优缺点

5.请说说MyBatis的工作原理

6.MyBatis的架构设计是怎样的?

7.#{}和${}的区别

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

9.如何获取生成的主键?

10.当实体类中的属性名和表中的字段名不一样怎么办?

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

12.使用MyBatis的mapper接口调用时有哪些要求?

13.最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗

14.MyBatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?

15.MyBatis的一级、二级缓存是什么?


1.MyBatis是什么?

MyBatis是一款优秀的dao持久层框架、一个半ORM(对象关系映射)框架。它支持定制化SQL、存储过程、高级映射、缓存机制,同时MyBatis几乎避免了JDBC代码中手动设置参数以及获取结果集等繁杂操作的过程。

2.JDBC编程有哪些缺陷?MyBatis又是如何改进的?

  • JDBC编程中频繁创建、释放数据库连接对象,容易造成系统资源浪费,影响系统性能。可以使用连接池解决这个问题。

        解决:在mybatis-config.xml中配置数据库连接池,使用连接池管理数据库连接。

  • JDBC编程中 sql 语句写在代码中造成代码不易维护,实际应用场景中sql的变化可能较大,sql变动需要改变java代码。

       解决:将SQL语句配置在XXXXmapper.xml映射文件中,与java代码分离。

  • JDBC编程中向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

        解决:Mybatis中自动将java对象映射至sql语句。

  • JDBC编程中对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

       解决:Mybatis中自动将sql执行结果映射至java对象。

3.MyBatis与Hibernate的区别在哪?

首先要提到一个词ORM。ORM(Object Relational Mapping)对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。

相同点:

  • 都是对jdbc的封装,都是持久层的框架,都用于dao层的开发。

不同点:

  • Hibernate是全自动ORM映射工具,Hibernate 对SQL语句封装,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性,此外还提供 HQL(Hibernate Query Language)操作数据库,数据库无关性支持好,但会多消耗性能。使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
  • MyBatis 在查询关联对象或关联集合对象时,需要手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。所以,称之为半自动ORM映射工具。

4.MyBatis的优缺点

优点

与传统的数据库访问技术相比,ORM有以下优点:

  • 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除SQL与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。

  • 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接。

  • 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。

  • 提供映射标签,支持对象与数据库的字段映射;提供对象关系映射标签,支持对象关系组件维护。

  • 能够与Spring很好的集成。

缺点

  • SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

  • SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

5.请说说MyBatis的工作原理

  1. 通过加载mybatis全局配置文件以及mapper映射文件初始化configuration对象和Executor对象(通过全局配置文件中的defaultExecutorType初始化);
  2. 创建一个defaultSqlSession对象,将configuration对象和Executor对象注入给defaulSqlSession对象中;
  3. defaulSqlSession通过getMapper()获取mapper接口的代理对象mapperProxy(mapperProxy中包含defaultSQLSession对象)
  4. 执行增删改查:

         1) 通过defaulSqlSession中的属性Executor创建statementHandler对象;
         2) 创建statementHandler对象的同时也创建parameterHandler和resultSetHandler;

      5. 通过parameterHandler设置预编译参数及参数值;
      6. 调用statementHandler执行增删改查;
      7. 通过resultsetHandler封装查询结果。

6.MyBatis的架构设计是怎样的?

  • API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

  • 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

  • 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

  • 引导层:加载xml配置和Java配置。

7.#{}和${}的区别

  • #{}是占位符,预编译处理,可以防止SQL注入;${}是拼接符,字符串替换,没有预编译处理,不能防止SQL注入。

  • Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值;Mybatis在处理${}时,是原值传入,就是把${}替换成变量的值,相当于JDBC中的Statement编译。

  • #{} 的变量替换是在DBMS 中,变量替换后,#{} 对应的变量自动加上单引号;${} 的变量替换是在 DBMS 外,变量替换后,${} 对应的变量不会加上单引号。

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

  • "%"#{question}"%"    注意:因为#{...}解析成sql语句时候,会在变量外侧自动加单引号'  ',所以这里 % 需要使用双引号"  ",不能使用单引号 '  ',不然会查不到任何结果。
<select id="getEmpByName" resultType="com.szh.bean.Employee">
    select id,lastName,email,gender
	from employee
    where lastName like "%"#{lastName}"%"
</select>

List<Employee> list = mapper.getEmpByName("小");
  • '%${question}%'  可能引起SQL注入,不推荐。
<select id="getEmpByName" resultType="com.szh.bean.Employee">
	select id,lastName,email,gender
	from employee
	where lastName like '%${lastName}%'
</select>

List<Employee> list = mapper.getEmpByName("小");
  • CONCAT('%',#{question},'%')     使用CONCAT()函数,推荐。
<select id="getEmpByName" resultType="com.szh.bean.Employee">
    select id,lastName,email,gender
    from employee
    where lastName like concat("%",#{lastName},"%")
</select>

List<Employee> list = mapper.getEmpByName("小");
  • 使用bind标签。
<select id="getEmpByName" resultType="com.szh.bean.Employee">
	<bind name="pattern" value="'%' + lastName + '%'"/>
	select id,lastName,email,gender
	from employee
	where lastName like #{pattern}
</select>

List<Employee> list = mapper.getEmpByName("小");

9.如何获取生成的主键?

对于支持主键自增的数据库(MySQL)

<!-- useGeneratedKeys 设置为"true"表明 MyBatis 要获取由数据库自动生成的主键,keyColumn指定数据库主键,keyProperty指定 Java 实体类中对应的主键字段 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userId" >
  insert into user(
  user_name, user_password, create_time)
  values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>

parameterType 可以不写,Mybatis可以推断出传入的数据类型。如果想要访问主键,那么parameterType 应当是java实体或者Map。这样数据在插入之后可以通过java实体或者Map来获取主键值。

10.当实体类中的属性名和表中的字段名不一样怎么办?

第1种:通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

<select id="getEmpById" resultType="com.szh.bean.Employee">
	select id,last_name lastName,email,gender
	from employee
	where id = #{id}
</select>

第2种:通过<resultMap>来映射字段名和实体类属性名的一一对应关系。

<resultMap id="myMap" type="com.szh.bean.Employee">
	<id column="id" property="id"/>
	<result column="last_name" property="lastName"/>
	<result column="email" property="email"/>
	<result column="gender" property="gender"/>
</resultMap>

第3种:在mybatis全局配置文件中开启驼峰命名规则。

<settings>
	<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

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

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们调用接口方法的时候,最终会执行绑定的SQL语句。

接口绑定有两种实现方式,当Sql语句比较简单时候,可以使用注解绑定,当SQL语句比较复杂时候,一般用xml绑定的比较多。

  • 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来实现接口绑定;

  • 通过在xml里面写SQL语句来实现绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全限定类名,同时接口的方法名和SQL语句的id一一对应。

12.使用MyBatis的mapper接口调用时有哪些要求?

  • Mapper.xml文件中的namespace应该是对应mapper接口的全限定类名。

  • Mapper接口方法名和mapper.xml中定义的sql语句id一一对应。

  • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql语句的parameterType的类型相同。

  • Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql语句的resultType的类型相同。

13.最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗

Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement

举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一个<select><insert><update><delete>标签,都会被解析为一个MappedStatement对象。

Dao接口里的方法,是不能重载的,因为是全限定类名+方法名的保存和寻找策略,需要保证全限定类名+方法名的唯一性。(重载了话,方法名一定是重复的)

Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理对象proxy,代理对象proxy会拦截接口方法调用,转而执行方法对应的sql语句,然后将sql执行结果返回。

14.MyBatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?

Mybatis动态sql可以让我们在xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签trim|where|set|foreach|if|choose|when|otherwise|bind。

其执行原理为,使用OGNL(对象导航图语言Object Graph Navigation Language)从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。

15.MyBatis的一级、二级缓存是什么?

  • 一级缓存:采用 PerpetualCache,HashMap 存储。MyBatis默认打开一级缓存,其存储作用域为 当前sqlSession会话对象,当 sqlSession flush 或 close 之后,该 sqlSession 中的所有 Cache 就将清空。可以调用 clearCache();//手动清理缓存。
  • 二级缓存:默认也是采用 PerpetualCache,HashMap 存储,不同之处在于二级缓存的存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,开启二级缓存之后(在mybatis主配置文件中添加<setting name="cacheEnabled" value="true"/>;其次在对应的mapper映射文件中添加 <cache/>),对应的实体类需要实现Serializable序列化接口(可用来保存对象的状态)。
  • 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)进行了C/U/D 操作后,默认该作用域下所有缓存将被清理掉。

以上是关于Java面试——MyBatis系列总结的主要内容,如果未能解决你的问题,请参考以下文章

033期JavaEE面试题:MyBatis

[Interview]Java 面试宝典系列之 MyBatis

Java进阶之光!2021必看-Java高级面试题总结

java面试总结之四

进军2020年:Java研发岗千道面试题总结:MyBatis+Redis+Spring...

JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结