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

博主介绍: ✌博主从事应用安全和大数据领域,有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.mfbz.cn/a/28331.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

社会心理学(1) 社会心理学的定义

今天开始 我们一起学习一门课程 社会心理学 社会心理学 他是 应用心理学 或者 心理学专业的一个必修课 吴江霖教授说过 心理学应该分为两大分支 生理心理学 和 社会心理学 如果认同他的观点 那么 社会心理学可谓是相当重要了 社会心理学的定义之广可以说 有多少社会心理学教…

【MySQL数据库一】MySQL数据库初体验

MySQL数据库初体验 1.数据库基本概念1.1 数据Data1.2 表1.3 数据库1.4 数据库管理系统1.5 数据库系统 2.数据库的发展3.主流的数据库介绍3.1 SQL Server&#xff08;微软公司产品&#xff09;3.2 Oracle &#xff08;甲骨文公司产品&#xff09;3.3 DB2&#xff08;IBM公司产品…

【Spark基础编程】 第8章 Spark MLlib

系列文章目录 文章目录 系列文章目录前言【 第8章 Spark MLlib 】8.1 Spark MLlib简介8.1.1 什么是机器学习8.1.2 基于大数据的机器学习8.1.3 Spark 机器学习库MLLib 8.2 机器学习工作流8.2.1 机器学习流水线概念8.2.2 构建一个机器学习流水线 8.3 特征抽取、转化和选择8.4 分类…

【Linux】进程间的通信之共享内存

进程间的通信之共享内存 一、system V 内存共享原理二、共享内存的使用1、ftok函数2、shmget函数3、shmat函数4、shmdt函数5、shmctl函数6、代码使用 三、一些细节的补充 一、system V 内存共享原理 利用内存共享进行进程间的通信的原理其实分为以下几个步骤&#xff1a; 在物…

Mysql数据库初体验

Mysql数据库初体验 一、数据库的基本概念1.数据&#xff08;Data&#xff09;2.表3.数据库4.数据库管理系统&#xff08;DBMS)5.数据库系统 二、数据库系统发展史1.第一代数据库2.第二代数据库3.第三代数据库 三、当今主流数据库介绍四、数据库分类1.关系数据库2.关系型 SQL 数…

mybatis-plus分页查询(springboot中实现单表和多表查询)

一、mybatis-plus单表查询 使用mybatis-plus实现单表分页查询 非常方便&#xff0c;主要操作步骤如下&#xff1a; 配置分页查询拦截器进行分页查询 1.首先&#xff0c;打开mybatis-plus官网的插件&#xff08;插件主体&#xff09; 或者点击mybatis-plus插件 我是配置在s…

基于Java汽车在线租赁管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

C语言-关键字

关键字就是c语言已经定义好的名字&#xff0c;直接可以拿过来使用&#xff0c;不需要再次定义 1 数据类型相关的关键字 用于定义变量或者类型 定义变量的语法结构&#xff1a; 类型 变量名&#xff1b; 拓展&#xff1a;变量名属于标识符&#xff0c;标识符&#xff08;变量…

希尔贝壳参与构建可信人工智能数据空间,助力大模型行业应用落地

2023年5月30日&#xff0c;由中国信息通信研究院、浙江省经济和信息化厅、杭州市人民政府、中国人工智能产业发展联盟主办的杭州通用人工智能发展论坛在未来科技城圆满落幕。本次会议以“大模型应用机遇和挑战”为主题&#xff0c;众多产学研代表现场参会&#xff0c;共同探讨人…

路径规划算法:基于未来搜索优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于未来搜索优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于未来搜索优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

异常数据检测 | Python实现基于高斯概率分布的异常数据检测

文章目录 文章概述模型描述源码分享学习小结参考资料文章概述 高斯分布也称为正态分布。它可以被用来进行异常值检测,不过我们首先要假设我们的数据是正态分布的。不过这个假设不能适应于所有数据集。但如果我们做了这种假设那么它将会有一种有效的方法来发现异常值。 模型描述…

RK3288 Android8.1添加lvds以及gt9触摸屏(一)

我们公司的屏幕是分为两部分 1.lvds负责屏幕亮起&#xff0c;显示UI 2.gt9触摸屏负责触摸点击反馈操作 现在先说lvds如何配置 RK的LVDS屏调试&#xff0c;主要是配置正确LVDS的dts&#xff0c;配置正确基本都是可以点亮的 1 首先拿到LVDS屏厂商给的屏规格书&#xff0c;规格…

哪个公司的 CEO 不想拥有一个自己的数字克隆?

⚠️ FBI Warning&#xff1a;本文纯属作者自娱自乐&#xff0c;数字人的观点不代表 CEO 本人的观点&#xff0c;请大家不要上当受骗&#xff01;&#xff01; 哪个公司的 CEO 不想拥有一个自己的数字克隆&#xff1f; 想象&#x1f914;一下&#xff0c;如果 CEO 数字克隆上线…

ISP下载原理分析

STM32的启动方式&#xff0c;系统存储器启动就是通过ISP下载 ISP简介 ISP下载是指可以通过串行外设&#xff0c;直接将程序下载Flash中&#xff0c;然后自动运行程序的启动方式。 ISP的时候需要用到bootloder(自举程序)&#xff0c;bootloader存储在STM32内部的自举ROM存储器…

wordpress去除分类URL的categpory

前言 在日常使用Wordpress搭建网站时&#xff0c;发现文章或者分类页的URL地址中默认带有Category&#xff0c;URL层级过长会影响我们网站SEO的优化&#xff0c;也不利于用户体验。这里讲一下如何去除URL中categpory的方法。 操作 第一步先登录到WordPress后台&#xff0c;然…

【论说文】段落与结构

结构和段落 开头和结尾 怎么写开头呢&#xff1f;基本套路就是用三句话。即表达清楚三层意思&#xff0c;第一句话是用简短的话来概括材料&#xff0c;但是不要照抄。写论说文&#xff0c;不是就事论事&#xff0c;而是就事论理。第二句话是过渡句。第三句&#xff0c;写出来中…

IDEA整合GO并创建module工程

IDEA整合Go 安装包环境配置idea配置并创建test mode 安装包 1.去官网下载对应还的安装包 官网下载地址 我选择下载的window 版本&#xff1a; 直接按照对应的目录&#xff0c;然后点击下一步 环境配置 1.配置go环境变量 在高级环境变量PAHT中添加安装包的**/bin 目录&…

Java网络开发(Tomcat同步数据增删改查)—— 用Jsp语法实现同步请求的 增删改查

目录 引出显示所有数据到前端&#xff08;1&#xff09;前端代码&#xff1a;list.jsp&#xff08;2&#xff09;后端代码&#xff1a;CompanyListServlet.java 新增数据---转发类型信息---新增信息业务&#xff08;1&#xff09;在list.jsp页面点击添加&#xff08;2&#xff…

SQL调优:让Java内存分担计算

作者: 剽悍一小兔 CSDN前端优质创作者&#xff0c;打破编程小说次元壁第一人《JavaScript百炼成仙》作者&#xff0c;专注Java硬核干货分享&#xff0c;分享创造快乐&#xff0c;技术成就梦想&#xff01; 我们在工作中&#xff0c;经常会因为一条慢sql调半天。这一节&#xff…

Jenkins集成钉钉通知插件的具体步骤怎么做你知道吗?

最近公司要求工作务必使用钉钉&#xff0c;其他聊天软件不再用于工作沟通了。虽然很抓狂&#xff0c;但是上面的决定不可违逆&#xff0c;只好转战钉钉。虽然强制使用钉钉挺令人反感的&#xff0c;但阿里在这款软件上确实下了些功夫&#xff0c;比如jenkins集成钉钉通知插件后&…
最新文章