深入学习 Mybatis 的四大组件源码

news2024/12/22 22:27:10

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)
💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

文章目录

  • 1、Mybatis 的四大组件
  • 2、SqlSessionFactoryBuilder 源码分析
    • 2.1、XMLConfigBuilder
    • 2.2、Configuration
  • 3、SqlSessionFactory 源码分析
  • 4、SqlSession 源码分析
  • 5、Mapper 源码分析
    • 5.1、Mapper 接口
    • 5.2、MapperProxyFactory 类
    • 5.3、MapperProxy 类
    • 5.4、MapperMethod 类

1、Mybatis 的四大组件

Mybatis 的四大组件包括:

  1. SqlSessionFactoryBuilder :SqlSessionFactoryBuilder 是 Mybatis 的核心组件之一。SqlSessionFactoryBuilder 负责创建 SqlSessionFactory 对象,它是 SqlSessionFactory 的构建器。SqlSessionFactoryBuilder 通过解析 Mybatis 的配置文件创建 SqlSessionFactory 对象,从而实现对 SqlSession 的创建和管理。在 Mybatis 中,SqlSessionFactoryBuilder 通常是通过静态方法调用进行使用,如 SqlSessionFactoryBuilder.build()。

  2. SqlSessionFactory:SqlSessionFactory 是 Mybatis 的工厂类,用于创建 SqlSession 对象。SqlSession 是 Mybatis 中用于执行 SQL 语句的对象。

  3. SqlSession:SqlSession 是 Mybatis 中用于执行 SQL 语句的对象。SqlSession 提供了一系列的方法,用于执行 SQL 语句、获取 Mapper 接口、提交事务、关闭连接等操作。

  4. Mapper:Mapper 是 Mybatis 中用于描述 SQL 语句与 Java 方法之间映射关系的接口。Mapper 接口中定义了 SQL 语句及其参数类型、返回值类型等信息。

源码大家可以去 GitHub 下载 https://github.com/pydlove/mybatis-3-mybatis-3.5.11

2、SqlSessionFactoryBuilder 源码分析

以下是 SqlSessionFactoryBuilder 的源码分析举例代码:

public class SqlSessionFactoryBuilder {
     public SqlSessionFactory build(Reader reader) {
        try {
            // 创建 XMLConfigBuilder 对象,用于解析 Mybatis 的配置文件
            XMLConfigBuilder parser = new XMLConfigBuilder(reader);
            
            // 创建 Configuration 对象,用于管理 Mybatis 的所有配置信息
            Configuration config = parser.parse();
            
            // 创建 SqlSessionFactory 对象,并返回该对象
            return build(config);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                reader.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
 }

以上代码中,SqlSessionFactoryBuilder 类的 build() 方法接收一个 Reader 对象作为参数,用于读取 Mybatis 的配置文件。在方法内部,首先创建一个 XMLConfigBuilder 对象,用于解析 Mybatis 的配置文件。然后,调用 XMLConfigBuilder 对象的 parse() 方法,对 Mybatis 的配置文件进行解析。接着,创建一个 Configuration 对象,用于管理 Mybatis 的所有配置信息。然后,调用 XMLConfigBuilder 对象的 parseConfiguration() 方法,将解析出来的配置信息设置到 Configuration 对象中。最后,调用 Configuration 对象的 buildSessionFactory() 方法,创建 SqlSessionFactory 对象,并返回该对象。

可以看出,SqlSessionFactoryBuilder 类的 build() 方法非常简洁明了,通过调用其他组件的方法,实现了对 SqlSessionFactory 对象的创建和管理。

2.1、XMLConfigBuilder

XMLConfigBuilder parser = new XMLConfigBuilder(reader);

a)、什么是 XMLConfigBuilder ?

XMLConfigBuilder 是 MyBatis 中用于解析 XML 配置文件的类。它继承自 BaseBuilder 类,负责将 XML 配置文件中的内容解析成 Configuration 对象,并完成 MyBatis 的初始化工作。

b)、XMLConfigBuilder 的主要方法 parse

XMLConfigBuilder 中的主要方法是 parse() 方法,它接受一个 InputStream 对象作为参数,用于读取 XML 文件中的内容。在解析 XML 文件时,XMLConfigBuilder 会创建一个 XPathParser 对象,用于对 XML 文件进行解析。同时,XMLConfigBuilder 也会创建一个 Configuration 对象,用于存储解析结果。

在解析 XML 文件时,XMLConfigBuilder 会根据 XML 文件中的标签和属性,对 Configuration 对象中的各个属性进行设置。例如,当解析到 dataSource 标签时,XMLConfigBuilder 会根据标签中的属性设置数据库连接信息;当解析到 mappers 标签时,XMLConfigBuilder 会根据标签中的属性扫描指定的包,并自动注册 Mapper 接口。

XMLConfigBuilder 是 MyBatis 中非常重要的一个类,它负责将 XML 配置文件解析成 Configuration 对象,并完成 MyBatis 的初始化工作。

2.2、Configuration

Configuration 是 MyBatis 框架中的一个重要组件,它是 MyBatis 的核心配置类,用于存储 MyBatis 的配置信息,包括数据库连接信息、映射器信息、类型处理器信息等。

Configuration 类的主要作用是

  1. 解析 MyBatis 的配置文件,将配置信息加载到内存中。
  2. 存储 MyBatis 的配置信息,包括数据库连接信息、映射器信息、类型处理器信息等。
  3. 提供获取和设置配置信息的方法,如获取 MapperRegistry 对象、获取 TypeHandlerRegistry 对象等。
  4. 提供创建 SqlSession 对象、Executor 对象、MappedStatement 对象等的方法。

Configuration 类的主要成员变量包括

  1. Properties:存储 MyBatis 的全局配置信息。
  2. TypeAliasRegistry:存储别名信息。
  3. TypeHandlerRegistry:存储类型处理器信息。
  4. MapperRegistry:存储映射器信息。
  5. MappedStatement:存储 SQL 语句的映射信息。
  6. SqlSourceBuilder:用于解析 SQL 语句的 SQL 解析器。
  7. LanguageDriverRegistry:存储语言驱动器信息。

Configuration 类的主要方法包括

  1. addMapper(Class type):向 MapperRegistry 中添加映射器。
  2. getMapper(Class type, SqlSession sqlSession):通过 MapperRegistry 获取映射器。
  3. getMappedStatement(String id):通过 MappedStatement 获取 SQL 语句的映射信息。
  4. addMappedStatement(MappedStatement ms):向 MappedStatement 中添加 SQL 语句的映射信息。
  5. newExecutor(Transaction transaction, ExecutorType executorType):创建 Executor 对象。
  6. newSqlSession(ExecutorType executorType, boolean autoCommit):创建 SqlSession 对象。

Configuration 是 MyBatis 框架中的一个重要组件,它负责存储和管理 MyBatis 的配置信息,提供了创建 SqlSession 对象、Executor 对象、MappedStatement 对象等的方法,是 MyBatis 框架的核心之一。

如下面这个简单的 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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

3、SqlSessionFactory 源码分析

SqlSessionFactory 是 MyBatis 中用于创建 SqlSession 对象的工厂类。SqlSession 是 MyBatis 中用于执行 SQL 语句的核心类,它提供了许多执行 SQL 语句的方法,例如 selectOne()、selectList()、insert()、update()、delete() 等。

SqlSessionFactory 的主要作用是创建 SqlSession 对象,并为其配置必要的参数。在创建 SqlSession 对象时,SqlSessionFactory 会为其设置数据库连接信息、事务管理器、执行器类型等参数,以便 SqlSession 能够正确地执行 SQL 语句。

SqlSessionFactory 的创建通常是在应用程序启动时完成的。在创建 SqlSessionFactory 时,我们需要提供一个 Configuration 对象,该对象包含了 MyBatis 的配置信息。SqlSessionFactory 会根据 Configuration 对象中的配置信息,创建一个 SqlSession 对象,并将其返回给调用者。

SqlSessionFactory 的创建通常是比较耗时的,因此在应用程序中应该尽量避免频繁地创建 SqlSessionFactory 对象。通常情况下,我们会将 SqlSessionFactory 对象创建成单例,并在整个应用程序中共享使用。

SqlSessionFactory 有一个实现类 DefaultSqlSessionFactory,如下:

/*
 *    Copyright 2009-2022 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session.defaults;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;

/**
 * @author Clinton Begin
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }

  @Override
  public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
  }

  @Override
  public SqlSession openSession(TransactionIsolationLevel level) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return openSessionFromDataSource(execType, level, false);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
    return openSessionFromDataSource(execType, null, autoCommit);
  }

  @Override
  public SqlSession openSession(Connection connection) {
    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, Connection connection) {
    return openSessionFromConnection(execType, connection);
  }

  @Override
  public Configuration getConfiguration() {
    return configuration;
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      final Transaction tx = transactionFactory.newTransaction(connection);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

  private void closeTransaction(Transaction tx) {
    if (tx != null) {
      try {
        tx.close();
      } catch (SQLException ignore) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

}

DefaultSqlSessionFactory 是 MyBatis 中 SqlSessionFactory 接口的默认实现类。它实现了 SqlSessionFactory 接口中的方法,并负责创建 SqlSession 对象。

在创建 DefaultSqlSessionFactory 对象时,我们需要提供一个 Configuration 对象作为参数。DefaultSqlSessionFactory 会使用这个 Configuration 对象来创建 SqlSession 对象,并为其设置必要的参数,例如数据库连接信息、事务管理器、执行器类型等。

DefaultSqlSessionFactory 中最重要的方法是 openSession(),它用于创建一个新的 SqlSession 对象。在创建 SqlSession 对象时,DefaultSqlSessionFactory 会为其设置必要的参数,并将其返回给调用者。SqlSession 对象创建完成后,我们就可以使用它来执行 SQL 语句了。openSession 内部的具体调用的方法 openSessionFromDataSource、openSessionFromConnection 这两个私有方法,可以读一下,主要是用来获取 SqlSession 的。

需要注意的是,DefaultSqlSessionFactory 对象的创建通常是比较耗时的,因此在应用程序中应该尽量避免频繁地创建 DefaultSqlSessionFactory 对象。通常情况下,我们会将 DefaultSqlSessionFactory 对象创建成单例,并在整个应用程序中共享使用。

4、SqlSession 源码分析

SqlSession 是 MyBatis 中用于执行 SQL 语句的核心类,它提供了许多执行 SQL 语句的方法,例如 selectOne()、selectList()、insert()、update()、delete() 等。下面我们来分析一下 SqlSession 的源码实现。

1. SqlSession 接口

SqlSession 接口定义了 MyBatis 中用于执行 SQL 语句的方法,包括 selectOne()、selectList()、insert()、update()、delete() 等。它的源码如下:

/*
 *    Copyright 2009-2022 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session;

import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;

/**
 * The primary Java interface for working with MyBatis.
 * Through this interface you can execute commands, get mappers and manage transactions.
 *
 * @author Clinton Begin
 */
public interface SqlSession extends Closeable {

  /**
   * Retrieve a single row mapped from the statement key.
   * @param <T> the returned object type
   * @param statement
   *          the statement
   * @return Mapped object
   */
  <T> T selectOne(String statement);

  /**
   * Retrieve a single row mapped from the statement key and parameter.
   * @param <T> the returned object type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @return Mapped object
   */
  <T> T selectOne(String statement, Object parameter);

  /**
   * Retrieve a list of mapped objects from the statement key.
   * @param <E> the returned list element type
   * @param statement Unique identifier matching the statement to use.
   * @return List of mapped object
   */
  <E> List<E> selectList(String statement);

  /**
   * Retrieve a list of mapped objects from the statement key and parameter.
   * @param <E> the returned list element type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @return List of mapped object
   */
  <E> List<E> selectList(String statement, Object parameter);

  /**
   * Retrieve a list of mapped objects from the statement key and parameter,
   * within the specified row bounds.
   * @param <E> the returned list element type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param rowBounds  Bounds to limit object retrieval
   * @return List of mapped object
   */
  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

  /**
   * The selectMap is a special case in that it is designed to convert a list
   * of results into a Map based on one of the properties in the resulting
   * objects.
   * Eg. Return a of Map[Integer,Author] for selectMap("selectAuthors","id")
   * @param <K> the returned Map keys type
   * @param <V> the returned Map values type
   * @param statement Unique identifier matching the statement to use.
   * @param mapKey The property to use as key for each value in the list.
   * @return Map containing key pair data.
   */
  <K, V> Map<K, V> selectMap(String statement, String mapKey);

  /**
   * The selectMap is a special case in that it is designed to convert a list
   * of results into a Map based on one of the properties in the resulting
   * objects.
   * @param <K> the returned Map keys type
   * @param <V> the returned Map values type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param mapKey The property to use as key for each value in the list.
   * @return Map containing key pair data.
   */
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

  /**
   * The selectMap is a special case in that it is designed to convert a list
   * of results into a Map based on one of the properties in the resulting
   * objects.
   * @param <K> the returned Map keys type
   * @param <V> the returned Map values type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param mapKey The property to use as key for each value in the list.
   * @param rowBounds  Bounds to limit object retrieval
   * @return Map containing key pair data.
   */
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

  /**
   * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
   * @param <T> the returned cursor element type.
   * @param statement Unique identifier matching the statement to use.
   * @return Cursor of mapped objects
   */
  <T> Cursor<T> selectCursor(String statement);

  /**
   * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
   * @param <T> the returned cursor element type.
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @return Cursor of mapped objects
   */
  <T> Cursor<T> selectCursor(String statement, Object parameter);

  /**
   * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
   * @param <T> the returned cursor element type.
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param rowBounds  Bounds to limit object retrieval
   * @return Cursor of mapped objects
   */
  <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);

  /**
   * Retrieve a single row mapped from the statement key and parameter
   * using a {@code ResultHandler}.
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param handler ResultHandler that will handle each retrieved row
   */
  void select(String statement, Object parameter, ResultHandler handler);

  /**
   * Retrieve a single row mapped from the statement
   * using a {@code ResultHandler}.
   * @param statement Unique identifier matching the statement to use.
   * @param handler ResultHandler that will handle each retrieved row
   */
  void select(String statement, ResultHandler handler);

  /**
   * Retrieve a single row mapped from the statement key and parameter using a {@code ResultHandler} and
   * {@code RowBounds}.
   *
   * @param statement
   *          Unique identifier matching the statement to use.
   * @param parameter
   *          the parameter
   * @param rowBounds
   *          RowBound instance to limit the query results
   * @param handler
   *          ResultHandler that will handle each retrieved row
   */
  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

  /**
   * Execute an insert statement.
   * @param statement Unique identifier matching the statement to execute.
   * @return int The number of rows affected by the insert.
   */
  int insert(String statement);

  /**
   * Execute an insert statement with the given parameter object. Any generated
   * autoincrement values or selectKey entries will modify the given parameter
   * object properties. Only the number of rows affected will be returned.
   * @param statement Unique identifier matching the statement to execute.
   * @param parameter A parameter object to pass to the statement.
   * @return int The number of rows affected by the insert.
   */
  int insert(String statement, Object parameter);

  /**
   * Execute an update statement. The number of rows affected will be returned.
   * @param statement Unique identifier matching the statement to execute.
   * @return int The number of rows affected by the update.
   */
  int update(String statement);

  /**
   * Execute an update statement. The number of rows affected will be returned.
   * @param statement Unique identifier matching the statement to execute.
   * @param parameter A parameter object to pass to the statement.
   * @return int The number of rows affected by the update.
   */
  int update(String statement, Object parameter);

  /**
   * Execute a delete statement. The number of rows affected will be returned.
   * @param statement Unique identifier matching the statement to execute.
   * @return int The number of rows affected by the delete.
   */
  int delete(String statement);

  /**
   * Execute a delete statement. The number of rows affected will be returned.
   * @param statement Unique identifier matching the statement to execute.
   * @param parameter A parameter object to pass to the statement.
   * @return int The number of rows affected by the delete.
   */
  int delete(String statement, Object parameter);

  /**
   * Flushes batch statements and commits database connection.
   * Note that database connection will not be committed if no updates/deletes/inserts were called.
   * To force the commit call {@link SqlSession#commit(boolean)}
   */
  void commit();

  /**
   * Flushes batch statements and commits database connection.
   * @param force forces connection commit
   */
  void commit(boolean force);

  /**
   * Discards pending batch statements and rolls database connection back.
   * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
   * To force the rollback call {@link SqlSession#rollback(boolean)}
   */
  void rollback();

  /**
   * Discards pending batch statements and rolls database connection back.
   * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
   * @param force forces connection rollback
   */
  void rollback(boolean force);

  /**
   * Flushes batch statements.
   * @return BatchResult list of updated records
   * @since 3.0.6
   */
  List<BatchResult> flushStatements();

  /**
   * Closes the session.
   */
  @Override
  void close();

  /**
   * Clears local session cache.
   */
  void clearCache();

  /**
   * Retrieves current configuration.
   * @return Configuration
   */
  Configuration getConfiguration();

  /**
   * Retrieves a mapper.
   * @param <T> the mapper type
   * @param type Mapper interface class
   * @return a mapper bound to this SqlSession
   */
  <T> T getMapper(Class<T> type);

  /**
   * Retrieves inner database connection.
   * @return Connection
   */
  Connection getConnection();
}

SqlSession 接口中定义了许多执行 SQL 语句的方法,包括 selectOne()、selectList()、insert()、update()、delete() 等。这些方法的实现会使用 MyBatis 中的 Executor 对象来执行 SQL 语句。

2. DefaultSqlSession 类

DefaultSqlSession 是 SqlSession 接口的默认实现类。它实现了 SqlSession 接口中的所有方法,并负责创建 Executor 对象和事务对象。下面我们来分析一下 DefaultSqlSession 的源码实现。

DefaultSqlSession 类有三个构造方法,分别是:

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
}

public DefaultSqlSession(Configuration configuration, Executor executor) {
 	this(configuration, executor, false);
}

DefaultSqlSession 类中的大部分方法都是用来执行 SQL 语句的。这些方法会使用 Executor 对象来执行 SQL 语句。下面以 selectOne() 方法为例,来看一下它的源码实现:

public <T> T selectOne(String statement, Object parameter) {
  return executor.query(statement, wrapCollection(parameter), RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
}

selectOne() 方法接受两个参数,分别是 SQL 语句的 ID 和参数对象。它会将参数对象转换为一个集合对象,并将其传递给 Executor 对象的 query() 方法来执行 SQL 语句。query() 方法接受四个参数,分别是 SQL 语句的 ID、参数对象、分页信息和结果处理器。在这里,我们传递了 RowBounds.DEFAULT 和 Executor.NO_RESULT_HANDLER 作为分页信息和结果处理器,表示不需要分页和结果处理器。

DefaultSqlSession 类中还包含了一些用于事务管理的方法,例如 commit()、rollback() 等。这些方法会使用 Transaction 对象来管理事务。下面以 commit() 方法为例,来看一下它的源码实现:

public void commit() {
  commit(false);
}

public void commit(boolean force) {
  executor.commit(isCommitOrRollbackRequired(force));
  clearCache();
}

private boolean isCommitOrRollbackRequired(boolean force) {
  return (!autoCommit && dirty) || force;
}

commit() 方法有两个重载版本,分别是带参数和不带参数的。带参数的版本表示是否强制提交事务。在 commit() 方法中,我们首先调用了 isCommitOrRollbackRequired() 方法来判断是否需要提交事务。isCommitOrRollbackRequired() 方法会根据自动提交标志和 dirty 标志来判断是否需要提交事务。如果需要提交事务,就调用 Executor 对象的 commit() 方法来提交事务,并清空缓存。

5、Mapper 源码分析

Mapper 是 MyBatis 中用于定义 SQL 映射关系的接口,它通过注解或 XML 文件来定义 SQL 语句和参数映射。下面我们来分析一下 Mapper 的源码实现。

5.1、Mapper 接口

Mapper 接口是 MyBatis 中用于定义 SQL 映射关系的接口,它定义了 SQL 语句和参数映射的方法。例如:

public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User getUserById(int id);
}

在这个例子中,我们使用 @Select 注解来定义了一个查询语句,并将参数 id 映射到 SQL 语句中的 #{id} 占位符。Mapper 接口中还可以使用其他注解,例如 @Insert、@Update、@Delete 等,用于定义插入、更新、删除等操作。

5.2、MapperProxyFactory 类

MapperProxyFactory 类是 MyBatis 中用于创建 Mapper 接口代理对象的工厂类。它会根据 Mapper 接口的定义,动态创建一个代理对象,并将 SQL 语句和参数映射到代理方法中。下面我们来分析一下 MapperProxyFactory 的源码实现。

MapperProxyFactory 类有一个构造方法,它接受一个 Class 类型的参数,表示要创建代理对象的 Mapper 接口类。在构造方法中,它会将这个参数保存到类的成员变量中。

public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
}

MapperProxyFactory 类中最重要的方法是 newInstance() 方法,它用于创建 Mapper 接口的代理对象。下面是 newInstance() 方法的源码实现:

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

在 newInstance() 方法中,我们首先创建了一个 MapperProxy 对象,它接受三个参数,分别是 SqlSession 对象、Mapper 接口类和方法缓存。然后,我们调用了另一个重载版本在newInstance() 方法,它接受一个 MapperProxy 对象作为参数,用于创建代理对象。在 newInstance() 方法中,我们使用 Java 动态代理机制,创建了一个代理对象,并将 Mapper 接口类和 MapperProxy 对象传递给代理对象的构造方法。这样,当代理对象调用 Mapper 接口方法时,就会执行 MapperProxy 对象中的逻辑,将 SQL 语句和参数映射到实际的 SQL 语句中,并通过 SqlSession 对象执行 SQL 语句。

5.3、MapperProxy 类

MapperProxy 类是 Mapper 接口的代理类,它会将 Mapper 接口中定义的方法映射到实际的 SQL 语句中,并通过 SqlSession 对象执行 SQL 语句。下面我们来分析一下 MapperProxy 的源码实现。

MapperProxy 类有一个构造方法,它接受三个参数,分别是 SqlSession 对象、Mapper 接口类和方法缓存。在构造方法中,它会将这些参数保存到类的成员变量中,并创建一个 MethodResolver 对象来解析 Mapper 接口中定义的方法。

public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
    this.methodResolver = new MethodResolver(mapperInterface);
}

MapperProxy 类中最重要的方法是 invoke() 方法,它用于调用 Mapper 接口中定义的方法。下面是 invoke() 方法的源码实现:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
}

在 invoke() 方法中,我们首先判断要调用的方法是否是 Object 类中的方法,如果是,则直接调用该方法。否则,我们判断要调用的方法是否是默认方法(即带有 default 关键字的方法),如果是,则调用 invokeDefaultMethod() 方法来执行默认方法。最后,我们从方法缓存中获取 MapperMethod 对象,并调用它的 execute() 方法来执行 SQL 语句。

5.4、MapperMethod 类

MapperMethod 类是 MyBatis 中用于封装 Mapper 接口方法的类,它会将 Mapper 接口方法中定义的 SQL 语句和参数映射信息封装到一个对象中,方便后续执行 SQL 语句。下面我们来分析一下 MapperMethod 的源码实现。

MapperMethod 类有两个构造方法,它们分别用于从注解和 XML 文件中解析 SQL 语句和参数映射信息。在构造方法中,它们会将 SQL 语句和参数映射信息保存到类的成员变量中。

public MapperMethod(Class<?> mapperInterface, Method method, SqlSource sqlSource, SqlCommandType sqlCommandType) {
    this.mapperInterface = mapperInterface;
    this.method = method;
    this.sqlSource = sqlSource;
    this.sqlCommandType = sqlCommandType;
    this.returnType = method.getReturnType();
    this.parameterTypes = method.getParameterTypes();
    this.hasRowBounds = hasRowBounds();
    this.hasResultHandler = hasResultHandler();
    this.hasMapResultHandler = hasMapResultHandler();
    this.resultSetType = getResultSetType(method);
    this.flushCache = isFlushCacheRequired(method);
    this.useCache = isUseCacheEnabled(method);
    this.keyGenerator = getKeyGenerator(method);
    this.keyProperty = getKeyProperty(method);
    this.keyColumn = getKeyColumn(method);
    this.databaseId = getDatabaseId(method);
    this.lang = getLang(method);
    this.resultOrdered = method.getAnnotation(ResultOrder.class) != null;
    this.resultSets = getResultSets(method);
}

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.mapperInterface = mapperInterface;
    this.method = method;
    this.sqlCommandType = getSqlCommandType(method);
    this.sqlSource = new XMLLanguageDriver().createSqlSource(config, method.getAnnotation(MappedStatement.class).sql(), method.getReturnType());
    this.returnType = method.getReturnType();
    this.parameterTypes = method.getParameterTypes();
    this.hasRowBounds = hasRowBounds();
    this.hasResultHandler = hasResultHandler();
    this.hasMapResultHandler = hasMapResultHandler();
    this.resultSetType = getResultSetType(method);
    this.flushCache = isFlushCacheRequired(method);
    this.useCache = isUseCacheEnabled(method);
    this.keyGenerator = getKeyGenerator(method);
    this.keyProperty = getKeyProperty(method);
    this.keyColumn = getKeyColumn(method);
    this.databaseId = getDatabaseId(method);
    this.lang = getLang(method);
    this.resultOrdered = method.getAnnotation(ResultOrder.class) != null;
    this.resultSets = getResultSets(method);
}

在构造方法中,我们使用反射机制获取 Mapper 接口方法的返回类型、参数类型、SQL 语句类型、是否需要刷新缓存、是否使用缓存、是否需要生成主键等信息,并根据这些信息创建一个 MapperMethod 对象。

MapperMethod 类中最重要的方法是 execute() 方法,它用于执行 SQL 语句,并将结果映射到 Java 对象中。下面是 execute() 方法的源码实现:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (sqlCommandType) {
        case INSERT: {
            Object param = getParam(args);
            result = executeInsert(sqlSession, param);
            break;
        }
        case UPDATE: {
            Object param = getParam(args);
            result = executeUpdate(sqlSession, param);
            break;
        }
        case DELETE: {
            Object param = getParam(args);
            result = executeDelete(sqlSession, param);
            break;
        }
        case SELECT:
        default: {
            Object param = getParam(args);
            result = executeSelect(sqlSession, param);
            break;
        }
    }
    if (result == null && returnType.isPrimitive() && !void.class.equals(returnType)) {
        throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + returnType + ").");
    }
    return result;
}

在 execute() 方法中,我们首先根据 SQL 语句类型,调用不同的方法来执行 SQL 语句。例如,对于 INSERT、UPDATE 和 DELETE 操作,我们会调用 executeInsert()、executeUpdate() 和 executeDelete() 方法来执行 SQL 语句,而对于 SELECT 操作,则会调用 executeSelect() 方法来执行 SQL 语句。然后,我们根据方法的返回类型,将执行结果映射到 Java 对象中,并返回该对象。

Mapper 是 MyBatis 中的一个重要组件,它充分利用了 Java 接口的特性,可以让我们通过编写简单的接口方法来实现复杂的 SQL 操作。在 MyBatis 中,Mapper 接口方法会被解析成一个 MapperMethod 对象,该对象封装了 SQL 语句和参数映射信息,并提供了 execute() 方法来执行 SQL 语句。通过对 Mapper 的深入理解,我们可以更好地使用 MyBatis 来操作数据库。

在这里插入图片描述

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/643310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript之ES6高级语法(一)

本文是我在学习过程中记录学习的点点滴滴&#xff0c;目的是为了学完之后巩固一下顺便也和大家分享一下&#xff0c;日后忘记了也可以方便快速的复习。 ES6高级语法&#xff08;一&#xff09; 前言一、垃圾回收机制&#xff08;Garbage Collection&#xff09;1.1、引用计数法…

看病排队问题

目录 一、代码 二、功能函数介绍 三、运行截图 一、代码 #define _CRT_SECURE_NO_WARNINGS 1//不用VS删除这一行 #include<stdio.h> #include<stdlib.h> struct LinkQueue {int data;struct LinkQueue* next; };struct Node {LinkQueue* frount;LinkQueue* re…

docker无法启动 -> 缺少libseccomp

systemctl status docker.servicejournalctl -u dockeryum install -y libseccomplibseccomp是一个用于Linux操作系统的安全计算模式&#xff08;seccomp&#xff09;的用户空间库。seccomp是一种Linux内核特性&#xff0c;允许限制进程可以执行的系统调用&#xff0c;以增加应…

C语言中的基本数据类型

C语言中的基本数据类型分别为以下几种 整型、浮点型、字符类型 整型又分为整型int、短整型short、长整型long 浮点型分为单精度浮点型float、双精度浮点型double 1、短整型short 2.整型 3.长整型 短整型、长整型、整形都是表示整形的&#xff0c;并且输出结果也都为10&…

教你如何批量关闭窗口,省时又省力!

哈喽哈喽&#xff0c;大家好&#xff0c;今天我们来分享一个小功能。 在实际的应用场景中&#xff0c;我们可能需要打开多个窗口&#xff0c;在关闭窗口时&#xff0c;逐个关闭窗口可能比较繁琐&#xff0c;而且有些窗口虽然不再显示了&#xff0c;但可能是隐藏的。在这种情况…

Flutter 库:强大的工具及扩展——nb_utils

Flutter 库&#xff1a;强大的工具及扩展——nb_utils 文章目录 Flutter 库&#xff1a;强大的工具及扩展——nb_utils一、概述1、简介2、功能3、官方资料 二、基本使用1、安装2、基本使用第一步&#xff1a;在 main.dart 中初始化第二步&#xff1a;在您的 MaterialApp 或 Cup…

java的字符输入流

字符流的底层也是字节流。字符流字节流字符集。 特点是输入流一次读一个字节&#xff0c;遇到中文时&#xff0c;一次读多个字节&#xff08;读多少个与字符集有关&#xff09;&#xff1b;输出流底层会把数据按照指定的编码方式进行编码&#xff0c;变成字节再写到文件中。 字…

15个提效的设计类AI生成工具推荐

最近越来越多的AI工具如雨后春笋般涌现。我相信很多设计师会开始使用人工智能工具来帮助我们提高工作效率。 本文整理了15种易于使用的设计类AI工具 即时 AI 即时 AI 是通过自然语言描述&#xff0c;快速生成可编辑的 UI 设计稿的设计工具。 输入文字描述后&#xff0c;即可…

C语言:使用函数完成整型数组的打印、元素逆置、初始化

题目&#xff1a; 创建一个整形数组&#xff0c;完成对数组的操作 1. 实现 函数init() -- 初始化数组为全0 2. 实现 函数print() -- 打印数组的每个元素 3. 实现 函数reverse() -- 函数完成数组元素的逆置 要求&#xff1a;自己设计以上函数的参数&#xff0c;返回值。 思路&a…

AB32VG1:SDK_AB53XX_V061(4)蓝牙音频测试笔记

文章目录 1. 淘宝上两种开发板&#xff0c;有一种的蓝牙功能不正常2. 蓝牙音频测试2.1 《config.h》和《Boombox.setting》两个配置以哪个为准2.2 codeblocks更换链接库2.2.1 这样进入build options是错的2.2.2 build options正确打开方式 2.3.编译工程&#xff0c;下载运行2.3…

kafka 报错 - Cannot assign requested address

背景 在华为云服务器上跑了 zookeeper 和 kafka 的 broker&#xff0c;想内外网分流&#xff0c;重点就是做不到从外网去消费&#xff0c;比如用自己的 windows 笔记本去消费。 配置 server.properties 的 listener 为 broker 所在机子的的内网 IP 后&#xff0c;终于能 star…

Scala环境搭建及安装

salca环境搭建 由于scala是基于java来开发的, 编写的java类可以使用javac命令编译成.class文件被JVM加载到内存中执行 ! 那么scala可以通过scalac命令将编写的scala文件编译成.class文件一样被JVM加载到内存中,因此Scala是运行在JVM平台上的&#xff0c;所以安装Scala之前要安装…

【SpringBoot实战专题】「开发实战系列」全方位攻克你的技术盲区之SpringBoot整合众多日志管理系统服务starter-logging

全方位攻克你的技术盲区之SpringBoot整合众多日志管理系统服务starter-logging 前提介绍Spring默认日志文件Spring的日志配置参数logging.levelmaven配置properties文件yaml文件 logging.fileapplication.yml中配置 logging.pathapplication.properties配置application.yml配置…

基于DBACAN的道路轨迹点聚类

目录 前言道路栅格化轨迹聚类参考资料 前言 很多针对道路轨迹的挖掘项目前期都需要对道路进行一段一段的分割成路段&#xff0c;然后对每一个路段来单独进行考察&#xff0c;如设定路段限速标识&#xff0c;超速概率等&#xff0c;如何对道路进行划分&#xff0c;其实是一个很…

华为OD机试真题 JavaScript 实现【滑动窗口】【2023 B卷 100分】,附详细解题思路

一、题目描述 有一个N个整数的数组&#xff0c;和一个长度为M的窗口&#xff0c;窗口从数组内的第一个数开始滑动直到窗口不能滑动为止&#xff0c;每次窗口滑动产生一个窗口和&#xff08;窗口内所有数和和&#xff09;&#xff0c;求窗口滑动产生的所有窗口和的最大值。 二…

python: read excel

""" Insurance。py edit&#xff1a; geovindu,Geovin Du,涂聚文 date 2023-06-13 保险类 """import sys import osclass Insurance:"""保险类"""def __init__(self, InsuranceName, InsuranceCost, IMonth):&quo…

STM32使用QUADSPI读写外部Nor Flash(以W25Q64为例)

使用QUADSPI读写W25Q64 QUADSPI介绍硬件连接双闪存模式禁止双闪存模式使能 QUADSPI命令序列指令阶段地址阶段交替字节阶段空指令周期阶段数据阶段 QUADSPI主要信号接口协议模式单线SPI模式双线SPI模式四线SPI模式 使用QUADSPI操作W25Q64发送命令函数状态轮询函数读ID函数QUADSP…

应用案例 | FG-200:通过Modbus将FF H1设备集成到DCS系统

一 背景 FOUNDATION Fieldbus&#xff08;FF&#xff09;协议是一种现代化的数字通信协议&#xff0c;其中FF H1协议在大型的化工、电力、石油等流程工业领域得到了广泛应用。由于FF H1协议具有诸多优势&#xff0c;例如高度可靠性、高速数据传输、强大的诊断能力和灵活的设备…

第4章 总体设计

文章目录 第5章 总体设计5.1 设计过程例题 5.2 设计原理5.2.1 模块化模块化的优势 例题5.2.2 抽象5.2.3 逐步求精求精实际上是细化的过程与抽象的关系 5.2.4 信息隐藏和局部化5.2.5 模块独立模块独立的重要性模块独立的定性标准度量耦合① 无直接耦合② 数据耦合③ 标记耦合④ …

MySQL数据库基础 11

第十一章 数据处理之增删改 1. 插入数据1.1 实际问题1.2 方式1&#xff1a;VALUES的方式添加1.3 方式2&#xff1a;将查询结果插入到表中 2. 更新数据3. 删除数据4. MySQL8新特性&#xff1a;计算列 1. 插入数据 1.1 实际问题 解决方式&#xff1a;使用 INSERT 语句向表中插入…