前言其实这篇文章不适合小白看。毕竟,如果你对Mybatis不是很了解,直接看这些面试题是不合适的,这样你可能读起来会比较吃力,但即使你是新手,我也会尽量让你明白。如果暂时看不懂,我不知道某道题的重点在哪里。没关系,收藏+关注,再也不会迷路了!老规矩,先熟悉一下Mybatis的概念吧,你心里可能有点反感啊,我天天用这个框架,别废话,开门见山,我就到这里随便说几句,这几句话也是我面试时爱问的一个点。不用花精力去处理各种JDBC的东西(想必大家刚开始学习的时候都被JDBC的各种步骤惊呆了)2、基于SQL,非常灵活,SQL写在XML文件中,而且还提供了多种XML标签,支持SQL3的动态编写,兼容各种数据库,兼容Spring4。缺点:可能需要一定的SQL基础,数据库之间的可移植性较差。Mybatis架构?框架介绍1、mybatis配置:SqlMapConfig.xml,该文件作为mybatis的全局配置文件,配置mybatis的运行环境等信息。mapper.xml文件为sql映射文件,操作数据库的sql语句配置在该文件中。此文件需要加载到SqlMapConfig.xml中。2、通过mybatis环境等配置信息,构造SqlSessionFactory,即会话工厂。3、通过会话工厂创建一个sqlSession,即会话,通过sqlSession操作数据库。4、mybatis底层自定义了Executor接口来操作数据库。Executor接口有两种实现,一种是基本执行器,一种是缓存执行器。5、MappedStatement也是mybatis的一个底层封装对象,里面封装了mybatis的配置信息和sql映射信息。mapper.xml文件中的一个sql对应一个MappedStatement对象,sql的id就是Mappedstatement的id。6、MappedStatement定义了sql执行的入参,包括HashMap、基本类型、pojo。Executor在执行sql之前通过MappedStatement将输入的java对象映射到sql。入参映射是jdbc编程中preparedStatement的参数设置。7、MappedStatement定义了SQL执行的输出结果,包括HashMap、基本类型、pojo。Executor通过MappedStatement将SQL执行后的输出结果映射到java对象中。输出结果映射过程相当于jdbc编程过程中对结果的解析。OK,稍微看了上面的内容,小白也能大概明白其中的意思了。它是一个帮助开发人员操作数据库的框架。走吧,不多说了。中年男人端着略有划痕的麦片走到我面前,张嘴问,吃了吗?开门见山,废话少说,你准备好了吗?让我们谈谈XML中的标签。XML文件中有9个顶级标签。下面简单介绍一下各个标签的作用。至于tags的具体使用和参数的含义,我会另写一篇文章来讲解cache:cacheconfigurationforagivennamespace。cache-ref:对其他命名空间缓存配置的引用。delete:映射对应的删除语句。insert:映射对应的insert语句。select:映射对应的查询语句。update:映射对应的update语句。sql:可重用的语句块,可以被其他语句引用。parameterMap:过时了!旧式参数映射。更好的方法是使用内联参数,这个元素将来可能会被移除。resultMap:是用于描述如何从数据库结果集中加载对象的最复杂和最强大的元素。Mybatis是如何将sql执行结果封装为目标对象并返回的呢?映射形式有哪些?第一种是使用标签将列名和对象属性名之间的映射关系一一定义。第二种是利用SQL列的别名功能,将列别名写成对象属性名,比如USER_NAMEASNAME,Mybatis会忽略列名的大小写,直接查找值对应的对象属性名。有了列名和属性名的映射关系后,Mybatis通过反射创建对象,通过反射对对象的属性进行一一赋值和返回。没有映射关系的属性是不可能完成赋值的。Mybatis中的动态SQL,说说怎么用吧。话不多说,直接上各个标签,给大家说说具体的用法。太多了,爱不释手,大家的阅读耐心也是有限的。foreach:用于循环容器的标签。concat:模糊查询。choose(when,otherwise)标签:choose标签依次判断when标签内部的测试条件是否为真。如果其中一个测试条件为真,则选择结束。当choose里面的when条件都不满足时,执行otherwise里面的sql。类似于Java的switch语句,choose是switch,when是case,otherwise是default。if:判断结论是否成立。where:SQL语句的where条件。set:使用set标签动态配置SET关键字,并去除附加在条件末尾的任何不相关的逗号。trim:trim是一个比较灵活的去除多余关键字的标签,可以练习where和set的效果。Mybatis分页实现mybatis框架分页实现。有几种方法。最简单的就是使用原生的sql关键字limit来实现。另一种是使用拦截器来拼接sql,实现和limit一样的功能。另一种是使用PageHelper来实现的就不用说了,第一种limit关键字。第二类拦截器其实就是拦截对应的session,实现limit的动态添加。第三种是在内部帮助我们实现拦截器的功能,不需要我们自己动手。实现了,可以认为是底层使用limit关键字获取分页数据。#{}和${}有什么区别?${}是字符串替换,相当于直接显示数据,#{}是预编译处理,相当于给数据加上双引号,即#是把传入的值当成字符串,先将其替换为?sign,然后调用PreparedStatement的set方法赋值,而$是直接显示传入的数据生成sql语句。使用#{}可以有效防止SQL注入,提高系统安全性(语句拼接)。如果按orderby使用,则需要使用${}。最大的区别是:#{}传入值时,sql解析参数带引号,${}传入值时,sql解析参数不带引号。通常,一个Xml映射文件会写一个与之对应的Dao接口。这个Dao接口的工作原理是什么?Dao接口中的方法在参数不同的情况下是否可以重载?Dao接口就是人们常说的Mapper接口,接口的全称是映射文件中namespace的值,接口的方法名是映射文件中MappedStatement的id值,接口方法中的参数是传递给sql的参数。Mapper接口没有实现类。调用接口方法时,以接口全名+方法名拼接的字符串作为键值,可以唯一定位MappedStatement。Dao接口中的方法不能重载,因为它是完全限定名+方法名的保存和查找策略。Dao接口的工作原理是JDK动态代理。Mybatis在运行时会使用JDK动态代理为Dao接口生成proxy代理对象。代理对象proxy会拦截接口方法,代之执行MappedStatement代表的sql,然后返回sql执行结果。Mybatis的Xml映射文件中,不同Xml映射文件的id可以重复吗?对于不同的Xml映射文件,如果配置了namespace,id可以重复;如果没有配置命名空间,id不能重复;毕竟,命名空间不是必需的,只是最佳实践。原因是因为MapMybatis使用namespace+id可以映射Enum枚举类?Mybatis可以映射枚举类,不止可以映射枚举类,Mybatis可以将任意对象映射到表的某一列。映射方法是自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用,一个是完成javaType到jdbcType的转换,另一个是完成jdbcType到javaType的转换,具体体现在setParameter()和getResult()这两个方法中,代表设置sql问题标记占位符参数并分别获取列搜索结果。在Mybatis映射文件中,如果A标签通过include引用B标签的内容,B标签是可以定义在A标签后面,还是必须定义在A标签前面?虽然Mybatis是按顺序解析Xml映射文件的,但是引用的B标签仍然可以在任何地方定义,Mybatis可以正确识别。原理是Mybatis解析A标签,发现A标签引用了B标签,但是B标签还没有解析,还不存在。这时候Mybatis会将A标签标记为未解析,然后继续解析剩下的标签,包括B标签,当所有标签解析完毕后,Mybatis会重新解析标记为未解析的标签。此时解析A标签时,B标签已经存在,可以正常解析A标签。为什么Mybatis是一个半自动的ORM映射工具?它和全自动有什么区别?Hibernate是一个全自动的ORM映射工具。使用Hibernate查询关联对象或关联集合对象时,可以根据对象关系模型直接获取,所以是完全自动化的。Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,因此被称为半自动ORM映射工具。Mybatis支持延迟加载吗?如果有,其实现原理是什么?Mybatis只支持关联关联对象延迟加载与集合关联的集合对象,关联是指一对一,集合是指一对多查询。在Mybatis的配置文件中,可以配置是否开启懒加载lazyLoadingEnabled=true|false。原理:使用CGLIB创建目标对象的代理对象。当调用目标方法时,进入拦截器方法,比如调用A.getB().getName(),拦截器invoke()方法发现A.getB()为null值。然后会单独发送B对象sql相关的预先保存的查询,查询B,然后调用A.setB(b),使A的对象b属性有值,然后完成A.getB()。getName()方法调用。这就是懒加载的基本原理。Mybatis有哪些Executors?它们之间有什么区别?Mybatis有3个基本的Executor,SimpleExecutor、ReuseExecutor和BatchExecutor。SimpleExecutor:每次执行update或select时,打开一个Statement对象,用完Statement对象立即关闭。ReuseExecutor:执行update或select,以sql为key查找Statement对象,存在则使用,不存在则创建,放在MapBatchExecutor中,而不是useselect后关闭Statement对象),addallsql到批处理(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象等待addBatch()完成executeBatch()批处理后一个一个执行。与JDBC批处理相同。在Mybatis的配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动将ExecutorType类型参数传递给创建SqlSession的DefaultSqlSessionFactory方法。作用范围:Executor的这些特性被严格限制在SqlSession生命周期的范围内。先说下Mybatis的一级缓存和二级缓存的理解。一级缓存:一个sqlsession级别,也就是说sqlsession只能访问自己的一级缓存中的数据,存储在默认的Map结构中。一级缓存查询存在于sqlsession类的各个实例对象中。当某个数据第一次被查询时,sqlsession类的实例对象会将数据存储在一级缓存中,其存储范围为Session。当AfterSessionflush或close时,会清空Session中的所有Cache,默认启用一级缓存。首先,当用户第一次查询sql时,会将sql的查询结果写入sqlsession的一级缓存,这样当用户第二次查询时,直接从第一次取数据级缓存而不是数据库。如果用户有增删改查等commit操作,sqlsession中的一级缓存区将被彻底清空。清除后,如果再次在一级缓存中找不到,就会去数据库中查找,然后再次存入缓存中。注意:缓存使用的数据结构也是一个map。二级缓存:二级缓存的作用范围是mapper级别,即mapper以命名空间为单位创建缓存数据结构,默认的Map结构。二级缓存与一级缓存相同。二级缓存中的多个sqlsession操作同一个mapper映射的sql语句,然后多个sqlsession可以共享二级缓存的idea。是跨sqlsession;定义存储源,如Ehcache,默认不开启二级缓存。启用二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可以用来保存对象的状态),可以在其映射文件中配置。SummaryBottom:mybatis的一级缓存是SqlSession级缓存。一级缓存缓存对象。当SqlSession被提交、关闭等更新数据库的操作发生时,一级缓存会被清空。二级缓存是SqlSessionFactory级别的缓存。同一个SqlSessionFactory生成的SqlSession都共享一个二级缓存。数据存储在二级缓存中。当命中二级缓存时,使用存储的数据构造对象并返回。查询数据时,查询过程是二级缓存>一级缓存>数据库。结论到这里,我说了很多问题。这些问题应该是面试中关于Mybatis最常被问到的问题了。也希望作为法官的你,能够对此有所了解。也许你对其中一个问题不了解,也许你不熟悉这些问题,但你通过这篇文章有所了解,本文的目的也达到了。本文转载自微信公众号“Java贼船”,可通过以下二维码关注。转载本文请联系Java盗贼公众号。
