Java面试题2018---J2EE后端---MyBatis 框架

1、MyBatis 的核心原理,及运行流程

现在开源项目中持久层框架用到最多的基本就是 iBatis、myBatis 和 Hibernate 了。

原理详解:

        MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置,配置来源于两个地方,一处是配置文件,一处是Java代码的注解,获取一个SqlSession。SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。

执行流程:

*MyBatis在初始化的时候,会将MyBatis的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration实例来维护。使用者可以使用sqlSession.getConfiguration()方法来获取。MyBatis的配置文件中配置信息的组织格式和内存中对象的组织格式几乎完全对应的。

*加载到内存中会生成一个对应的MappedStatement对象,然后会以key="com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary" ,value为MappedStatement对象的形式维护到Configuration的一个Map中。当以后需要使用的时候,只需要通过Id值来获取就可以了。

我们可以看到SqlSession的职能是:

SqlSession根据Statement ID, 在mybatis配置对象Configuration中获取到对应的MappedStatement对象,然后调用mybatis执行器来执行具体的操作。

*MyBatis执行器Executor根据SqlSession传递的参数执行query()方法

Executor.query()方法几经转折,最后会创建一个StatementHandler对象,然后将必要的参数传递给StatementHandler,使用StatementHandler来完成对数据库的查询,最终返回List结果集。

Executor的功能和作用是:

(1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;

(2、为查询创建缓存,以提高性能

(3、创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List查询结果。

*StatementHandler对象负责设置Statement对象中的查询参数、处理JDBC返回的resultSet,将resultSet加工为List 集合返回

StatementHandler对象主要完成两个工作:

         (1. 对于JDBC的PreparedStatement类型的对象,创建的过程中,我们使用的是SQL语句字符串会包含 若干个? 占位符,我们其后再对占位符进行设值。StatementHandler通过parameterize(statement)方法对Statement进行设值;       

         (2.StatementHandler通过List<E> query(Statement statement, ResultHandler resultHandler)方法来完成执行Statement,和将Statement对象返回的resultSet封装成List;

         StatementHandler 的parameterize(Statement) 方法调用了 ParameterHandler的setParameters(statement) 方法,

ParameterHandler的setParameters(Statement)方法负责 根据我们输入的参数,对statement对象的 ? 占位符处进行赋值。

         StatementHandler的List<E> query(Statement statement, ResultHandler resultHandler)方法的实现,是调用了ResultSetHandler的handleResultSets(Statement)方法。ResultSetHandler的handleResultSets(Statement) 方法会将Statement语句执行后生成的resultSet结果集转换成List<E> 结果集

从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:

MyBatis封装了对数据库的访问,把对数据库的会话和事务控制放到了SqlSession对象中。

  • SqlSession           作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
  • Executor             MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
  • StatementHandler  封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
  • ParameterHandler  负责对用户传递的参数转换成JDBC Statement 所需要的参数,
  • ResultSetHandler   负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
  • TypeHandler         负责java数据类型和jdbc数据类型之间的映射和转换
  • MappedStatement  MappedStatement维护了一条<select|update|delete|insert>节点的封装,
  • SqlSource           负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql            表示动态生成的SQL语句以及相应的参数信息
  • Configuration       MyBatis所有的配置信息都维持在Configuration对象之中。

本题参考博客:https://blog.csdn.net/d12345678a/article/details/53956485

2、MyBatis 与 Hibernate 有什么异同? 

hibernate:

1、Hibernate则相对来说较为复杂,学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,怎样用好 Hibernate 方面需要经验和能力都很强才行。 

2、Hibernate 会自动生成sql 语句,能够在程序运行时自动生成,能够自动建表,无论到什么机器上,都不需要数据库,都能自动完成迁移;

3、Hibernate 功能强大,数据库无关性好,O/R(对象/关系)映射能力强,Hibernate 对数据库结构提供了较为完整的封装,Hibernate 的O/R Mapping实现了POJO(实体类) 和数据库表之间的映射,以及SQL 的自动生成和执行。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过 Hibernate 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握, Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行;

5、hibernate可移植性好,如一个项目开始使用的是mysql数据库,但是随着业务的发展,现mysql数据库已经无法满足当前的绣球了,现在决定使用Oracle数据库,虽然sql标准定义的数据库间的sql语句差距不大,但是不同的数据库sql标准还是有差距的,那么我们手动修改起来会存在很大的困难,使用hibernate只需改变一下数据库方言即可搞定。用hibernate框架,数据库的移植变的非常方便。

6、当系统属于二次开发,无法对数据库结构做到控制和修改,那myBatis 的灵活性将比 Hibernate 更适合。系统数据处理量巨大,性能要求极为苛刻,这往往意味着我们必须通过经过高度优化的SQL语句(或存储过程)才能达到系统性能设计指标。在这种情况下 myBatis 会有更好的可控性和表现。 

7、涉及到大数据的系统使用Mybatis比较好,因为优化较方便。涉及的数据量不是很大且对优化没有那么高,可以使用hibernate。

mybatis:

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

    mybatis的优点:

  • 1. 与JDBC相比,减少了50%以上的代码量。
  • 2. MyBatis是最简单的持久化框架,小巧并且简单易学。
  • 3. MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
  • 4. 提供XML标签,支持编写动态SQL语句。
  • 5. 提供映射标签,支持对象与数据库的ORM字段关系映射。

   MyBatis框架的缺点:

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

 iBatis与myBatis的关系:
  iBatis是2002年发起的开放源代码项目,之前一直托管在apache下,于2010年6月16号被谷歌托管,改名为MyBatis,myBatis 与 iBatis 一样,也是一种“半自动化”的ORM实现,共同的优点:

  • 是一个基于Java的持久层框架 
  • 提供的持久层框架包括SQL Maps和Data Access Objects(DAO) 
  • 支持普通 SQL查询,存储过程和高级映射的优秀持久层框架 
  • 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索 
  • 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录 
  • myBatis 采用功能强大的基于OGNL的表达式来消除其他元素。 

功能架构: 


  • 1.API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 
    2.数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。 
    3.基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。 

本题参考博客:https://blog.csdn.net/weixin_42131983/article/details/81322029

3、什么是 MyBatis 命名空间? 

在 mybatis 中,可以为每个映射文件起一个唯一的命名空间,这样,定义在这个映射文件中的每个 SQL 语句就成了定义在这个命名空间中的一个 id。只要我们能够保证每个命名空间是唯一的,即使在不同映射文件中的语句的 id 相同,也就不会冲突了。

在mybatis中,映射文件中的namespace是用于绑定Dao接口的,即面向接口编程
当你的namespace绑定接口后,你可以不用写接口实现类,mybatis会通过该绑定自动
帮你找到对应要执行的SQL语句,如下:
假设定义了IArticeDAO接口

public interface IArticleDAO
{
   List<Article> selectAllArticle();
}

对于映射文件如下:

<mapper namespace="IArticleDAO">
  <select id="selectAllArticle" resultType="article">
      SELECT t.* FROM T_article t WHERE t.flag = '1' ORDER BY t.createtime DESC
  </select>
</mapper>

请注意接口中的方法与映射文件中的SQL语句的ID一一对应 。
则在代码中可以直接使用IArticeDAO面向接口编程而不需要再编写实现类。

本题参考博客:https://blog.csdn.net/suyu_yuan/article/details/51329144

4、MyBatis 中如何进行 Mapper 的动态代理? 

UserMapper是一个接口 它并没有实现类,为什么接口可以直接使用呢? 那是因为MyBbatis使用了JDK动态代理机制动态生成了代理类

采用Mapper动态代理方法只需要编写相应的Mapper接口(相当于Dao接口),那么Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
1、Mapper.xml文件中的namespace与mapper接口的全类名相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

此处参考博客:https://blog.csdn.net/xiaokang123456kao/article/details/66476828

源码分析:

*Mybatis关于包装Mapper的代码都在org.apache.ibatis.binding 这个包下面。其中有4个类: 
MapperRegistry 类是注册Mapper接口与获取代理类实例的工具类。

*类的getMapper方法里面最后会去调用MapperProxyFactory类的newInstance方法。在调用getMapper方法前会初始化MapperProxyFactory,它是创建Mapper代理对象的工厂

*在这里创建了MapperProxy对象 这个类实现了JDK的动态代理接口 InvocationHandler

*从缓存中获得执行方法对应的MapperMethod类实例。如果MapperMethod类实例不存在的情况,创建加入缓存并返回相关的实例。最后调用MapperMethod类的execute方法。 
到这里我们可以看到,getMapper方法就是用来获得相关的数据操作类接口。而事实数据操作类邦定了动态代理。所以操据操作类执行方法的时候,会触动每个方法相应的MapperProxy类的invoke方法。所以invoke方法返回的结果就是根据操作类执行方法的结果。这样子我们就知道最后的任务交给了MapperMethod类实例。
*MapperMethod类里面有俩个成员:SqlCommand类和MethodSignature类。从名字上我们大概的能想到一个可能跟SQL语句有关系,一个可能跟要执行的方法有关系。事实也是如此。 
上面使用一个内部类SqlCommand来封装底层的增删改查操作,确切来讲这一部分的内容跟XxxMapper的XML配置文件里面的select节点、delete节点等有关。我们都会知道节点上有id属性值。那么MyBatis框架会把每一个节点(如:select节点、delete节点)生成一个MappedStatement类。要找到MappedStatement类就必须通过id来获得。有一个细节要注意:代码用到的id = 当前接口类 + XML文件的节点的ID属性。
*我们可以回头去找一找在什么时候增加了MappedStatement类。上面之所以可以执行是建立在XML配置信息都被加载进来了。所以MappedStatement类也一定是在加载配置的时候就进行的。请读者们自行查看一下MapperBuilderAssistant类的addMappedStatement方法——加深理解。SqlCommand类的name成员和type成员我们还是关注一下。name成员就是节点的ID,type成员表示查寻还是更新或是删除。至于MethodSignature类呢。他用于说明方法的一些信息,主要有返回信息。

小知识:反射调用的最大好处就是配置性大大提高,就如同Spring IOC容器一样,我们可以给很多配置参数,使得java应用程序能够顺利运行起来,大大提高java的灵活性和可配置性,降低模块之间的耦合。动态代理就是基于反射实现的。JAVA自带的动态代理是基于java.lang.reflect.Proxy、java.lang.reflect.InvocationHandler两个类来完成的,使用了JAVA反射机制,通常使用下面方法创建代理对象: Object proxy = Proxy.newProxyInstance(定义代理对象的类加载器,要代理的目标对象的归属接口数组,回调接口InvocationHandler)
本题参考博客:https://blog.csdn.net/xiaokang123456kao/article/details/76228684

 

也可查看我的另一篇博客https://blog.csdn.net/jinhaijing/article/details/84314576,查看具体完整的源码流程

建议可以看看https://blog.csdn.net/xubaifu/article/details/78831147这篇博文,看看自己怎么实现jdk和cglib的动态代理。对于理解mybatis中的动态代理有好处。

 

5、MyBatis 中如何定义别名查询? 

非自定义别名,所有javaJDK中的类都定义了别名,别名是类名不区分大小写: map 替换java.util.Map,如果是包装类,还可以使用基本数据类型:int 替换java.lang.Integer

自定义别名:

<configuration>
    <typeAliases>
      <typeAlias  alias="Person" type="com.stone.model.Person"/> 
    </typeAliases>
</configuration>

mapper.xml中可以使用:

<select id="getAllUser" resultType="Person">
    select * from users
</select>

6、MyBatis 的结果集 resultMap 可以定义哪些类型? 

resultmap是mybatis中最复杂的元素之一,它描述如何从结果集中加载对象,主要作用是定义映射规则、级联的更新、定制类型转化器。

比如查询对象中有集合,或者对象时,此时就需要用到resultMap来适配结果。

association 一对一级联

 

collection 一对多级联

 

注:此处,用到了分步查询,如果还需要配置延迟加载(也叫懒加载和按需加载),在全局文件中还需要配置

<settings>
	<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
	<setting name="lazyLoadingEnabled" value="true"/>
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>

关于分步查询和延迟加载可参考博客:https://blog.csdn.net/jinhaijing/article/details/84173959

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

mybatis原生也是支持分页的,但为了与数据库语法解耦,实现的是逻辑分页,首先将所有结果查询出来,然后通过计算offset和limit,只返回部分结果,操作在内存中进行,所以也叫内存分页,当数据量比较大的时候,肯定是不行的,核心类叫Rowbounds

使用原生分页方法:

给接口中传入RowBounds rowBounds = new RowBounds(0, 5);

listPo = userPoMapper.selectByExample(type,rowBounds);

 List<DictionaryPo> selectByExample(Integer type, RowBounds rowBounds) throws Exception;

 

注:PageHelper的使用

  • PageHelper的出现使得mybatis实现了物理上的分页(之前一直听说mybatis分页很弱,而且实现的是逻辑分页,非常影响性能,而且大大增加了OOM的可能),实际原理就是修改最后的执行sql,增加xi相应的分页内容,是基于拦截器实现的。
  • 实现原理: 就是在StatementHandler之前进行拦截,对MappedStatement进行一系列的操作(大致就是拼上分页sql)
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEconfiguration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
      <!-- 配置分页插件 -->
      <plugins>
            <plugininterceptor="com.github.pagehelper.PageHelper">
                  <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->       
           <propertyname="dialect"value="mysql"/>
            </plugin>
      </plugins>
</configuration>

 

运用分页插件后mybatis的运行流程:

1、sqlsession: sqlsession是一次与数据库的会话。在你每次访问数据库时都需要创建它(当然并不是说在sqlsession里只能执行一次sql,你可以执行多次,当一旦关闭了Sqlsession就需要重新创建它)。sqlsession只能由SqlsessionFactory的openSession方法来完成创建
2、executor: 通过源码我们可以看到:executor其实只是DefaultSqlSession的一个属性,而executor的每个操作都需要一个对象,那就是MappedStatement

3、MappedStatement: 这个东西个人看来就是用来封装sql和返回结果的,我们很熟悉的可以看到id(个人猜想可能是mapper.xml中的那个id,即具体执行的是哪个sql)、parameterMap(应该就是mapper.xml中的那个parameterMap,即sql中变量的类型)、 resultMaps(大概就是你需要的返回对象类型)
可参考博客:https://blog.csdn.net/leozhou13/article/details/50394242看怎么自己制作一个分页插件

8、MyBatis 中什么是逻辑分页,什么是物理分页,分别有什么优缺点? 

逻辑分页:将所有结果查询出来,然后通过计算offset和limit,只返回部分结果,操作在内存中进行,所以也叫内存分页,当数据量比较大的时候,肯定是不行的,核心类叫Rowbounds

物理分页:修改最后的执行sql,增加xi相应的分页内容,是基于拦截器实现的

具体可看上一题

9、MyBatis 怎样进行事务管理? 

mybatis支持两种事务类型,分别为JdbcTransaction和ManagedTransaction。

Mybatis定义了一个事务类型接口Transaction,JdbcTransaction和ManagedTransaction两种事务类型都实现了Transaction接口。

具体可参考博客:https://blog.csdn.net/majinggogogo/article/details/72026693

10、比较 MyBatis 和 Hibernate 事务管理的区别 

11、MyBatis 框架有哪些注解? 

12、MyBatis 如何进行关联关系(一对一,一对多,多对多),以及双向关联关系查询? 

13、MyBatis 有几种缓存,获取 Sqlsession 后,查询数据的顺序;MyBatis 中与Hibernate 中获取 session 后,查询数据的顺序有什么区别? 

14、简述 MyBatis 的插件运行原理,以及如何编写一个插件 

14、MyBatis 怎样处理延迟加载? 

15、集成 Spring MVC+Spring+MyBatis 有哪些步骤? 

16、什么是 MyBatis 的接口绑定,有什么好处 

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

18、MyBatis 执行批量插入,能返回数据库主键列表吗? 

19、MyBatis 动态 SQL 是做什么的?都有哪些动态 SQL?能简述一下动态 SQL 的执行原理

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

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

22、MyBatis 中如何指定使用哪一种 Executor 执行器? 

30、简述 MyBatis 的 Xml 映射文件和 MyBatis 内部数据结构之间的映射关系? 

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

32、Mybatis 如何与 LOG4J 结合打印日志 

33、MyBatis 如何执行存储过程 

34、Mybatis 数据源管理方式有几种? 

35、MyBatis 引入 XXX.mapper 映射文件有几种方式? 

36、MyBatis 事务管理有几种方式? 

37、什么样的需求使用 mybatis 框架更好?什么样的需求使用 hibernate 框架更好? 

38、解释下 DefaultSqlSessionFactory 的作用? 

39、解释下 SqlSessionFactoryBuilder 的作用? 

40、说出 MyBatis 缓存和 Hibernate 缓存的区别? 

41、MyBatis 中 sql 语句执行类型有几种方式?(ExecutorType) 

42、MyBatis 中 ObjectFactory 是什么? 

43、MyBatis 中 TypeHandler 是什么? 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值