MyBatis高级面试题-2024

MyBatis的核心组件有哪些?

首先第一个是,SqlSessionFactory,它就像是一个会话工厂。它的任务是创建 SqlSession 对象,这个对象是我们与数据库交互的主要途径。SqlSessionFactory 的作用很重要,因为它可以帮我们配置数据库连接信息和事务管理等。一旦这个工厂被建立起来,它就会加载一些必要的配置和映射文件,为后续的数据库操作提供一个可靠的基础。

第二个是 SqlSession,可以理解为我们与数据库进行互动的窗口。通过它,我们能够执行 SQL 语句,提交或回滚事务,还可以获取 Mapper 接口的实例。不过需要注意的是,SqlSession 的生命周期是短暂的,通常在数据库操作完成后就应该关闭它,这样可以释放资源。

接下来是 Mapper 接口,这个概念有点像定义了一套数据库操作的规则。每个 Mapper 接口对应一个或多个映射文件,里面的方法定义了具体的 SQL 操作,比如插入、更新、删除和查询等。MyBatis 通过动态代理的方式,把接口方法和映射文件中的 SQL 语句关联起来,这样我们就可以方便地通过接口来执行数据库操作。

最后,是映射文件,它是一个用来连接 Java 对象和数据库表的桥梁。在映射文件里,我们可以定义 SQL 语句、参数映射、结果映射等等。里面的 SQL 语句可以包括增删改查等操作,MyBatis 会根据我们调用的方法来选择正确的 SQL 语句来执行。

MyBatis的工作流程是怎样的?

1、首先,你要配置 MyBatis,就是告诉它怎么干活。你得创建一个配置文件,里面得写上数据库怎么连接,还有映射文件在哪,还有其他的一些设置。

2、然后,你得写映射文件,这个东西定义了咱们的 Java 对象和数据库表是怎么对应的。就是告诉 MyBatis,怎么把查询结果映射到我们的 Java 对象里面去。

3、接下来,你得在 Java 代码里面弄一个数据访问的接口。这个接口里面定义了跟数据库打交道的方法,咱们在映射文件里面配置好对应的 SQL 语句。

4、咱们的应用启动的时候,MyBatis 会读咱们的配置文件和映射文件,然后它会用这些信息创建一个 SqlSessionFactory,这玩意挺重要的,是用来产生 SqlSession 实例的。

5、然后就是 SqlSession,咱们用 SqlSessionFactory 来创建这个东西。SqlSession 就是用来跑 SQL 操作的,你可以用它来查啊插入啊更新啊删除啊,啥都行。

6、然后,咱们就可以用 SqlSession 执行咱们之前写的数据访问接口里面的方法。MyBatis 会根据配置找到对应的 SQL 语句然后执行。

7、查询的结果会被 MyBatis 映射回咱们的 Java 对象里面。它会根据映射文件的设置,把查询结果的列和我们 Java 对象的属性对应起来,就是把数据变成对象。

8、最后别忘了,操作完了得关掉 SqlSession,这样数据库的连接等资源就能被释放了。

MyBatis一二级缓存的区别?

1、首先,咱们说说一级缓存。在同一个会话里,MyBatis 会自动开启一级缓存。这个缓存就是为了优化查询操作的速度。一旦你执行了一个查询,MyBatis 会把结果先存起来,这样下次再查询相同的数据的时候,它就可以直接从缓存里拿,不用再去数据库查询了。只要会话不结束,这个缓存就一直有效,只在这一个会话里起作用。

2、然后,咱们看看二级缓存。这个缓存是用来跨会话共享数据的。不同的会话也能分享同样的缓存数据,这就意味着可以减少数据库的访问次数。不过,二级缓存需要手动配置开启,然后它可以把数据存到更持久的存储地方,比如文件系统或者分布式缓存里。这样多个会话就可以共享同样的缓存数据了。

3、区别嘛,首先就是作用范围。一级缓存只在一个会话内部有效,而二级缓存可以在不同会话之间共享数据。其次,一级缓存默认就是开启的,不需要特别设置。但是二级缓存需要你手动配置才能生效。然后就是数据共享性,因为一级缓存只在会话内有效,所以不同的会话无法共享缓存数据。但是二级缓存可以让不同的会话共享数据,这可以减少数据库的访问次数。最后,缓存失效的机制也不一样。一级缓存在会话结束时会被清空,而二级缓存可以根据一些设置来失效和更新。

4、那总的来说,一级缓存适合在一个会话里共享数据,而二级缓存适合多个会话之间的数据共享。但是要记得,二级缓存需要手动设置才能使用哦。根据实际情况,我们可以选择使用不同的缓存级别。

MyBatis如何处理延迟加载?

所谓的延迟加载,其实就是一种优化方法,目标是为了在查数据库的时候,尽量不读取多余的数据,从而提高我们应用的表现和节约资源。在MyBatis里,这个延迟加载的技巧主要是用在处理对象关系映射的时候,也就是ORM。

来个例子帮你理解:假设有两张表,一张是订单表,另一张是商品表。每个订单下面可能有好几个商品。用延迟加载的话,当我们查一个订单的时候,MyBatis不会马上查出这个订单的所有商品,而是等到我们真的要用商品的数据时才去查。这样做就避免了在查订单的时候额外加载了一堆没用的商品。

用延迟加载有几个好处:

  1. 性能更好:特别是在对象关系复杂或者数据多的情况下,延迟加载可以减少一次性加载大量数据,让查询更快,应答更快。

  2. 省资源:不一开始就加载所有关联数据,可以按需加载,节约了内存和网络资源。

  3. 避免不必要的查:如果有些关联数据在当前情境下用不上,用延迟加载就能避免没必要的数据库查,不累积数据库的负担。

但要注意,虽然延迟加载能提升性能,可别用得过了,免得碰上懒加载的N+1问题,就是要查很多次才能拿到关联数据,结果性能就拖垮了。所以用延迟加载的时候,得根据实际情况合理配置和使用。

总之,MyBatis的延迟加载能帮助优化数据库查询,不读取不必要的数据,提升应用的响应速度,资源利用也更高。

MyBatis中XML映射有哪些标签?

除了常见的select、insert、update和delete标签,MyBatis的XML映射文件中还有一些其他标签用于更复杂的操作和配置。以下是一些常见的额外标签:

  1. resultMap: 用于定义查询结果与Java对象之间的映射关系,可以在多个查询中重复使用。

  2. association和collection: 用于在resultMap中定义关联关系,用于处理一对一和一对多的关系。

  3. discriminator: 在resultMap中使用,根据不同的条件选择不同的映射规则,用于处理继承关系的映射。

  4. sql: 可以定义可重用的SQL片段,然后在其他地方引用。主要用于减少重复编写SQL语句。

  5. include: 用于在SQL语句中引入外部定义的SQL片段,提高可维护性。

  6. if、choose、when、otherwise: 用于在SQL语句中进行条件判断和逻辑控制,用于动态SQL的构建。

  7. trim、where、set: 用于在SQL语句中添加固定的SQL片段,如where和set关键字,用于动态的条件构建。

  8. foreach: 用于在SQL语句中进行集合迭代,适用于生成IN语句等。

  9. bind: 用于在SQL语句中声明并绑定一个变量,可以在查询中重复使用。

  10. cache: 用于配置二级缓存。

  11. selectKey: 用于在插入操作后获取生成的主键值。

  12. insert、update、delete的flushCache、useGeneratedKeys、keyProperty属性: 用于配置插入、更新和删除操作的一些属性。

这些标签和属性可以让你更灵活地配置和使用MyBatis,从而适应不同的需求和场景。

MyBatis如何实现动态数据源切换?

1、在实现动态数据源切换方面,MyBatis有几种方法,让你能够在不同的数据库之间轻松切换。比如,你可能会在开发环境和生产环境中使用不同的数据库。下面是一些可以考虑的方法:

2、首先,我们可以通过配置文件来实现切换。具体来说,你可以在MyBatis的配置文件里配置多个数据源,然后根据需要在代码中进行切换。这就涉及到定义多个数据源的连接信息和配置,然后在代码里通过指定数据源的标识来选择要使用哪个数据源。这种方法需要在配置文件中进行一些准备工作,但切换过程相对比较容易。

3、其次,我们可以运用AOP切面编程来实现切换。通过使用面向切面编程(AOP),你可以在方法调用之前进行拦截,然后根据条件来动态地切换数据源。你可以创建一个切面,将切入点设定为需要切换数据源的方法,然后在切面中实现数据源切换的逻辑。这样的做法能够将切换逻辑和业务逻辑分隔开,有助于提高代码的可维护性。

4、另外,我们还可以使用MyBatis提供的AbstractRoutingDataSource类。这个类允许你创建一个数据源路由器,根据特定的规则来选择数据源。你可以继承这个类,然后实现其中的determineCurrentLookupKey()方法,以返回当前应该使用的数据源标识。这种方式非常灵活,可以根据不同的条件来切换数据源。

5、当然,还有一种方式是使用第三方库。除了上述提到的方法,还有一些第三方库可以帮助你实现动态数据源切换,例如Druid和HikariCP等。这些库通常提供了更多的功能和配置选项,可以根据实际需求来选择合适的库。

6、总体来说,MyBatis为实现动态数据源切换提供了多种方法,你可以根据项目的具体需求和复杂程度来选择适合的方法。无论选择哪种方法,都要确保在切换数据源时考虑到线程安全性和性能等因素。希望这些解释对你有帮助!

MyBatis如何处理懒加载和预加载?

当谈到MyBatis中的懒加载和预加载时,我们实际上在讨论在获取数据库数据时如何处理关联对象的加载方式。
**懒加载:**是一种延迟加载技术,它在需要访问关联对象的时候才会加载相关数据。这意味着,当你从数据库中获取一个主对象时,它的关联对象并不会立即加载到内存中,只有当你实际调用访问关联对象的方法时,MyBatis才会去数据库中加载并填充这些关联对象的数据。懒加载适用于关联对象较多或者关联对象数据较大的情况,这样可以减少不必要的数据库查询,提升性能。
**预加载:**则是一种在获取主对象时同时加载其关联对象的技术。这样一来,当你获取主对象时,它的所有关联对象也会被一并加载到内存中,避免了多次数据库查询。预加载适用于你确定在后续使用中肯定会访问关联对象,这样可以减少每次访问关联对象时的延迟。

选择懒加载还是预加载取决于你的具体需求和场景。如果你希望在尽量少的数据库查询次数下获取数据,懒加载是个不错的选择。如果你在获取主对象后会频繁地访问其关联对象,预加载可能更适合,因为它可以减少多次查询带来的性能开销。

两者都是优化数据库访问性能的手段,根据具体的使用场景选择合适的加载方式非常重要。

能说说MyBatis的工作原理吗?

MyBatis的工作原理:

  1. 配置文件 :MyBatis需要一个XML配置文件,叫做mybatis-config.xml,用于定义数据源、事务管理以及其他一些设置。
  2. SQL映射文件:为了告诉MyBatis如何映射SQL查询到我们的对象或Java Beans,我们需要定义另一些XML文件。这些文件里,我们会写SQL语句,并定义输入和输出。
  3. SqlSessionFactory:当MyBatis初始化时,它会根据上面提到的配置文件创建一个SqlSessionFactory。这个工厂只会被创建一次,然后被用来生产SqlSession,这些SqlSession是应用中真正做数据库操作的对象。
  4. SqlSession :这是MyBatis的一个关键组件。每当我们想和数据库进行交互时,我们就从SqlSessionFactory那里拿到一个SqlSession。这个会话包含了所有执行SQL的方法,比如insert, update, delete, select等。
  5. 映射器:为了使代码更整洁,我们经常使用接口来代表SQL映射。这些接口的方法对应了之前在XML映射文件中定义的SQL语句。这样,我们就可以像调用普通的Java方法那样执行SQL语句了。

那么,当你在应用中调用一个映射器方法时,这里发生了什么?

  1. MyBatis会找到对应的SQL语句。
  2. 使用给定的参数,MyBatis会为这条SQL语句创建一个PreparedStatement。
  3. 执行这个PreparedStatement。
  4. 如果这是一个查询操作,MyBatis会将查询结果映射到Java对象或集合中。
  5. 最后,返回这个结果。

这就是MyBatis的基本工作原理。使用MyBatis,我们可以更方便地与数据库交互,同时避免大部分重复和模板化的代码。

讲讲Mybatis 的一级、二级缓存

一级缓存(本地缓存):

  1. 作用范围: 一级缓存是在SqlSession的生命周期内有效,也就是说,每个SqlSession拥有独立的一级缓存。
  2. 默认开启: 一级缓存在MyBatis中默认是开启的,无需额外配置。
  3. 特点: 当执行查询操作时,查询的结果会被缓存在当前SqlSession中。如果再次执行相同的查询,MyBatis会首先尝试从缓存中获取数据,而不再访问数据库。
  4. 自动刷新: MyBatis会在执行insert、update、delete等写操作时自动清空一级缓存,以保持数据的一致性。

二级缓存(全局缓存):

  1. 作用范围: 二级缓存是在多个SqlSession之间共享的,即多个SqlSession可以共享同一个二级缓存。
  2. 配置开启: 二级缓存需要手动配置开启,需要在映射文件的<mapper>标签下添加<cache>元素。

<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>

3.特点: 二级缓存能够跨SqlSession共享查询结果,有效减少数据库访问次数。它的数据存储在全局范围的缓存中,可以由多个SqlSession访问。

4.缓存策略: 你可以根据需求选择不同的缓存策略(例如LRUFIFO等),以及配置缓存的大小、刷新间隔等参数。

5.注意事项: 二级缓存可以缓存的对象需要是可序列化的,要确保对象可以正确地序列化和反序列化。另外,对于关联数据的更新操作,需要手动清除相关的二级缓存,以避免脏数据的问题。

需要注意的是,虽然缓存可以提高查询性能,但不合理的使用缓存可能导致数据不一致等问题,特别是在分布式环境下。因此,在使用缓存时需要根据业务需求和性能测试结果进行合理的配置和管理。

如何在 MyBatis 中进行分页查询

基本的分页查询:

MyBatis 提供了一个简单的方式来实现分页查询,主要涉及到两个参数:offsetlimitoffset 表示从结果集的哪一行开始取数据,而 limit 则表示每页显示多少条数据。在 SQL 语句中,你可以使用类似于 LIMIT offset, limit 的语法来实现分页查询。

<select id="getPagedData" resultType="YourResultType">
  SELECT *
  FROM your_table
  LIMIT #{offset}, #{limit}
</select>

在 Java 代码中,你需要为 offsetlimit 参数提供值。offset 可以通过公式 (pageNum - 1) * pageSize 计算得出,其中 pageNum 表示页码,从 1 开始,pageSize 表示每页显示的记录数。

常用的分页插件和技巧:

  1. PageHelper 插件: PageHelper 是一个流行的 MyBatis 分页插件,它简化了分页查询的操作。你只需要在查询方法前调用 PageHelper.startPage(pageNum, pageSize),然后执行查询语句,PageHelper 就会自动处理分页逻辑。
  2. 使用 RowBounds: 在 MyBatis 中,你还可以使用 RowBounds 对象来实现分页查询。通过在查询方法中传递一个 RowBounds 对象,你可以指定从哪一行开始取数据,以及每页显示多少条数据。
  3. 自定义分页插件: 如果你有特殊的分页需求,你可以编写自己的分页插件。这可能涉及到在 MyBatis 的拦截器链中插入你自己的逻辑,以实现定制化的分页处理。

总之,MyBatis 中的分页查询并不复杂,你可以选择基本的分页方式,也可以利用一些分页插件和技巧来简化操作。记得考虑好性能问题,避免在分页查询时产生过多的数据库查询操作。

MyBatis的插件能够在哪些地方进行拦截?

  1. Executor(执行器)层面的拦截: 这是SQL语句的执行层面,插件可以在SQL语句执行前后进行拦截。这包括了SQL的预处理、参数设置、查询结果的映射等。
  2. StatementHandler(语句处理器)层面的拦截: 这是对SQL语句的处理层面,插件可以在SQL语句被执行之前进行拦截,你可以在这里修改、替换、生成SQL语句。
  3. ParameterHandler(参数处理器)层面的拦截: 这是处理参数的层面,插件可以在参数传递给SQL语句之前进行拦截,你可以在这里修改参数值。
  4. ResultSetHandler(结果集处理器)层面的拦截: 这是处理查询结果的层面,插件可以在查询结果返回给调用方之前进行拦截,你可以在这里对查询结果进行修改、处理。

实际上,使用插件可以实现许多功能,下面是一些使用插件实现的实际场景示例:

  1. 日志记录: 创建一个插件,拦截Executor层的SQL执行,记录每个SQL语句的执行时间、执行情况等,以便于性能分析和故障排查。
  2. 分页支持: 编写一个拦截器,在StatementHandler层拦截SQL语句,根据传入的分页参数,动态修改SQL语句以实现数据库分页查询。
  3. 权限控制: 开发一个插件,拦截StatementHandler层的SQL执行,根据当前用户的权限动态添加查询条件,确保用户只能访问其有权限的数据。
  4. 二级缓存扩展: 创建一个插件,在ResultSetHandler层拦截查询结果,对结果进行加工处理,然后再将处理后的结果放入二级缓存,提供更加定制化的缓存机制。
  5. 自动填充字段: 编写一个拦截器,在ParameterHandler层拦截参数设置,根据需要自动填充一些字段,比如创建时间、更新时间等。

这些只是一些示例,MyBatis插件的应用非常灵活,你可以根据自己的项目需求来编写插件,实现各种定制化的功能。插件的核心思想是在关键点拦截MyBatis的执行流程,从而实现额外的功能或者改变默认行为。

如何编写一个MyBatis插件?

首先,你需要实现一个MyBatis的拦截器(Interceptor)。一个拦截器需要实现MyBatis的Interceptor接口,其中最重要的是intercept方法,它会在执行SQL语句前后被调用。

import org.apache.ibatis.executor.statement.StatementHandler;

import org.apache.ibatis.plugin.*;

import java.sql.Statement;

import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, org.apache.ibatis.session.ResultHandler.class})})

public class LoggingInterceptor implements Interceptor {

@Override

public Object intercept(Invocation invocation) throws Throwable {

System.out.println("Before executing query...");

Object result = invocation.proceed(); // 执行原来的方法

System.out.println("After executing query...");

return result;

}

@Override

public Object plugin(Object target) {

return Plugin.wrap(target, this);

}

@Override

public void setProperties(Properties properties) {

// 可以在这里设置一些属性

}

}

在这个例子中,LoggingInterceptor实现了Interceptor接口,重写了intercept方法,在执行查询SQL语句前后打印日志。

接下来,你需要在MyBatis的配置文件中注册这个插件:

<plugins>

<plugin interceptor="com.example.LoggingInterceptor">

<!-- 这里是插件的属性配置,如果有的话 -->

</plugin>

</plugins>

在配置中,interceptor属性指定了插件的完全限定名,即LoggingInterceptor的类名。你还可以在插件标签内设置插件的属性,这些属性会在插件的setProperties方法中被接收。

最后,当你执行查询操作时,插件会自动拦截并执行你在intercept方法中定义的逻辑。

需要注意的是,这只是一个简单的插件示例。MyBatis插件可以实现更复杂的逻辑,比如性能分析、自定义SQL改写等。编写插件时要确保逻辑正确,不影响系统稳定性和性能。

简述 Mybatis 的插件运行原理

插件机制的核心是Interceptor接口,你可以实现这个接口,编写自己的插件逻辑。一个插件主要包括以下几个步骤:

  1. 实现Interceptor接口: 创建一个类,实现MyBatis提供的Interceptor接口,该接口包含了interceptplugin两个方法。
  2. 实现intercept方法: intercept方法是插件的核心,它会在方法执行前后进行拦截。你可以在这个方法中编写自己的逻辑。
  3. 实现plugin方法: plugin方法用于创建代理对象,将插件包装在目标对象上,使得插件逻辑能够被执行。
  4. 配置插件: 在MyBatis的配置文件中,通过<plugins>标签配置你的插件。通常需要指定插件类和一些参数。

以下是一个简单的插件示例:

public class MyPlugin implements Interceptor {

@Override

public Object intercept(Invocation invocation) throws Throwable {

// 在方法执行前进行逻辑处理

System.out.println("Before executing method: " + invocation.getMethod().getName());

Object result = invocation.proceed(); // 调用原始方法

// 在方法执行后进行逻辑处理

System.out.println("After executing method: " + invocation.getMethod().getName());

return result;

}

@Override

public Object plugin(Object target) {

// 创建代理对象,将插件包装在目标对象上

return Plugin.wrap(target, this);

}

@Override

public void setProperties(Properties properties) {

// 设置插件的属性,可以在配置文件中配置

}

}

Mybatis 是如何进行分页的?

MyBatis的分页原理

  1. 数据库方言(Dialect):不同的数据库(如MySQL、Oracle、SQL Server等)在分页查询语法上有所不同。MyBatis并不直接支持所有数据库的分页语法,而是通过数据库方言来处理。数据库方言是一个抽象层,它根据数据库类型生成相应的分页查询语句。
  2. 参数传递:在查询方法中,你可以传递分页相关的参数,如页码(pageNumber)和每页条数(pageSize)。
  3. 分页处理 :MyBatis会根据数据库方言生成合适的分页查询语句,然后将查询结果返回给调用者。通常,MyBatis会添加类似LIMIT(对于MySQL)或ROWNUM(对于Oracle)等语句来限制返回的结果行数。

分页插件的原理

分页插件是一种扩展机制,它允许MyBatis在查询过程中,自动应用分页逻辑而不需要手动编写分页查询语句。分页插件的一般原理如下:

  1. 拦截器(Interceptor):分页插件实际上是MyBatis的一个拦截器,它可以在查询被执行之前或之后进行干预。
  2. 处理分页逻辑:在查询执行之前,分页插件会检测是否有分页参数传入。如果有分页参数,插件会根据数据库方言生成适当的分页查询语句。
  3. 修改查询参数:插件会修改查询的SQL语句,添加分页的限制条件。同时,它还会修改参数对象,将分页参数替换为实际的分页偏移量(offset)和每页条数(limit)。
  4. 执行查询:修改后的查询语句被执行,得到查询结果。
  5. 封装分页结果:插件会根据查询结果和分页参数,将查询结果进行切割,得到分页后的结果。
相关推荐
duration~4 分钟前
SpringAOP模拟实现
java·开发语言
小码ssim11 分钟前
IDEA使用tips(LTS✍)
java·ide·intellij-idea
一条晒干的咸魚12 分钟前
【Web前端】实现基于 Promise 的 API:alarm API
开发语言·前端·javascript·api·promise
就爱六点起30 分钟前
C/C++ 中的类型转换方式
c语言·开发语言·c++
我明天再来学Web渗透31 分钟前
【SQL50】day 2
开发语言·数据结构·leetcode·面试
猫猫的小茶馆33 分钟前
【C语言】指针常量和常量指针
linux·c语言·开发语言·嵌入式软件
潜洋41 分钟前
Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序
java·spring boot·后端
暮志未晚Webgl1 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
DanielYQ1 小时前
LCR 001 两数相除
开发语言·python·算法
yngsqq1 小时前
037集——JoinEntities连接多段线polyline和圆弧arc(CAD—C#二次开发入门)
开发语言·c#·swift