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

Posted Spring-_-Bear

tags:

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

文章目录

1. 谈谈 MyBatis 和 JPA 的区别

  1. ORM(Object-Relationl Mapping)映射不同:
    • MyBatis 是半自动的 ORM 框架,提供数据库与结果集的映射
    • JPA(Java Persistence API)(默认采用 Hibernate 实现)是全自动的 ORM 框架,提供对象与数据库的映射
  2. 可移植性不同:
    • JPA 通过它强大的映射结构和 HQL 语言,大大降低了对象与数据库的耦合性
    • MyBatis 由于需要写 SQL,因此与数据库的耦合性直接取决于 SQL 的写法,如果 SQL 不具备通用性而用了很多数据库的特性 SQL 的话,移植性就会降低很多,移植时成本很高
  3. 日志系统的完整性不同:
    • JPA 日志系统非常健全、涉及广泛,包括:SQL 记录、关系异常、优化警告、缓存提示、脏数据警告等
    • MyBatis 除了基本的记录功能外,日志功能薄弱很多
  4. SQL 优化上的区别:
    • 由于 Mybatis 的 SQL 都是写在 XML 里,因此优化 SQL 比 Hibernate 方便很多
    • Hibernate 的 SQL 很多都是自动生成的,无法直接维护 SQL。虽有 HQL,但功能还是不及 SQL 强大,见到报表等复杂需求时 HQL 就无能为力,也就是说 HQL 是有局限的。Hibernate 虽然也支持原生 SQL,但开发模式上却与 ORM 不同,需要转换思维,因此使用上不是很方便。总之写 SQL 的灵活度上 Hibernate 不及 Mybatis

2. MyBatis 输入输出支持的类型有哪些?

MyBatis 支持多种输入输出类型,包括:

  1. 简单的基本数据类型,如整数、小数、字符串等
  2. 集合类型,如 Map 等
  3. 自定义的 JavaBean

其中,简单的类型,其数值直接映射到参数上。对于 Map 或 JavaBean 则将其属性按照名称映射到参数上。

3. MyBatis 里如何实现一对多关联查询?

一对多映射有两种配置方式,都是使用 collection 标签实现的。在此之前,为了能够存储一对多的数据,需要在主表对应的实体类中增加集合属性,用于封装子表对应的实体类

嵌套查询:

  1. 通过 select 标签定义查询主表的 SQL,返回结果通过 reusltMap 进行映射
  2. 在 resultMap 中,除了映射主表属性,还要通过 collection 标签映射子表属性,该标签需包含如下内容:
    • 通过 property 属性指定子表属性名
    • 通过 javaType 属性指定封装子表属性的集合类型
    • 通过 ofType 属性指定子表的实体类型
    • 通过 select 属性指定查询子表所依赖的 SQL,这个 SQL 需单独定义,内部包含查询子表的语句

嵌套结果:

  1. 通过 select 标签定义关联查询主表和子表的 SQL,返回结果通过 resultMap 进行映射
  2. 在 resultMap 中,除了映射主表属性,还要通过 collection 标签映射子表属性,该标签需包含如下内容:
    • 通过 property 属性指定子表属性名
    • 通过 ofType 属性指定子表的实体类型
    • 通过 result 子标签定义子表字段和属性的映射关系
<!-- 方式一:多表联查为集合中的 bean 赋值 -->
<resultMap id="empInDeptResultMap" type="Dept">
    <id property="did" column="did"/>
    <result property="deptName" column="dept_name"/>
    <!-- ofType 属性设置集合存储的 bean 类型 -->
    <collection property="empList" ofType="Emp">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="sex" column="sex"/>
        <result property="age" column="age"/>
        <result property="email" column="email"/>
    </collection>
</resultMap>
<!-- Dept getEmpInDeptByDid(@Param("did")Integer did); -->
<select id="getEmpInDeptByDid" resultMap="empInDeptResultMap">
    select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #did
</select>

4. MyBatis 中的 $ 和 # 有什么区别?

  • 使用 # 设置参数时,MyBatis 会创建预编译的 SQL 语句,然后在执行 SQL 时 MyBatis 会为预编译 SQL 中的占位符(?)赋值。预编译的 SQL 语句执行效率高,并且可以防止 SQL 注入
  • 使用 $ 设置参数时,MyBatis 只是创建普通的 SQL 语句,然后在执行 SQL 语句时MyBatis将参数直接拼入到 SQL 里。这种方式在效率、安全性上均不如前者,但是可以解决一些特殊情况下的问题。例如,在一些动态表格(根据不同的条件产生不同的动态列)中,我们要传递 SQL 的列名,根据某些列进行排序,或者传递列名给 SQL 都是比较常见的场景,这就无法使用预编译的方式了

5. 既然 $ 不安全,为什么还需要 $,什么时候会用到它?

它可以解决一些特殊情况下的问题。例如,在一些动态表格(根据不同的条件产生不同的动态列)中,我们要传递 SQL 的列名,根据某些列进行排序,或者传递列名给 SQL 都是比较常见的场景,这就无法使用预编译的方式了。

  • 动态表名查询
  • 动态列名查询

6. MyBatis 的 xml 文件和 Mapper 接口是怎么绑定的?

是通过 xml 文件中,<mapper> 根标签的 namespace 属性进行绑定的,即 namespace 属性的值需要配置成接口的全限定名称,MyBatis 内部就会通过这个值将这个接口与这个 xml 关联起来。

7. MyBatis 分页和自己写的分页哪个效率高?

自己写的分页效率高。在 MyBatis 中,我们可以通过分页插件实现分页,也可以通过分页 SQL 自己实现分页。其中,分页插件的原理是,拦截查询 SQL,在这个 SQL 基础上自动为其添加 limit 分页条件。它会大大的提高开发的效率,但是无法对分页语句做出有针对性的优化,比如分页偏移量很大的情况,而这些在自己写的分页 SQL 里却是可以灵活实现的。

8. 了解 MyBatis 缓存机制吗?

MyBatis的缓存分为一级缓存和二级缓存。

  • 一级缓存:一级缓存也叫本地缓存,它默认会启用,并且不能关闭。一级缓存存在于 SqlSession 的生命周期中,即它是 SqlSession 级别的缓存。在同一个 SqlSession 中查询时,MyBatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个 Map对象中。如果同一个 SqlSession 中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当 Map 缓存对象中己经存在该键值时,则会返回缓存中的对象。

  • 二级缓存:二级缓存是 SqlSessionFactory 级别的,通过同一个 SqlSessionFactory 创建的 SqlSession 查询的结果才会被缓存,缓存的数据只能通过同一个 SqlSessionFactory 创建的 SqlSession 才能访问。两次查询之间执行了任意的增删改,会使一级、二级缓存同时失效。二级缓存开启的四个条件:

    1. 在核心配置文件中配置全局属性 cacheEnabled="true",默认为 true
    2. 在对应的 mapper 映射文件中设置标签 <cache/>
    3. 查询的数据转换成的 JavaBean 类必须实现 Serilizable 序列化接口
    4. 只有在 SqlSession 关闭或提交之后二级缓存才会生效

    二级缓存具有如下效果:

    1. 映射语句文件中的所有 SELECT 语句将会被缓存
    2. 映射语句文件中的所有 INSERT 、UPDATE 、DELETE 语句会刷新缓存
    3. 缓存会使用 Least Recently Used ( LRU ,最近最少使用的)算法来收回
    4. 根据时间表(如 no Flush Interval ,没有刷新间隔),缓存不会以任何时间顺序来刷新
    5. 缓存会存储集合或对象(无论查询方法返回什么类型的值)的 1024 个引用
    6. 缓存会被视为 read/write(可读/可写)的,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改

以上是关于[Interview]Java 面试宝典系列之 MyBatis的主要内容,如果未能解决你的问题,请参考以下文章

[Interview]Java 面试宝典系列之 Spring Boot

[Interview]Java 面试宝典系列之 Java 多线程

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

[Interview]Java 面试宝典系列之 Java 集合类

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

[Interview]Java 面试宝典系列之 Spring MVC