MyBatis

MyBatis层次结构

20141028140852531.png
大致流程:

  1. MyBatis的初始化:MyBatis的初始化的过程其实就是解析配置文件和初始化Configuration的过程,解析完MyBatis配置文件后,configuration就初始化完成了,然后根据configuration对象来创建SqlSession,到这里时,MyBatis的初始化的征程已经走完了。
  2. MyBatis的SQL查询流程:SQL语句的执行才是MyBatis的重要职责,该过程就是通过封装JDBC进行操作,然后使用Java反射技术完成JavaBean对象到数据库参数之间的相互转换,这种映射关系就是有TypeHandler对象来完成的,在获取数据表对应的元数据时,会保存该表所有列的数据库类型
    执行器在query操作中,优先会查询缓存是否命中,命中则直接返回,否则从数据库中查询。
    真正的doQuery操作是由SimplyExecutor代理来完成的,该方法中有2个子流程,一个是SQL参数的设置,另一个是SQL查询操作和结果集的封装。

Mybatis缓存

MyBatis提供查询缓存,用于减轻数据库压力,提高性能。MyBatis提供了以及缓存和二级缓存

  1. 一级缓存为SQLSession级别的缓存,每个SQLSession都有一个哈希表用于缓存数据,不同SQLSession对象之间缓存不共享。
    同一个SqlSession对象对象执行2遍相同的SQL查询,在第一次查询执行完毕后将结果缓存起来,这样第二遍查询就不用向数据库查询了,直接返回缓存结果即可。
    MyBatis默认是开启一级缓存的。
  2. 二级缓存是mapper级别的缓存,二级缓存是跨SqlSession的,多个SqlSession对象可以共享同一个二级缓存。
    不同的SqlSession对象执行两次相同的SQL语句,第一次会将查询结果进行缓存,第二次查询直接返回二级缓存中的结果即可。
    MyBatis默认是不开启二级缓存的,可以在配置文件中使用如下配置来开启二级缓存:xml <settings> <setting name="cacheEnabled" value="true"/> </settings>

缓存的清空

当SQL语句进行更新操作(删除/添加/更新)时,会清空对应的缓存,保证缓存中存储的都是最新的数据。

MyBatis的二级缓存对细粒度的数据级别的缓存实现不友好,比如如下需求:对商品信息进行缓存,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息。

因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存,具体业务具体实现。

MyBatis相关面试题

#{}和${}的区别

  • ${}是Properties文件中的变量占位符,处理方式是字符串替换,不会额外在字符串两边加引单号
  • #{}是SQL语句的参数占位符,mybatis会把#{}内容替换为?,在SQL执行前会使用PreparedStatement的参数设置方法按序设置参数,并且会在值两边加上单引号

除了select|insert|update|delete标签之外,还有哪些标签

比如:

  1. <id>:在命名空间中唯一的标识符,可以被用来引用这条语句。
  2. <resultMap>:期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
  3. <parameterType>:将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置
  4. <sql>,<include>:这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。
  5. <selectKey>:用于生成能够唯一识别对象的key

加上动态sql的9个标签trim||where|set|foreach|if|choose|when|otherwise|bind

DAO接口的原理是什么,接口内的方法能够重载吗

Dao接口介绍

DAO接口,也就是人们常说的Mapper接口

  • 接口的全限定名,就是映射文件中的 namespace 的值。
  • 接口的方法名,就是映射文件中MappedStatement的 id 值。
  • 接口方法内的参数,就是传递给 sql 的参数。

Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个MappedStatement

举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到 namespace 为com.mybatis3.mappers.StudentDao下面id = findStudentByIdMappedStatement

在 Mybatis 中,每一个<select><insert><update><delete>标签,都会被解析为一个MappedStatement对象。

能否重载?

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

Dao接口原理

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

MyBatis插件运行原理

Mybatis 仅可以编写针对 ParameterHandlerResultSetHandlerStatementHandlerExecutor 这 4 种接口的插件。

Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行需要拦截的这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandlerinvoke()方法。

MyBatis动态sql

简介

Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。

有哪些动态SQL

Mybatis 提供了 9 种动态 sql 标签trim|where|set|foreach|if|choose|when|otherwise|bind

动态SQL执行原理

其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

MyBatis是如何将sql结果封装为对象返回,有哪些映射形式

  1. 第一种使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。
  2. 第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是 name,小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能找到与之对应对象属性名。

有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

MyBatis是否支持延迟加载

支持,可以配置是否启用延迟加载lazyLoadingEnabled=true|false

原理

使用cglib创建目标的代理对象,当调用目标方法时,进入拦截器方法,拦截器如果发现目标对象为null,则会先发送事先保存好的查找该对象的sql,先把这个对象查询上来,查询完成后再继续之前拦截器方法的调用。

当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。

不同XML映射文件,id是否可以重复

如果配置了namespace则id可以重复,如果没有配置namespace,id不可以重复。

原因为: namespace+id 是作为 Map<String, MappedStatement>的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。

MyBatis如何执行批处理

使用BatchExecutor完成批处理

MyBatis有哪些Executor执行器,区别是什么

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

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

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

作用范围:Executor 的这些特点,都严格限制在 SqlSession 生命周期范围内。

MyBatis如何指定使用哪一种Executor执行器

在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型。

也可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。

MyBatis映射中标签的解析有顺序吗,后解析的标签可以提前使用吗

虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。

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

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。

而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。