Mybatis 插件原理解析

本文深入解析了MyBatis的SqlSessionFactory构建过程,包括Configuration的解析、映射器内部组成以及SqlSessionFactory的构建。此外,还详细阐述了SqlSession的运行机制,特别是Executor、StatementHandler、ParameterHandler和ResultHandler的角色。最后,探讨了MyBatis插件的接口、初始化、代理和反射设计,以及如何使用MetaObject,并提供了插件开发示例。
摘要由CSDN通过智能技术生成

一、构建 SqlSessionFactory 的过程

SqlSessionFactoryMyBatis 核心类之一,其重要功能是创建 MyBatis 的核心接口 SqlSession。MyBatis 通过 SqlSessionFactoryBuilder 构建 SqlSessionFactory,构建分为两步:

  1. 通过 org.apache.ibatis.builder.xml.XMLConfigBuilder 解析 XML 配置文件,读取配置参数并存入 org.apache.ibatis.session.Configuration
  2. 使用 Configuration 构建 SqlSessionFactory,MyBatis 提供了 SqlSessionFactory 的默认实现类 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

这种创建方式是一种 Builder 模式。对于复杂对象而言,直接使用构造函数构建会导致大量的逻辑放在构造函数中,使得代码看起来很繁杂。使用一个参数类总领全局,然后按步骤构建可以降低构建的复杂性

1.1 构建 Configuration

Configuration 的作用如下:

  1. 读入配置文件,包括基础配置文件和映射器文件
  2. 初始化基础配置,比如 MyBatis 的别名,一些重要的类对象(插件、映射器、ObjectFactoryTypeHandler
  3. 提供 Configuration 单例,为后续创建 SqlSessionFactory 服务提供配置参数

1.2 映射器内部组成

插件需要频繁访问映射器内部组成,所以有必要单独研究一下映射器的内部组成。一般而言,映射器由 3 部分组成:

  1. MappedStatement:保存映射器的一个节点,包括配置的 sql、sql id、resultMap、resultType 等重要配置内容
  2. SqlSource:提供 BoundSql 的地方,是 MappedStatement 的一个属性
  3. BoundSql:建立 SQL 和相关参数的地方,有三个常用属性,sql、parameterObject 和 parameterMappings

在这里插入图片描述

一般而言,我们主要对参数和 SQL 进行修改,这部分主要反映在 BoundSql 类上,在插件中可以通过 BoundSql 拿到当前运行 SQL 和参数及参数规则,并做出适当修改,满足我们的需求

BoundSql 主要有三个属性:sqlrameterObjectparameterMappings

  1. parameterObject:参数对象本身,可以传递简单对象、POJOMap 或者 @Param 注解的参数,其传递规则如下:
    • 传递简单对象会将其变为简单对象的包装类传递,如 int 变为 Integer 传递
    • 传递 POJOMap,parameterObject 就是传入的 POJOMap 不变
    • 当传递多个参数,如果没有 @Param 注解,那么 MyBatis 会将 parameterObject 变为一个 Map<String, Object> 对象,类似于这样的形式:{"param1":p1, "param2":p2},所以我们可以使用 #{param1} 引用一个参数
    • 如果使用 @Param 注解,那么 MyBatis 会将 parameterObject 变为一个 Map<String, Object> 对象。只是将 Map 的键值置换为 @Param 注解的键值。例如 @Param("key1") String p1, @Param("key2") String p2,parameterObject 的形式为 {"key1":p1, "key2":p2}
  2. parameterMappings:是一个 List,每个元素都是 ParameterMapping 对象。这个对象会描述我们的参数,参数包括属性、名称、表达式、JavaType、typeHandler 信息,一般不会去改变它。通过 parameterMappings 可以实现参数和 SQL 的结合,以便 PreParedStatement 通过 parameterMappings 找到 parameterObject 对象的属性并设置参数
  3. sql:即我们书写在映射器里面的一条 SQL,可以通过插件进行改写

1.3 构建 SqlSessionFactory

有了 Configuration 便可以快速构建 SqlSessionFactory

二、SqlSession 运行过程

2.1 映射器动态代理

todo

2.2 SqlSession 四大对象

映射器其实就是一个动态代理对象,最终会进入 MapperMethodexecute 方法。execute 经过简单判断就调用 SqlSession 的删除、更新、插入、选择等方法。这些方法是通过 ExecutorStatementHandlerParameterHandlerResultHandler 来完成数据库操作和结果返回的

  1. Executor:执行器,负责调度 StatementHandlerParameterHandlerResultHandler 执行 SQL并返回结果
  2. StatementHandler:使用数据库的 Statement(PrepareStatement)执行操作,是四大对象的核心
  3. ParameterHandler:用于 SQL 对参数的处理
  4. ResultHandler:进行最后结果数据集(ResultSet)的封装返回处理
2.2.1 Executor

Executor 负责执行 Java 和 数据库交互,在 MyBatis 中存在三种执行器,可以在配置文件的 setting 元素的 defaultExecutorType 指定

public enum ExecutorType {
   
  SIMPLE, REUSE, BATCH
}
  1. SIMPLE:简单执行器,它是默认执行器
  2. REUSE:重用预处理语句的执行器
  3. BATCH:重用语句和批量更新,针对批量专用的执行器

构造过程如下:

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;
}

执行过程中,interceptorChain.pluginAll(executor) 就是 MyBatis 插件执行的地方,这行代码会为 executor 构建一层层代理对象,在调度真实的 Executor 方法之前执行配置插件的代码可以修改。SimpleExecutor 的查询处理过程如下所示:

public class SimpleExecutor extends BaseExecutor {
   

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
   
    Statement stmt = null;
    try {
   
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
    } finally {
   
        closeStatement(stmt);
    }
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
   
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

如代码所示,SimpleExecutor 的查询过程一共有 4 步:

  1. 通过 configuration 创建 StatementHandler
  2. 调用 StatementHandler#prepare 进行预编译和基础设置
  3. 调用 StatementHandler#parameterize 设置参数
  4. 调用 StatementHandler#query 执行查询并通过 ResultHandler 组装查询结果并返回
2.2.2 StatementHandler

StatementHandler,顾名思义就是处理数据库会话的,其创建过程如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值