深度解析MyBatis核心:探寻其核心对象的精妙设计

简介: 深度解析MyBatis核心:探寻其核心对象的精妙设计

数据存储类对象

Configuration

Configuration 类是 MyBatis 框架的核心配置类,它负责管理 MyBatis 的各种配置信息。在 MyBatis 框架启动时,会通过 XMLConfigBuilder 或者 Java API 读取配置信息并构建 Configuration 对象。

Configuration 对象中包含了 MyBatis 的各种配置信息,例如:

  • 数据库连接池相关配置,如数据源、连接池大小、连接超时等。
  • Mapper 文件路径和 Mapper 接口类的映射关系。
  • 类型处理器(TypeHandler)相关配置,用于将 Java 类型转换为数据库类型或者将数据库类型转换为 Java 类型。
  • 全局 SQL 过滤器、插件等扩展功能的配置。
  • 缓存相关配置,包括一级缓存和二级缓存。
  • 全局属性配置,如默认的查询超时时间、JDBC 驱动类名等。

Configuration 类提供了一系列方法,用于获取和设置各种配置信息。例如,我们可以通过 getDataSource() 方法获取数据源对象,通过 getTypeHandlerRegistry() 方法获取类型处理器注册表对象,通过 addMapper() 方法添加 Mapper 接口等。

Configuration 类还可以创建其他的核心类实例,提供了如 newParameterHandler、newResultSetHandler、newStatementHandler、newExecutor 等方法。

在 MyBatis 框架的整个生命周期中,Configuration 对象是不可变的,也就是说,一旦配置信息被加载到 Configuration 对象中之后,就不能再进行修改。这是因为 MyBatis 框架的设计目标之一就是保持线程安全和可重入性,避免多线程环境下的竞争和锁等问题。

Configuration 是 MyBatis 的核心配置类,封装了 mybatis-config.xml 中的各个配置项。管理所有的 MappedStatement,并且可以通过 Configuration 来创建其他的核心类实例,如 Executor、StatementHandler …

MappedStatement

MappedStatement 是 MyBatis 框架中的一个重要的类,用于描述一个 SQL 语句的详细信息。它是 MyBatis 框架中的一个核心组件,位于 org.apache.ibatis.mapping 包下。

在 MyBatis 框架中,MappedStatement 包含了一个 SQL 语句的所有信息,如 statementId、statementType、resultSetType、参数映射、返回值映射、SQL 语句模板等。MappedStatement 还包含了执行该 SQL 语句所需的其他信息,如缓存配置、动态 SQL 解析器等。

MappedStatement 类的具体属性和用法如下:

  • statementId:SQL 语句的唯一标识符,由 namespace 和 id 组成。
  • statementType:Statement 的类型,STATEMENT、PREPARE、CALLABLE,默认 PREPARE。
  • sqlCommandType:SQL 语句的类型,包括 UNKNOWN、SELECT、INSERT、UPDATE、DELETE、FLUSH 六种类型。
  • resultSetType:SQL 语句的结果集类型,可选值为 FORWARD_ONLY、SCROLL_INSENSITIVE、SCROLL_SENSITIVE 和 DEFAULT。
  • parameterMap:参数映射,用于指定 SQL 语句中的参数类型和参数名称。
  • resultMaps:返回值映射,用于指定返回结果的映射关系。
  • cache:缓存配置,用于配置 SQL 语句的缓存策略。
  • keyGenerator:主键生成策略,用于生成插入操作的主键。
  • timeout:SQL 语句的超时时间,单位为秒。
  • fetchSize:每次从数据库中获取的记录数。
  • statement:SQL 语句模板,可以使用参数占位符(?)表示参数。
  • configuration:关联的核心配置类实例。
  • sqlSource:通过 BoundSql 封装 SQL 语句以及对应的参数。

Mybatis 通过解析 XML,生成 SQL 对应的 MappedStatement,并放入 SqlSessionTemplate 中 Configuration 类属性中,等正真执行 Mapper 接口中的方法时,根据 Mapper 接口的 全类名 + 方法名 作为 Key,会从 Configuration 中找到对应的 MappedStatement,然后进行后续的操作。

Mapper 的 XML 文件中,一个 SQL 语句对应一个 MappedStatement,唯一标识为:namespace.id,MappedStatement 和 Configuration 是双向依赖的,既可以通过 Configuration 找到所有的 MappedStatement,也可以通过 MappedStatement 找到对应的 Configuration。MappedStatement 将 XML 中真正的 SQL 语句和参数封装在了 BoundSql 对象中。

操作类对象

Executor

Executor 类是 MyBatis 框架中的一个核心组件,用于执行 SQL 语句并处理结果。它位于 org.apache.ibatis.executor 包下。

在 MyBatis 中,Executor 负责管理 SQL 语句的执行过程,并将结果映射到相应的对象中。它通常与 StatementHandler、ParameterHandler 和 ResultSetHandler 等其他组件配合工作,完成 SQL 语句的预编译、参数设置、结果集处理等操作。

Executor 类的主要作用可以总结为以下几点:

  1. 执行 SQL 语句:Executor 的核心功能是执行 SQL 语句。它通过调用 StatementHandler 的 prepare、parameterize 和 execute 方法来实现。prepare 方法用于创建 PreparedStatement 对象,parameterize 方法用于设置参数值,execute 方法用于执行 SQL 语句。
  2. 缓存支持:Executor 可以根据配置进行缓存操作,提高 SQL 语句的执行效率。在执行 SQL 语句之前,Executor 会先检查是否存在缓存的结果,如果存在,则直接返回缓存的结果,避免了对数据库的重复访问。
  3. 事务管理:Executor 在执行 SQL 语句时,可以根据配置来管理事务。MyBatis 支持多种事务管理方式,如 JDBC 的自动提交、Spring 事务管理等。Executor 可以在适当的时候开启、提交或回滚事务,确保数据的一致性和完整性。
  4. 结果映射:Executor 通过调用 ResultSetHandler 的 handleResultSets 方法,将 SQL 查询的结果集映射到 Java 对象中。ResultSetHandler 负责将数据库返回的结果转化为 Java 对象,使得开发者可以方便地操作和处理查询结果。
  5. 批处理支持:Executor 提供了对批处理的支持,可以将多个 SQL 语句放在一个批次中执行。这种方式可以减少与数据库的交互次数,提高系统的性能。

Executor 类的具体实现有多种,包括 SimpleExecutor、ReuseExecutor 和 BatchExecutor 等。

  1. SimpleExecutor:SimpleExecutor 是 Executor 接口的默认实现类。它通过 JDBC 的 Statement 对象执行 SQL 语句,每次执行都会创建一个新的 Statement 对象。SimpleExecutor 在执行 SQL 语句时不会使用预编译的 PreparedStatement,而是直接将 SQL 字符串传递给数据库进行执行。这种实现方式适用于简单的场景,不支持缓存和批处理。
  2. ReuseExecutor:ReuseExecutor 是 Executor 接口的另一个实现类,它与 SimpleExecutor 类似,也使用 JDBC 的 Statement 对象执行 SQL 语句。不同之处在于 ReuseExecutor 会重用已经创建的 Statement 对象,而不是每次执行都创建新的对象。这样可以减少 Statement 对象的创建和销毁开销,提高性能。ReuseExecutor 适用于需要频繁执行相同 SQL 语句的场景。
  3. BatchExecutor:BatchExecutor 是 Executor 接口的批处理实现类。它可以将多个 SQL 语句放在一个批次中执行,减少与数据库的交互次数,提高性能。BatchExecutor 在执行 SQL 语句时会使用 JDBC 的 addBatch 和 executeBatch 方法。在执行过程中,它会将所有的 SQL 语句收集起来,然后一次性发送给数据库执行。BatchExecutor 适用于需要批量处理大量数据的场景。

这些实现类都实现了 Executor 接口,因此可以通过配置文件中的 标签来指定使用的实现类。例如:

<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <executor type="org.apache.ibatis.executor.SimpleExecutor"/>
      <!-- 其他配置 -->
    </environment>
  </environments>
</configuration>

Executor 是一个接口,其中定义了有关增删改(update)、查询(query)、事务(rollback、getTransaction …)、缓存(isCached、clearLocalCache)等方法。他是 MyBatis 中处理功能的核心接口。

StatementHandler

StatementHandler 类是 MyBatis 中用来处理 SQL 语句的核心组件之一,主要负责对 SQL 语句进行解析、参数设置和执行操作。在 MyBatis 中,每个 SQL 操作都会对应一个 StatementHandler 对象。

StatementHandler 接口的实现类有三种类型:SimpleStatementHandler、PreparedStatementHandler(默认) 和 CallableStatementHandler。这三个实现类分别对应着 JDBC 中的 Statement、PreparedStatement 和 CallableStatement。

StatementHandler 接口中定义了以下几个方法:

  • prepare:该方法用于创建 PreparedStatement 或 CallableStatement 对象,并对占位符进行设置。
  • parameterize:该方法用于设置 SQL 语句中的参数。
  • batch:该方法用于执行批处理操作。
  • update:该方法用于执行更新操作,返回受影响的行数。
  • query:该方法用于执行查询操作,返回查询结果集。

默认情况下,MyBatis 使用 RoutingStatementHandler 类作为 StatementHandler 的实现类,它是一个路由器,根据 SQL 语句的类型和配置文件中的参数自动选择合适的 StatementHandler 实现类,这是一个典型的装饰器设计模式。

如果需要修改 StatementHandler 的默认行为,可以通过实现 org.apache.ibatis.executor.statement.StatementHandler 接口,并在配置文件中指定新的 StatementHandler 实现类来实现自定义的 StatementHandler。

真正与 JDBC 产生联系,封装了 JDBC 中的三种 Statement 来完成增删查改操作。

ParameterHandler

ParameterHandler 是 MyBatis 框架中的一个接口,用于处理 SQL 语句中的参数。它负责将 Java 对象中的属性值设置到 PreparedStatement 对象中。

ParameterHandler 接口定义了以下方法:

  • void setParameters(PreparedStatement ps):将 Java 对象中的属性值设置到 PreparedStatement 对象中。

具体来说,ParameterHandler 的实现类需要完成以下工作:

  1. 根据 SQL 语句中的占位符数量,确定参数的个数。
  2. 根据参数的类型和配置的 TypeHandler,将 Java 对象中的属性值转换为对应的数据库类型,并设置到 PreparedStatement 对象中。

ParameterHandler 只有一个实现类 DefaultParameterHandler。

将 MyBatis 中的 Java 参数转变为 JDBC的参数,即完成 @Param -> #{} -> ? 的转变。

public class DefaultParameterHandler implements ParameterHandler {
  // 属性
  // 持有 typeHandler 注册器
  private final TypeHandlerRegistry typeHandlerRegistry;
  // 持有 MappedStatement 实例,这是一个静态的 xml 的一个数据库操作节点的静态信息而已
  private final MappedStatement mappedStatement;
  
  // 持有当前操作传入的实际参数
  private final Object parameterObject;
  // 动态语言被执行后的结果 sql
  private final BoundSql boundSql;
  private final Configuration configuration;
  
  // 构造函数
  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }
  
  // 实现方法
  @Override
  public Object getParameterObject() {
    return parameterObject;
  }
  
  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 1. 获取 boundSql 中的参数映射信息列表
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      // 1.1. 遍历参数映射列表,这个列表信息就是我们 xml 文件中定义的某个查询语句的所有参数映射信息,注意这个 List 中的参数映射元素的顺序是和真实 xml 中 sql 的参数顺序对应的
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        // 1.2. 只有入参类型才会设置 PreparedStatement
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          // 取出参数名,这里比如说是 'phone'
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            // 1.3. 这一步的工作就是从当前实际传入的参数中获取到指定 key('phone')的 value 值,比如是'15800000000'
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          
          // 2. 获取该参数对应的 typeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          
          // 2.1. 获取该参数对应的 jdbcType
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 3. 重点是调用每个参数对应的 typeHandler 的 setParameter 方法为该 ps 设置正确的参数值
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
}

ResultSetHandler

ResultSetHandler 是 Mybatis 的核心组件,主要负责将结果集 resultSets 转化成结果列表(或 cursor)和处理储存过程的输出。

在原生 JDBC 查询的代码中,使用 Statement 进行操作,会返回 ResultSet 对象。ResultSet 也是 java.sql 中的接口,它表示通过执行查询数据库的语句生成的结果集对象,在 JDBC 的操作中数据库的所有查询记录将使用 ResultSet 进行接收。

我们获取到 ResultSet 后,就可以将其转换为程序中的 Java 对象进行数据展示,但是原生的操作非常繁琐,所以 Mybatis 提供了 ResultSet 处理器,我们只需要定义好返回类型,Mybatis 就可以自动进行转换映射了,底层使用反射技术。

DefaultResultSetHandler 是 ResultSetHandler 的默认实现类,其中实现了很多处理一对一、一对多、嵌套查询等结果集的处理方法。

TypeHandler

TypeHandler 是 MyBatis 中的一个接口,用于处理 Java 对象与数据库数据类型之间的转换。它提供了一种机制,允许你自定义数据类型在数据库和Java对象之间的映射规则。MyBatis 默认提供了一些常见数据类型的 TypeHandler 实现,但是在特定场景下,你可能需要自定义 TypeHandler 来满足你的需求。

类型处理器这个接口其实很简单,总共四个方法,一个方法将入参的 Java 类型的数据转换为 JDBC 类型,三个方法将返回结果转换为 Java 类型。源码如下:

public interface TypeHandler<T> {
  // Java 类型的数据转换为 JDBC 类型
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  
  // 将返回结果转换为 Java 类型
  T getResult(ResultSet rs, String columnName) throws SQLException;
 
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
 
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

自定义类型处理器的方法有两种,一种是实现 TypeHandler 这个接口,另一个就是继承 BaseTypeHandler 这个便捷的抽象类。实现其中的方法后将类型处理器注入给 MyBatis。可以使用注解如:@MappedTypes@MappedJdbcTypes,也可以在配置文件中指定 mybatis.type-handlers-package=自定义类型处理器的路径


相关文章
|
1月前
|
SQL Java 数据库连接
|
8天前
|
XML Java 数据库连接
Javaweb之Mybatis的XML配置文件的详细解析
Javaweb之Mybatis的XML配置文件的详细解析
13 0
|
8天前
|
SQL Java 数据库连接
Javaweb之Mybatis的基础操作之新增和更新操作的详细解析
Javaweb之Mybatis的基础操作之新增和更新操作的详细解析
10 0
|
13天前
|
Java API 数据库
深入解析:使用JPA进行Java对象关系映射的实践与应用
【4月更文挑战第17天】Java Persistence API (JPA) 是Java EE中的ORM规范,简化数据库操作,让开发者以面向对象方式处理数据,提高效率和代码可读性。它定义了Java对象与数据库表的映射,通过@Entity等注解标记实体类,如User类映射到users表。JPA提供持久化上下文和EntityManager,管理对象生命周期,支持Criteria API和JPQL进行数据库查询。同时,JPA包含事务管理功能,保证数据一致性。使用JPA能降低开发复杂性,但需根据项目需求灵活应用,结合框架如Spring Data JPA,进一步提升开发便捷性。
|
18天前
|
Java 关系型数据库 MySQL
高级对象装配:解析Spring创建复杂对象的秘诀
高级对象装配:解析Spring创建复杂对象的秘诀
27 0
高级对象装配:解析Spring创建复杂对象的秘诀
|
1月前
|
Python
Python类与对象:深入解析与应用
本文介绍了Python中的核心概念——类和对象,以及它们在面向对象编程中的应用。类是用户定义的类型,描述具有相同属性和行为的对象集合;对象是类的实例,具备类的属性和方法。文章通过示例讲解了如何定义类、创建及使用对象,包括`__init__`方法、属性访问和方法调用。此外,还阐述了类的继承,允许子类继承父类的属性和方法并进行扩展。掌握这些概念有助于提升Python编程的效率和灵活性。
|
1月前
|
设计模式 XML SQL
C++建造者模式解析:构建复杂对象的优雅方式
C++建造者模式解析:构建复杂对象的优雅方式
39 1
C++建造者模式解析:构建复杂对象的优雅方式
|
1月前
|
存储 JSON JavaScript
Python如何解析json对象?
Python如何解析json对象?
25 1
|
1月前
|
设计模式 缓存 安全
代理模式:解析对象间的间接访问与控制
代理模式:解析对象间的间接访问与控制
|
5天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)

推荐镜像

更多