Mybatis、Mybatis-Plus面试题

主要参考以下资料并加入一些平时学习时的问题做整理!!! 如果版权问题,立刻删除!
新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题)_哔哩哔哩_bilibili

Mybatis

1、什么是Mybatis?

  • MyBatis是一款基于Java的持久层框架,它提供了一种简单的方式来映射数据库操作到Java对象。它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态SQL,可以严格控制SQL执行性能,灵活度高。
  • MyBatis可以使用XML注解配置映射原生信息,将POJO映射成数据库中的记录,避免了几乎所有JDBC代码和手动设置参数以获取结果集
  • 通过XML文件或注解的方式将要执行的各种statement配置起来,并通过Java对象和statement中的SQL的动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射为Java对象并返回

2、说说MyBatis的优点和缺点?

  • 优点
    1. 基于SQL语句编程,相当灵活,不会对应用程序或数据库的现有设计造成任何影响,SQL写在XML里,解除SQL与程序代码的耦合(用注解写就另说了),便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用
    2. 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码不需要手动开关连接
    3. 提供应设标签,支持对象与数据库的ORM字段关系映射,提供对象关系映射标签,支持对象关系组件维护
  • 缺点
    1. SQL语句编写的工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求
    2. SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
    3. XML配置较为繁琐,MyBatis的配置文件中需要编写大量的XML代码来描述SQL语句和映射关系,这可能会使配置文件显得较为繁琐

3、#{}${}的区别是什么?

  • #{}在SQL语句中表示一个占位符,它可以防止SQL注入攻击,并且可以自动进行参数类型转换。在执行SQL语句时,MyBatis会将参数值以安全的方式设置到SQL语句中。底层使用的是PreparedStatement#{}占位符替换为?
  • ${}在SQL语句中也表示一个占位符,但它不会对参数进行任何处理,直接将参数值拼接到SQL语句中,因此容易引发SQL注入攻击。底层使用的是Statement

4、MyBatis执行流程是什么样子的?

  • ①读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件
  • ②构造会话工厂SqlSessionFactory,一个项目只需要一个,单例的,一般由 spring进行管理
  • ③会话工厂创建SqlSession对象,这里面就含了执行SQL语句的所有方法
  • ④操作数据库的接口,Executor执行器,同时负责查询缓存的维护
  • ⑤Executor接口的执行方法中有一个MappedStatement类型的参数,封装了 映射信息
  • ⑥输入参数映射
  • ⑦输出结果映射

5、Mybatis是否支持延迟加载?延迟加载的底层原理知道吗?

  • 支持,在Mybatis配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false,默认是关闭的或者在mapper文件中设置fetch_type=lazy

  • 延迟加载在底层主要使用的CGLIB动态代理完成的

    Mybatis、Mybatis-Plus面试题_第1张图片

    • 使用CGLIB创建目标对象的代理对象,这里的目标对象就是开启了 延迟加载的mapper
    • 当调用目标方法时(获取另一个实体对象),进入拦截器invoke方法,发现目标方法是null 值,再执行sql查询(懒加载)
    • 获取数据以后,调用set方法设置属性值,再继续查询目标方法,就 有值了

6、Mybatis的一级、二级缓存用过吗?

  • mybatis的一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,SqlSession级别,**默认开启。**同一个SqlSession内部的多次查询操作,如果查询的参数和查询语句都相同,那么第一次查询时查询结果会被缓存到一级缓存中,后续的查询操作会直接从缓存中获取结果,而不会再去查询数据库。一级缓存的生命周期与SqlSession的生命周期相同,当SqlSession被关闭时,一级缓存也会被清空。

  • 二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQL session,默认也是采用 PerpetualCache,HashMap 存储。**因此当多个SqlSession操作同一个Mapper时,他们之间共享一个二级缓存。二级缓存需要单独开启,二级缓存的缓存时间是无限制的,但是它的缓存策略是基于LRU(最近最少使用)算法的。**为了提高缓存命中率,MyBatis还提供了缓存清空机制,可以通过在Mapper中配置flushCache="true"来清空缓存。此外,MyBatis还支持基于注解的缓存配置,可以通过在Mapper接口或方法上添加@CacheNamespace或者@CacheNamespaceRef注解来配置缓存

    • 1,全局配置文件

      <settings>
          settings>
      
    • 2,映射文件

      使用标签让当前mapper生效二级缓存

  • 二级缓存的注意事项

    • 1,对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear。
    • 2,二级缓存需要缓存的数据实现Serializable接口
    • 3,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中。

7、插入数据后如何获取插入数据库数据的主键?

  • 在Mapper 接口中的方法上添加一个@Options注解,并在注解中指定属性useGeneratedKeys=truekeyProperty="实体类属性名"
  • Mybatis Plus: 自动将主键值回写到实体类中

8、如何处理Java实体类属性名和数据库表中的字段名不一致的情况?

  • 如果实体类属性名和数据库表查询返回的字段名不一致,Mybatis不能自动封装,将会导致这些字段的值为null。

  • 解决方法如下:

    • SQL语句中,对不一样的列名起别名,别名和实体类属性名一样
    @Select("select id, username, password, name, gender, image, job, entrydate, " +
    "dept_id AS deptId, create_time AS createTime, update_time AS updateTime " +
    "from emp " + "where id=#{id}")
    public Emp getById(Integer id);
    
    • 手动结果映射:通过 @Results及@Result 进行手动结果映射
    @Results({@Result(column = "dept_id", property = "deptId"),
    @Result(column = "create_time", property = "createTime"),
    @Result(column = "update_time", property = "updateTime")})
    @Select("select id, username, password, name, gender, image, job,
    entrydate, dept_id, create_time, update_time from emp where id=#{id}")
    public Emp getById(Integer id);
    
    • 开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。但是要使用驼峰命名前提是实体类的属性与数据库表中的字段名严格遵守驼峰命名。
    # 在application.properties中添加:
    mybatis.configuration.map-underscore-to-camel-case=true
    

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

  • MyBatis使用RowBounds对象进行分页,它是针对ResultSet结果及执行的内存分页,而非物理分页。可以在SQL内直接拼写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页,例如在原有SQL后面拼写limit
  • 分页插件的基本原理是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的SQL,然后重写SQL,根据dialect方言,添加对应的物理分页语句和物理分页参数

10、Mybatis使用注解方式开发还是使用XML方式开发?

  • 使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能, 建议使用XML来配置映射语句。

11、什么是动态SQL?怎么用?

  • 动态SQL:SQL语句会随着用户的输入或外部条件的变化而变化, 比如只按照name 或者gender查询, 不一定每个字段都需要赋值。
  • 用于判断条件是否成立,如果条件为true,则拼接SQL 形式。
  • where元素只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的 AND或OR。
  • 动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)

12、XML映射文件中,除了常见的select、insert、update、delete标签外,还有哪些标签?

  1. :用于定义查询结果与Java对象的映射关系,可以自定义属性的映射关系,包括一对一、一对多等复杂映射关系
  2. :用于定义可重用的SQL片段,可以将SQL片段抽象出来,供多个SQL语句使用
  3. :用于包含其他XML文件中定义的SQL片段,可以实现SQL的复用
  4. :用于在SQL语句中添加条件判断,可以根据参数动态生成SQL语句
  5. :用于将多个条件组合成一个WHERE子句,避免生成无用的WHERE子句
  6. :用于遍历集合或数组,并将元素拼接成SQL语句中的IN子句
  7. :用于实现复杂的条件判断,类似Java中的if-else语句
  8. :用于对SQL语句进行字符串处理,如去除逗号、括号等

13、MyBatis实现一对一有几种方式?具体怎么操作的?

  1. 使用标签定义一对一映射关系

    • 例如,我们有一个Order类和一个User类,一个订单只属于一个用户,因此Order类中包含一个User对象作为属性。在XML映射问建中,我们可以定义一个标签,定义Order类与User类之间的映射关系
    <resultMap id="orderMap" type="Order">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="userId" column="user_id"/>
        <association property="user" javaType="User" select="getUserById"/>
    resultMap>
    
    • 在上面的标签中,我们使用标签定义Order类中的user属性与User类之间的映射关系。其中,property属性指定Order类中的user属性,javaType属性指定User类的类型,select属性指定通过getUserById查询用户信息的SQL语句。
  2. 使用嵌套查询

    • 使用嵌套查询可以在查询订单的同时查询用户信息,将查询结果组装成一个Order对象,例如我们在XML映射文件中定义以下SQL语句
    <select id="getOrderById" resultMap="orderMap">
        select o.id, o.name, o.user_id, u.username, u.phone, u.address
        from orders o
        join users u on o.user_id = u.id
        where o.id = #{id}
    select>
    
    • 在上面的SQL语句中,我们通过join语句关联了orders表和users表,并查询了订单和用户的信息。在