你不知道的 Mybatis 四大核心组件!

apei_02
发布于 2022-4-16 18:14
浏览
0收藏

01、Executor

一个 SqlSession 对应一个 Executor 对象,这个对象负责增删改查的具体操作

1.1 组件类图
你不知道的 Mybatis 四大核心组件!-鸿蒙开发者社区如图所示, Exeutor 是一个顶层的接口, 有两个实现类, 分别是 BaseExecutor、CachingExecutor

1.2 BaseExeutor
抽象类, 采用了模版设计模式, 实现了 Exeutor 接口, 针对接口提供了默认实现, 并提供了额外的抽象方法, 由不同的实现类来负责实现不同的功能

抽象类下有三(其实是四个)个不同功能的实现类: SimpleExecutor、ReuseExecutor、BatchExecutor

1.3 CachingExecutor
支持结果缓存的 SQL 执行器,  其类中有一个 Executor 的接口属性, 这个属性可能是三个具体执行器的任何一个

先从缓存中查询结果, 如果存在, 就返回; 如果不存在, 则交由抽象类 BaseExecutor 的子实现类去查询或更新

1.4 SimpleExecuto
简单执行器, 是 mybatis 中 默认使用的执行器, 每执行一次 update 或 select, 就开启一个 Statement 对象, 用完就直接关闭 Statement 对象

“Statement 说的是一个接口, 具体的实现 Statement、 PrepareStatement、CallableStatement

1.5 ReuseExecutor
可重用的执行器, 可重用的对象为 Statement, 在执行器内部提供一个 Map 映射, 执行 SQL 作为 key, value 为 Statement

private final Map<String, Statement> statementMap = new HashMap<String, Statement>();

1.6 BatchExecutor
批量提交 SQL 的一个执行器, 在内部维护了两个属性, 用作批量提交

private final List<Statement> statementList = new ArrayList<Statement>();
private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();

平常我们在 .xml 文件写过 insert 语句进行 foreach 循环拼接 SQL, 但是这种会有一个弊端, 如果拼接字符串过长, 在 MYSQL 会抛出异常, 所以这个时候就需要 批量执行器

在 mybatis-plus 提供的方法中, 就有 saveBatch 方法, 默认 1000 条提交一次数据库, 防止了拼接字符串过长问题

// 默认 batchSize = 1000
public boolean saveBatch(Collection<T> entityList, int batchSize) {
    String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
    try (SqlSession batchSqlSession = sqlSessionBatch()) {
        int i = 0;
        for (T anEntityList : entityList) {
            batchSqlSession.insert(sqlStatement, anEntityList);
            // 当结果为 1000 时提交一次数据库
            if (i >= 1 && i % batchSize == 0) {
                batchSqlSession.flushStatements();
            }
            i++;
        }
        batchSqlSession.flushStatements();
    }
    return true;
}

02、ParameterHandler
mybatis 提供的 参数处理器, 没有过多的类关联关系, 只有一个默认的实现类

2.1 组件类图
你不知道的 Mybatis 四大核心组件!-鸿蒙开发者社区2.2 组件解读
ParameterHandler 的作用域比较单一, 作用于 PreparedStatemet 参数解析时使用

接口中只包含两个方法

public interface ParameterHandler {
    // 读取参数
    Object getParameterObject();
    // PreparedStatement 参数赋值
    void setParameters(PreparedStatement ps) throws SQLException;
}

03、StatementHandler
StatementHandler 是 mybatis 创建 Statement 的处理器, 会负责 Statement 的创建工作, 在 JDBC 中 Statement 执行 SQL 语句时主要分为两个主要对象

一个是平常大家都知道的 Statement 和 PrepareStatement, 都是在 java.sql 包下提供的对象

3.1 组件类图
你不知道的 Mybatis 四大核心组件!-鸿蒙开发者社区StatementHandler 是接口, 定义了基本的方法, BaseStatementHandler 作为其抽象实现类

3.2 BaseStatementHandler
上图中具体的三个 StatementHandler 处理类都是 BaseStatementHandler 的实现类, 在 BaseStatementHandler 中定义了接口的部分方法实现以及定义了共有参数

Base 类额外定义了 instantiateStatement 方法, 三个实现类用来真正创建 Statement 实例

3.3 SimpleStatementHandler
StatementHandler 接口具体的实现类, 对应 java.sql.Statement 对象的处理, 处理不带动态参数运行的 SQL

3.4 PrepareStatementHandler
PrepareStatementHandler 是能够解决 SQL 注入的一种推荐 Handler, 对应 java.sql.PrepareStatement 对象的处理, 负责处理 SQL 中的占位符动态参数赋值操作

3.5 CallableStatementHandler
CallableStatementHandler 实际就是使用 CallableStatement 来执行 SQL 语句, 只不过它执行的是 存储过程

3.6 RoutingStatementHandler
类中有一个 StatementHandler 属性, 看类图从结构上可以得知 StatementHandler 和 Executor 的结构是一致的

RoutingStatementHandler 是一个路由器的作用, 看对应的构造方法就明白了其作用

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    switch (ms.getStatementType()) {
        case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
}

根据 MappedStatement 中的 statementType 属性来决定创建具体的实现

04、ResultSetHandler
同 ParameterHandler 一致, 都只有一个默认的实现类

ResultSetHandler 作用域只有一个, 那就是负责处理 Statement 返回的结果, 根据定义返回类型进行封装返回

4.1 组件类图你不知道的 Mybatis 四大核心组件!-鸿蒙开发者社区4.2 DefaultResultSetHandler
mybatis 定义返回结果有两种, 一种是 resultMap, 一种是 resultType

另外也负责存储过程的结果封装解析

05、插件
上面讲到的 mybatis 四大组件都支持插件化, 可以配合代码来相应理解一下, 四大组件都是在 Configuration 类中创建的

这里使用 Executor 的创建作为例子, 这里能够明显看出, 使用了 工厂方法设计模式 进行的创建, 插件化这里同时使用了 责任链模式

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    // 责任链设计模式
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

06、学习总结
具体的插件责任链模式感兴趣可以自己了解下

这篇文章主要是简单介绍了 mybatis 四大组件的各自作用, 因为有时候自己也会搞混 ParameterHandler 和 PreparedStatemet

正常的工作是立足的根本, 业余时间的学习还是非常有必要滴...

本文转载自微信公众号「龙台的技术笔记」

分类
收藏
回复
举报
回复
    相关推荐