文章目录
1、MyBatis 中 #{} 和 ${} 的区别是什么?
- #{} 是预编译处理,${} 是字符串替换。
- MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为?号,调用PreparedStatement的set方法来赋值。
- MyBatis 在处理 ${} 时,只是把 ${} 替换成变量的值。
- 使用 #{} 可以有效防止 SQL 注入,提高系统安全性。
2、MyBatis 有几种分页方式?
- 数组分页
- SQL 分页
- 拦截器分页
- RowBounds 分页。
3、MyBatis 逻辑分页和物理分页的区别是什么?
- 数据库负担:物理分页每次都访问数据库,逻辑分页只访问一次数据库,物理分页对数据库造成的负担大。
- 服务器负担:逻辑分页一次性将数据读取到内存,占用了较大的内存空间,物理分页每次只读取一部分数据,占用内存空间较小。
- 实时性:逻辑分页一次性将数据读取到内存,数据发生改变时数据库的最新状态不能实时反应到操作中,实时性差,物理分页每次需要数据时都访问数据库,能够获取到数据库的最新状态,实时性强。
- 适用场合:逻辑分页主要用于数据量不大,数据稳定的场合;物理分页主要用于数据量较大,更新频繁的场合。
4、MyBatis 是否支持延迟加载?延迟加载的原理是什么?
MyBatis 仅支持 association(一对一)关联对象和 collection(一对多)关联集合对象的延迟加载。在 MyBatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnable=true|false,MyBatis 默认是关闭延迟加载的。
延迟加载的原理是使用 CGLIB 创建目标对象的代理对象,拦截属性的get方法,在调用get方法时会检查是否为null,如果为null就先查询数据库将属性设置进去,然后返回属性的值。
5、说一下 MyBaits 的一级缓存和二级缓存。
一级缓存:基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session ,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
二级缓存:和一级缓存的机制相同。区别是二级缓存的作用域为 Mapper(Namespace),并且可以自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口,可在它的映射文件中配置标签;二级缓存的更新机制为:当某一个作用域进行了C/U/D操作后,默认该作用域下的所有 select 中的缓存将被清空。
6、MyBatis 和 Hibernate 的区别有哪些?
- Hibernate 是一个全自动的 ORM 框架,MyBatis 是一个半自动的 ORM 框架。
- MyBatis 直接编写原生态 SQL,可以严格控制 SQL 执行性能,灵活度高,非常适合对关系数据模型要求不高的系统开发,这类系统需求变化频繁,一旦有需求变化要求迅速输出成果。但灵活的前提是 MyBatis 无法做到数据库无关性,如果需要实现支持多种数据库的系统,则需要定义多套 SQL 映射文件,工作量大。
- Hibernate 对象/关系映射能力强,数据库无关性号,对于关系模型要求高的系统,如果用 Hibernate 开发可以节省很多代码,提高效率。
7、MyBatis 有哪些执行器(Executor)?
MyBatis 有三种基本的执行器:
- SimpleExecutor: 每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
- ReuseExecutor: 执行 update 或 select ,以 SQL 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简而言之,就是可以重复使用 Statement 对象。
- BatchExecutor: 执行 update (没有 select ,JDBC 批处理不支持 select),将所有 SQL 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch() 完毕后,等待逐一执行 executeBatch() 批处理。与 JDBC 批处理相同。
8、MyBatis 分页插件的原理是什么?
分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL, 根据 Dialect 方言,添加对应的物理分页语句和物理分页参数。
9、MyBatis 如何编写一个自定义插件?
MyBatis 自定义插件针对 MyBatis 四大对象(Executor、StatementHandler、ParamentterHandler、ResultSetHandler)进行拦截。
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed): 主要用于SQL重写。
- StatementHandler (prepare, parameterize, batch, update, query):用于 JDBC 层的控制。
- ParameterHandler (getParameterObject, setParameters):用于参数处理。
- ResultSetHandler (handleResultSets, handleOutputParameters):用于结果集二次处理。
MyBatis自定义插件必须实现Interceptor接口:
package org.apache.ibatis.plugin;
import java.util.Properties;
/**
* @author Clinton Begin
*/
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
intercept方法:拦截器具体处理逻辑方法
plugin方法:根据签名signatureMap生成动态代理对象
setProperties方法:设置Properties属性
自定义插件Demo:
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author ZhengNC
* @date 2020/8/12 15:16
*/
@Intercepts({
@Signature(type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class DemoPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object object = invocation.getTarget();
Method method = invocation.getMethod();
Object[] args = invocation.getArgs();
System.out.println("方法执行前。。。");
Object result = invocation.proceed();
System.out.println("方法执行后。。。");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
要使用自定义插件还必须在MyBatis的配置文件中配置此插件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.test.javademo.mybatisplugin.DemoPlugin">
<property name="dbType" value="mysql"/>
</plugin>
</plugins>
</configuration>