Java Mybatis

1.MyBatis 中 #{}和 ${}的区别是什么?
答案: #{} 是预编译的占位符,MyBatis会将其转化为一个占位符参数,安全性较高,可以防止SQL注入; ${} 是字符串替换,直接将内容替换到SQL语句中,不会进行参数处理,潜在风险是SQL注入。
---------------------------------------------------------------------------------------
在MyBatis中,#{}和${}是两种常用的参数传递方式,用于在SQL语句中引用参数值。它们之间的主要区别如下:
​
#{}:被#{}包围的参数会被预编译处理,将参数值以安全的方式插入到SQL语句中,可以有效防止SQL注入攻击。#{}会将参数值转义处理,并将其作为预编译参数传递给数据库。
​
示例:SELECT * FROM users WHERE age > #{age}
​
在执行时,会将实际的参数值填充到SQL语句中,例如:SELECT * FROM users WHERE age > 18
​
使用#{}还可以指定一些附加属性,如:#{age, jdbcType=INTEGER},用于精确控制参数值的类型转换。
​
:被{}包围的参数会被直接拼接到SQL语句中,不进行预编译处理。这种方式可以用于动态生成SQL语句中的表名、列名等。
​
示例:SELECT * FROM ${tableName}
​
在执行时,会将实际的参数值直接拼接到SQL语句中,例如:SELECT * FROM users
​
注意:使用${}时,需要注意安全性和潜在的SQL注入风险。因为参数值会直接拼接到SQL语句中,所以需要确保参数值的来源可信且合法,避免被恶意输入的参数值造成安全问题。
​
综上所述,#{}用于传递参数值并进行预编译处理,适用于大部分参数传递的场景,更安全;而${}直接将参数值拼接到SQL语句中,适用于动态生成SQL语句的场景,但需要注意安全性。选择使用哪种方式,取决于具体的使用场景和安全性要求。
2.MyBatis 有几种分页方式?
答案:MyBatis有两种分页方式,一种是使用 RowBounds 进行内存分页,另一种是使用插件进行 物理分页。
---------------------------------------------------------------------------------------
MyBatis提供了多种分页方式,可以根据具体的需求选择适合的方式。以下是常用的MyBatis分页方式:
​
基于数据库的分页:使用数据库的特定语法(如MySQL的LIMIT、Oracle的ROWNUM)进行分页。在SQL语句中直接指定分页的起始位置和每页的记录数。
​
示例:MySQL的分页语句 SELECT * FROM users LIMIT #{offset}, #{pageSize}
​
这种方式简单直接,但依赖于具体的数据库语法,在切换数据库时需要注意语法的兼容性。
​
使用物理分页插件:通过使用第三方插件实现分页功能,如MyBatis-Paginator、PageHelper等。这些插件通常提供了更丰富的分页功能,可以自动构建分页SQL语句,支持各种数据库。
​
示例:使用PageHelper插件实现分页
​
添加依赖:compile 'com.github.pagehelper:pagehelper:版本号'
​
配置插件:
​
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="dialect" value="数据库方言"/>
    </plugin>
</plugins>
在查询方法中使用分页:
​
PageHelper.startPage(pageNum, pageSize);
List<User> userList = userDao.getUserList();
PageInfo<User> pageInfo = new PageInfo<>(userList);
物理分页插件提供了更灵活和易于使用的分页功能,可以自动进行分页查询,包括计算总记录数、处理排序等。
​
自定义分页:通过编写自定义的分页逻辑实现分页功能。可以在查询前先查询总记录数,并根据总记录数和分页参数进行分页计算,手动截取查询结果。
​
示例:
​
// 查询总记录数
int total = userDao.getUserCount();
​
// 计算分页参数
int offset = (pageNum - 1) * pageSize;
int limit = pageSize;
​
// 执行分页查询
List<User> userList = userDao.getUserListByPage(offset, limit);
这种方式相对较为手动和繁琐,但在一些特殊场景下可能会有更好的控制和优化能力。
​
以上是常见的MyBatis分页方式,可以根据实际需求选择适合的方式进行分页操作。
3.RowBounds 是一次性查询全部结果吗?为什么?
答案:RowBounds 方式不是一次性查询全部结果,MyBatis会将查询到的结果集读取到内存中,然后进行分页操作。
---------------------------------------------------------------------------------------
RowBounds 不是一次性查询全部结果,它只查询满足条件的部分结果。
​
RowBounds 是 MyBatis 提供的一种分页查询方式,主要用于查询大数据量的场景。在使用 RowBounds 进行分页查询时,实际上相当于先查询出完整的结果集,然后通过 RowBounds 对结果集进行分页处理,返回指定范围内的结果。
​
具体来说,当调用分页查询方法时,MyBatis 会首先执行完整的 SQL 查询语句,将结果集返回给 Java 程序,然后再使用 RowBounds 对结果集进行分页处理。因此,只有满足分页条件的部分结果会被返回,不会一次性查询全部的结果集。
​
值得注意的是,由于 RowBounds 是通过全量查询的方式获取数据的,因此对于数据量较大的表格,使用 RowBounds 进行分页查询可能会导致性能问题,因此在进行大数据量的分页查询时,建议使用以下两种方式之一:
​
对数据进行分段查询,即只查询分页所需的数据,而不使用 RowBounds 对完整的结果集进行分页处理。
​
使用数据库自身提供的分页功能,如 MySQL 的 LIMIT 语句或 Oracle 的 ROWNUM 特性,将分页操作下推至数据库内部,优化分页查询的性能。
4.MyBatis 逻辑分页和物理分页的区别是什么?
答案:逻辑分页是在数据库中查询所有结果,然后在应用层进行分页;物理分页是通过数据库的特 定语法(如LIMIT 、 OFFSET )进行分页,只查询所需数据。
---------------------------------------------------------------------------------------
MyBatis 的逻辑分页和物理分页的区别主要在查询方式和数据来源上。
​
逻辑分页是在查询结果之后进行的分页,即查询完整结果集之后,再对结果集进行分页处理,只返回指定页码和页大小的结果。逻辑分页是在代码层面实现的,与数据库本身无关。比较常见的逻辑分页方式有使用 RowBounds 分页和使用 limit offset 分页。
​
物理分页则是在 SQL 查询过程中进行分页处理,基于具体数据库的分页功能实现。物理分页让数据库分页查询结果,并直接返回指定页码和页大小的数据,减轻了应用程序的负担。比较常见的物理分页方式有使用 limit offset 分页和使用数据库特有的分页功能,如 MySQL 的 LIMIT 语句和 Oracle 的 ROWNUM 特性等。
​
因为逻辑分页是在代码层面实现的,所以查询渲染方式不会影响它的实现。而物理分页则需要依赖数据库的分页查询功能,这可能会导致在不同数据库之间使用物理分页时,查询语句结构、查询速度和执行结果都会有所不同。除了这些区别外,在大数据量情况下二者的性能差异也可能比较明显,因为物理分页可以减少Java 程序对完整的数据集的处理,而逻辑分页则必须要查询完整的结果集。
5.MyBatis 是否支持延迟加载?延迟加载的原理是什么?
答案:是的,MyBatis支持延迟加载。延迟加载是指在需要时才真正查询关联对象,原理是在查询 主对象时只查询主对象的数据,当访问关联对象属性时,再根据需要进行关联查询。
---------------------------------------------------------------------------------------
是的,MyBatis 支持延迟加载(Lazy Loading)。
​
延迟加载是一种延迟获取关联对象数据的加载方式。在使用延迟加载时,如果一个对象关联了其他对象,并且这些关联对象在查询时并不是立即需要的,那么这些关联对象的数据就可以在后续需要时再进行加载。
​
延迟加载的原理是通过使用代理对象来延迟加载关联对象的数据。在查询主对象时,MyBatis 实际上并没有立即查询关联对象的数据,而是为关联对象生成一个代理对象。当真正访问关联对象时,代理对象会触发加载关联对象的数据,再返回给用户。这样就可以在需要关联对象数据时进行查询,而不是一次性加载所有关联对象的数据。
​
延迟加载可以有效地解决 N+1 查询问题,即在查询主对象列表时,如果同时查询关联对象,就会发起大量的单独查询请求,导致性能下降。使用延迟加载可以在需要关联对象数据时进行查询,避免额外的查询请求。
​
延迟加载通常使用的场景是在一对多或多对多的关联关系中,对于关联对象数据量较大或者查询较为复杂的情况下,可以将关联对象的加载延迟到必要时才进行,提升查询性能。
​
需要注意的是,延迟加载需要开启事务以及关闭自动提交,以确保在触发延迟加载时能够正常加载关联对象的数据。另外,在基于代理的延迟加载中,需保证延迟加载的对象未关闭会话(Session)之前进行访问,否则可能会出现延迟加载异常。
6.说一下 MyBatis 的一级缓存和二级缓存?
答案:一级缓存是指在同一个SqlSession中,查询的结果会被缓存起来,以提高性能;二级缓存是 指多个SqlSession之间共享缓存,可以跨Session共享数据。
---------------------------------------------------------------------------------------
MyBatis 中存在一级缓存和二级缓存,它们分别位于 SqlSession 的作用域和 Mapper 的作用域。
​
一级缓存是指在同一个 SqlSession 中进行的缓存。当执行一条查询语句后,MyBatis 会将查询结果缓存在一级缓存中,再次执行相同的查询语句时,会直接从缓存中获取结果,而无需再次查询数据库。一级缓存是默认开启的,且无法关闭。
​
二级缓存是指在多个 SqlSession 之间共享的缓存。当执行一条查询语句后,MyBatis 会将查询结果缓存在二级缓存中,其他 SqlSession 可以共享这个缓存,下次执行相同的查询语句时,可以直接从缓存中获取结果。二级缓存需要进行配置开启,并且可以通过在 Mapper 接口中添加 @CacheNamespace 注解来指定使用二级缓存。
​
在使用二级缓存时需要注意以下几点:
​
需要在 MyBatis 配置文件中开启二级缓存,即设置 <setting name="cacheEnabled" value="true"/>。
​
被缓存的实体类需要实现 Serializable 接口,以支持对象的序列化和反序列化。
​
对于更新或删除操作,MyBatis 会自动清空对应的二级缓存。
​
二级缓存是基于命名空间进行管理的,每个 Mapper 接口的方法调用会在对应的命名空间下进行缓存。
​
对于需要使用二级缓存的 Mapper 接口,需要在对应的 Mapper XML 文件中添加 <cache/> 标签来指定使用缓存。
​
需要注意的是,一级缓存和二级缓存的有效范围是在同一个 SqlSession 或多个 SqlSession 上。如果有并发的事务或多个 SqlSession 实例,对于相同的查询语句,一级缓存和二级缓存可能会出现数据不一致的情况,因此在使用缓存时要根据具体业务场景进行合理配置和使用。
7.MyBatis 和 Hibernate 的区别有哪些?
答案:MyBatis是基于SQL和映射配置的持久化框架,需要手写SQL,更加灵活;Hibernate是ORM框架,将Java对象映射到数据库,不需要写SQL,更加面向对象。
---------------------------------------------------------------------------------------
MyBatis 和 Hibernate 是两个常用的 Java ORM(对象关系映射)框架,它们有以下几个主要区别:
​
数据库查询方式:MyBatis 是基于 SQL 查询方式的框架,开发者需要手动编写 SQL 语句来进行数据库操作,可以灵活地控制 SQL 的编写和执行。而 Hibernate 则是基于对象查询方式的框架,使用 HQL(Hibernate Query Language)或者 Criteria API 进行查询,隐藏了底层 SQL 的细节,更加面向对象。
​
缓存机制:MyBatis 和 Hibernate 在缓存方面有些不同。MyBatis 默认开启一级缓存,且可以进行配置,可以在同一个 SqlSession 中进行缓存。而 Hibernate 则默认开启一级缓存和二级缓存,一级缓存在 Session 作用域内,二级缓存可以在多个 Session 之间共享,提供更高级的缓存管理机制。
​
关联对象处理:MyBatis 对关联对象的处理相对较为简单,需要手动定义关联关系和加载关联对象的 SQL。Hibernate 则提供了更为便捷的关联对象处理,可以利用 Hibernate 的延迟加载机制自动加载关联对象,减少手动编写和管理的工作量。
​
对象状态管理:Hibernate 是一个全自动的 ORM 框架,通过 Session 的状态管理机制来追踪对象的变化并执行相应的 SQL 操作。而 MyBatis 则是一个半自动的 ORM 框架,需要开发者显式地编写和调用 SQL 语句来进行对象的 CRUD 操作,开发者对对象的状态变化更加明确和控制。
​
学习和使用成本:相对而言,MyBatis 更加简单和直观,使用者对 SQL 有更好的掌控和理解,适合于对 SQL 较熟悉的开发人员。而 Hibernate 更加高级抽象和面向对象,需要学习和掌握更多的框架特性和概念,适合于对对象关系映射有高层次要求的开发人员。
​
需要根据具体项目需求和开发者的背景来选择使用 MyBatis 还是 Hibernate,或者两者结合使用来满足不同的需求。
8.MyBatis 有哪些执行器(Executor)?
答案:MyBatis有三种执行器,分别是 SimpleExecutor 、 ReuseExecutor 和 BatchExecutor , 用于控制SQL语句的执行。
---------------------------------------------------------------------------------------
MyBatis 中有三个执行器(Executor)可供选择:
​
SimpleExecutor:简单执行器,每个 SQL 语句都会使用一个 Statement 对象进行执行。它没有使用二级缓存,也没有进行批量操作,适用于小规模的简单应用场景。
​
ReuseExecutor:可重用执行器,执行器会复用预处理的 Statement 对象。如果是相同的 SQL 语句,则复用之前预处理的 Statement 对象,减少了 Statement 的创建和销毁开销,适用于批量、重复次数较多的操作。
​
BatchExecutor:批量执行器,将多个 SQL 语句一起进行提交。相比于 SimpleExecutor 和 ReuseExecutor,它在执行 SQL 时具有更大的性能优势,特别适用于需要执行大量 SQL 语句的批处理场景。
​
这三个执行器可以通过配置文件中的 <setting name="defaultExecutorType" value="xxx"/> 来指定使用的默认执行器,其中 xxx 可以是 “SIMPLE”、“REUSE” 或 “BATCH”。
​
需要根据具体的业务场景和性能需求来选择合适的执行器。一般情况下,SimpleExecutor 提供了基本的执行功能,ReuseExecutor 可以用于大部分的应用场景,BatchExecutor 主要用于批处理等性能要求较高的场景。
9.MyBatis 分页插件的实现原理是什么?
答案:MyBatis分页插件通过拦截SQL执行,将原始SQL改写为带有分页参数的SQL,然后执行修 改后的SQL,最终返回分页结果。
---------------------------------------------------------------------------------------
MyBatis 分页插件的实现原理主要是通过拦截器(Interceptor)来实现。
​
在 MyBatis 中,可以编写自定义的拦截器来拦截 SQL 执行过程中的方法调用,并在执行前后进行一些额外的处理。分页插件就是通过拦截器来实现分页功能的。
​
分页插件一般有以下几个主要步骤:
​
编写自定义的拦截器类,实现 MyBatis 的 Interceptor 接口,并重写 intercept 方法。该方法会在 SQL 执行前后进行拦截和处理。
​
在 intercept 方法中,通过拦截到的方法参数和目标对象信息,判断是否需要进行分页操作。一般会通过解析 SQL 语句中的分页参数,如页码和每页记录数,来计算出对应的分页 SQL 语句。
​
使用拦截到的方法参数和目标对象信息,构造出一个新的 MappedStatement 对象,将分页 SQL 语句替换原有的 SQL 语句。
​
调用 invocation.proceed() 方法继续执行原始的 SQL 语句,此时执行的是替换后的分页 SQL 语句。
​
在 SQL 执行的结果返回后,如果需要,可以对返回的结果进行一些额外的处理,如对查询结果进行分页包装。
​
最后,将处理后的结果返回。
​
在使用分页插件时,需要在 MyBatis 的配置文件中进行相应的配置,包括添加插件和指定拦截的方法等。
​
需要注意的是,分页插件的实现原理是通过拦截器对 SQL 语句进行拦截和修改来实现分页功能,所以在使用分页插件的同时,需要注意插件的顺序和其他拦截器的影响,以避免意外的问题。
10.MyBatis 如何编写一个自定义插件?
答案:编写MyBatis自定义插件需要实现 Interceptor 接口,然后通过在MyBatis配置文件中配置 插件,使其生效。插件可以在SQL执行前后进行拦截,实现自定义的功能。
---------------------------------------------------------------------------------------
要编写一个自定义的 MyBatis 插件,可以按照以下步骤进行:
​
创建一个实现了 MyBatis 的 Interceptor 接口的类,实现其中的 intercept 方法和 plugin 方法。
​
在 intercept 方法中,编写自定义的拦截逻辑。可以在方法执行前后进行一些额外的操作,如修改 SQL 语句、缓存处理、日志记录等。
​
在 plugin 方法中,调用 Plugin.wrap 方法,将自定义的拦截器包装成一个新的代理对象,并返回。该代理对象会被 MyBatis 使用。
​
在需要使用自定义插件的地方,例如 MyBatis 的配置文件,通过 <plugins> 标签将自定义插件添加到 MyBatis 的插件列表中。可以指定插件的顺序。
​
以下是一个简单的示例:
​

// 自定义插件类
public class MyCustomInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在方法执行前进行一些操作
        System.out.println("Before method execution...");
​
        // 执行原始的方法
        Object result = invocation.proceed();
​
        // 在方法执行后进行一些操作
        System.out.println("After method execution...");
​
        return result;
    }
​
    @Override
    public Object plugin(Object target) {
        // 将自定义拦截器包装成代理对象
        return Plugin.wrap(target, this);
    }
​
    @Override
    public void setProperties(Properties properties) {
        // 可以在这里设置插件的属性
    }
}
在配置文件中使用自定义插件:
​
<!-- MyBatis 配置文件 -->
<configuration>
    <!-- ...其他配置... -->
​
    <plugins>
        <plugin interceptor="com.example.MyCustomInterceptor" />
    </plugins>
</configuration>
通过上述步骤,就可以编写一个简单的自定义插件,并将其应用到 MyBatis 中,实现自定义的拦截逻辑。需要根据具体需求来编写相应的拦截逻辑和插件功能。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值