1.MyBatis 中 #{}和 ${}的区别是什么?
#{} 是预编译的占位符,MyBatis会将其转化为一个占位符参数,安全性较高,可以防止 SQL注入; ${} 是字符串替换,直接将内容替换到SQL语句中,不会进行参数处理,潜在风险是 SQL注入
在 MyBatis 中,#{} 和 ${} 都被用作参数绑定,但它们在处理参数的方式和安全性上有很大的区别:
${}
:这是一个基本的字符串替换操作。比如你在 SQL 语句中写${column}
,MyBatis 就会直接把这个${column}
替换成参数的值。由于它的这种直接替换特性,它可能会引起 SQL 注入问题。比如,如果用户恶意设置参数的值为一个 SQL 语句片段,那么他就可以通过这种方式执行任意的 SQL 语句,这就造成了严重的安全问题。因此,我们需要非常小心地使用${}
,尽量避免在不受信任的输入或用户控制的输入上使用它。#{}
:MyBatis 会使用预编译语句(PreparedStatement)的方式,通过在 SQL 中构建一个参数占位符?并设值参数来防止 SQL 注入,来安全地处理#{}
中的值。这也是 MyBatis 推荐的方式来处理动态参数。你可以在查询中写#{userId}
,然后在你的参数映射中有一个userId的字段,MyBatis 就会安全地为你的 SQL 语句设值。
总结一下,#{}
比${}
更安全,因为它通过预编译的方式处理参数,可以防止 SQL 注入。除非在你完全确定没有 SQL 注入风险的情况下,一般情况下我们更推荐使用#{}
。
2.MyBatis 有几种分页方式?
MyBatis有两种分页方式,一种是使用 RowBounds 进行内存分页,另一种是使用插件进行物理分页。
RowBounds 是 MyBatis 中的一个分页对象,它可以将所有符合条件的数据全都查询到内存中,然后在内存中对数据进行分页。然而,这种分页操作是对 ResultSet 结果集进行分页,也就是人们常说的逻辑分页,而非物理分页,效率低下,不建议使用。
3.RowBounds 是一次性查询全部结果吗?为什么?
是的, RowBounds 方式是一次性查询全部结果,MyBatis会将整个结果集读取到内存中, 然后进行分页操作。
Mybatis可以通过传递RowBounds对象,来进行数据库数据的分页操作,然而遗憾的是,该分页操作是对ResultSet结果集进行分页,也就是人们常说的逻辑分页,而非物理分页(物理分页当然就是我们在sql语句中指定limit和offset值)。
4.MyBatis 逻辑分页和物理分页的区别是什么?
逻辑分页是在数据库中查询所有结果,然后在应用层进行分页;物理分页是通过数据库的特 定语法(如 LIMIT 、 OFFSET )进行分页,只查询所需数据。
逻辑分页是在应用程序层面进行的,它首先从数据库查询出所有的结果,然后在应用程序中对这些结果进行分页显示。这种方法的优点是简单易用,适用于数据量较小的情况。然而,如果数据量很大,查询所有的数据可能会消耗大量的内存和时间。
物理分页是利用数据库的特定语法(如SQL中的LIMIT和OFFSET)进行的,这意味着数据库直接返回所需的数据记录,而不是所有的数据。这种方法的优点是效率高,适用于处理大量数据。然而,使用不同的数据库系统可能会对LIMIT和OFFSET等关键字的支持程度不同,因此可能需要进行一些调整。
5.MyBatis 是否支持延迟加载?延迟加载的原理是什么?
是的,MyBatis支持延迟加载。延迟加载是指在需要时才真正查询关联对象,原理是在查询 主对象时只查询主对象的数据,当访问关联对象属性时,再根据需要进行关联查询。
MyBatis 支持延迟加载。延迟加载(Lazy Loading)是一种常用的优化技术,用于在需要时才加载数据。在 MyBatis 中,可以通过配置来实现延迟加载。
延迟加载的原理是,在初始加载时,只加载必要的少量数据,然后在需要的时候才加载更多的数据。这样可以减少初始加载的时间和内存消耗,提高应用的启动速度和响应速度。
在 MyBatis 中,可以通过配置 fetchType
属性来实现延迟加载。例如,可以将 fetchType
设置为 lazy
来实现延迟加载。当设置为 lazy
时,MyBatis 只会加载与当前查询结果相关的数据,而不是一次性加载所有数据。
延迟加载的原理是基于对象代理实现的。在 MyBatis 中,可以通过配置 <association>
和 <collection>
元素来实现对象代理。当配置了这些元素后,MyBatis 会为相关对象生成代理对象,从而实现在需要时才加载数据的效果。
需要注意的是,延迟加载可能会增加数据库的查询次数,从而影响应用的性能。因此,在使用延迟加载时,需要根据实际情况进行权衡和优化。
6.说一下 MyBatis 的一级缓存和二级缓存?
一级缓存是指在同一个SqlSession中,查询的结果会被缓存起来,以提高性能;二级缓存是 指多个SqlSession之间共享缓存,可以跨Session共享数据。
MyBatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 消除了几乎所有的 JDBC 代码和参数的手动设置以及结果集的检索。
在 MyBatis 中,一级缓存和二级缓存是两个重要的概念。
- 一级缓存:也称为 SqlSession 级别的缓存。每一个新的 SqlSession 有自己的一级缓存,当 SqlSession 进行查询时,会先从一级缓存中查找是否已经存在相应的查询结果,如果存在,则直接返回;如果不存在,再去数据库进行查询,并将查询结果存入一级缓存。一级缓存的生命周期和 SqlSession 保持一致,当 SqlSession 被关闭或清空,那么一级缓存就会消失。
- 二级缓存:也称为 Mapper 级别的缓存。二级缓存是多个 SqlSession 共享的,因此,不同的 SqlSession 可以访问、修改和更新二级缓存中的数据。二级缓存的数据会持久化到磁盘中,因此可以跨多个 SqlSession 和多个会话对象进行共享。
MyBatis 的二级缓存可以跨多个 SqlSession,因此它可以在多个数据库之间进行数据共享。然而,需要注意的是,由于二级缓存的数据是共享的,因此如果一个 SqlSession 对数据进行修改,那么这个修改会反映到其他所有的 SqlSession 中。为了避免这种情况,可以在进行修改操作时关闭二级缓存。
总的来说,一级缓存和二级缓存都是 MyBatis 中非常重要的特性,它们可以提高数据库查询的效率,减少对数据库的访问次数。然而,在使用它们时需要注意数据的共享性和一致性问题。
7.MyBatis 和 Hibernate 的区别有哪些?
MyBatis是基于SQL和映射配置的持久化框架,需要手写SQL,更加灵活;Hibernate是 ORM框架,将Java对象映射到数据库,不需要写SQL,更加面向对象。
MyBatis和Hibernate是两种广泛使用的Java持久化框架,它们的主要区别体现在以下方面:
- 原理不同:MyBatis是一种基于SQL语句的持久化框架,通过预编译SQL语句并执行来实现数据库访问。而Hibernate是一种基于对象的持久化框架,通过将Java对象映射到数据库表来实现数据库访问。
- 使用方式不同:MyBatis的使用方式更加灵活,允许用户编写自己的SQL语句并执行,支持多种数据库查询方式(如普通查询、存储过程等)。Hibernate也有自己的SQL书写方式,但相较于MyBatis,Hibernate更加注重映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
- 开发难度不同:总体来说,Hibernate的开发难度要大于Mybatis。这主要是因为Hibernate比较复杂、庞大,学习周期较长。而Mybatis相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。
- 日志统计和数据库扩展性:Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。不过没有自己的日志统计,所以要借助log4j来记录日志。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。不过Hibernate具有自己的日志统计。另外,Mybatis由于所有SQL都是依赖数据库书写的,所以扩展性、迁移性比较差。
综上所述,MyBatis和Hibernate各有其优点和缺点。在选择使用时,需要根据具体的项目需求和个人经验进行判断。
8.MyBatis有哪些执行器(Executor)?
MyBatis有三种执行器,分别是 SimpleExecutor 、 ReuseExecutor 和 BatchExecutor , 用于控制SQL语句的执行。
MyBatis 在其内部实现中使用了三种类型的执行器(Executor),分别是 SimpleExecutor,ReuseExecutor 和 BatchExecutor。
- SimpleExecutor:这是一个简单的执行器,每次SQL语句都会重新生成SQL,并且会创建新的结果映射器(Mapper)对象,对于一些非常简单不需要映射的操作比较有用。
- ReuseExecutor:这是MyBatis默认的执行器,它会重用预处理语句(PreparedStatement)和结果映射器(Mapper)对象,减少了创建和销毁对象的次数,提高了性能。
- BatchExecutor:这是批量执行器,它主要用于执行批量操作,可以一次执行多条SQL语句。
9.MyBatis 分页插件的实现原理是什么?
MyBatis分页插件通过拦截SQL执行,将原始SQL改写为带有分页参数的SQL,然后执行修改后的SQL,最终返回分页结果。
MyBatis 分页插件的实现原理主要是利用了 MyBatis 的插件机制。分页插件通过拦截 MyBatis 的执行语句,在 SQL 执行前后进行相应的操作,以达到分页的效果。
具体来说,分页插件首先通过实现 MyBatis 的插件接口,如 Interceptor
,来拦截待执行的 SQL 语句。在拦截到 SQL 语句后,插件会根据 SQL 语句的内容和参数,生成对应的物理分页语句和物理分页参数。然后,插件会将这些物理分页语句和参数插入到原始 SQL 语句中,并继续执行 SQL 语句。
在执行物理分页语句时,插件会根据数据库方言(dialect)的不同,使用不同的物理分页语法。例如,对于 MySQL 数据库,可以使用 LIMIT
和 OFFSET
关键字;对于 Oracle 数据库,可以使用 ROWNUM
关键字等。
另外,为了提高性能,分页插件通常会缓存已经执行过的 SQL 语句和对应的物理分页语句和参数。这样,在下次执行相同 SQL 语句时,可以直接使用缓存的物理分页语句和参数,避免了重复的生成和执行过程。
需要注意的是,虽然物理分页可以提高查询效率,但也可能会增加数据库的负载。因此,在使用物理分页时,需要根据具体的应用场景和数据库性能来权衡选择。
10.MyBatis 如何编写一个自定义插件?
编写MyBatis自定义插件需要实现 Interceptor 接口,然后通过在MyBatis配置文件中配置 插件,使其生效。插件可以在SQL执行前后进行拦截,实现自定义的功能。
MyBatis 允许用户编写自定义的插件,以便对 SQL 语句进行更细粒度的控制和扩展。以下是一个简单的 MyBatis 自定义插件的示例:
-
首先创建一个新的 Java 类,并让它实现
Interceptor
接口:import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.plugin.*; import java.sql.Connection; import java.util.Properties; @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); // 这里可以对 SQL 语句进行修改或扩展 // ... // 继续执行原始操作 return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { // 可以设置自定义的属性 // ... } }
-
在
intercept
方法中,你可以通过StatementHandler
对象获取到 SQL 语句,然后对 SQL 语句进行修改或扩展。这里是一个简单的示例,它将 SQL 语句中的所有SELECT
关键字替换为SELECT * FROM
:
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
String sql = statementHandler.getBoundSql().getSql();
String newSql = sql.replace("SELECT", "SELECT * FROM");
// 设置新的 SQL 语句到 StatementHandler 中,以便后续执行
((BoundSql) statementHandler.getBoundSql()).setSql(newSql);
// 继续执行原始操作
return invocation.proceed();
}
原文地址:https://blog.csdn.net/g877835148/article/details/134636822
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_201.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!