likes
comments
collection
share

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession

作者站长头像
站长
· 阅读数 27

概述:

创建
生产
执行
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
SQL操作和事务管理

SqlSessionFactoryBuilderSqlSessionFactorySqlSession 是 MyBatis 框架中负责数据库操作和会话管理的核心组件。

  • SqlSessionFactoryBuilder 是一个建造者模式的实现,它负责解析配置文件和构建 SqlSessionFactory 实例。这个过程通常在应用程序启动时执行一次,因为 SqlSessionFactory 的创建是资源密集型的,并且它的设计是作为一个单例存在。

  • SqlSessionFactory 是一个工厂模式的实现,提供了一个创建 SqlSession 实例的方法。SqlSessionFactory 作为核心配置的持有者,保证了 MyBatis 的配置信息在应用程序运行期间只被加载和解析一次。

  • SqlSession 是 MyBatis 与数据库交互的主要接口,封装了执行 SQL 命令、管理事务和获取映射器(Mapper)的操作。它的生命周期应该是一个请求或事务的范围,确保每次数据库操作都是在一个干净的环境中进行。

在整个 MyBatis 的使用流程中,SqlSessionFactoryBuilder 构建 SqlSessionFactorySqlSessionFactory 生产 SqlSession,而 SqlSession 则是执行具体的 SQL 操作和事务管理的地方。这三者的设计和协作体现了 MyBatis 对数据库访问层的简化和灵活性,同时也允许开发者保持对 SQL 的完全控制。

creates
produces
SqlSessionFactoryBuilder
+build(Configuration config) : SqlSessionFactory
SqlSessionFactory
+openSession() : SqlSession
SqlSession
-execute SQL statements
-manage transactions

MyBatis 的设计理念之一是提供一个简单的编程 API,同时允许开发者保持对 SQL 语句的完全控制。 为了实现这一点,MyBatis 使用了工厂模式、建造者模式和模板方法模式等设计模式。

代码讲解:

下面是 SqlSessionFactoryBuilderSqlSessionFactorySqlSession 的实现原理和设计理念的细化讲解。

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder 使用了建造者模式,它的主要职责是基于配置信息构建并返回一个 SqlSessionFactory 实例。建造者模式使得对象的构建过程和表示方式分离,提供了更大的构建灵活性。

在 MyBatis 中,SqlSessionFactoryBuilder 通常使用 XML 配置文件或者注解方式来构建 SqlSessionFactory。它会解析配置文件,创建 Configuration 对象,该对象包含了所有的配置信息,如数据源、事务管理器、映射文件位置等。这个过程涉及到解析 XML、注册映射器、解析注解等一系列操作。

SqlSessionFactory

SqlSessionFactory 是一个接口,它定义了创建 SqlSession 的方法。SqlSessionFactory 的实现类(如 DefaultSqlSessionFactory)持有配置信息 Configuration 对象,并负责创建 SqlSession 实例。

SqlSessionFactory 的设计理念是单例模式,即在应用程序的生命周期中只存在一个 SqlSessionFactory 实例。这是因为 SqlSessionFactory 的创建过程通常是资源密集型的,且其内部包含了所有的配置信息,因此没必要重复创建。

SqlSession

SqlSession 是 MyBatis 提供的一个核心接口,它封装了与数据库交互的所有方法,包括执行 SQL 语句、提交或回滚事务和获取映射器(Mapper)实例。SqlSession 的实现类(如 DefaultSqlSession)通过内部的 Executor 对象来执行 SQL 语句。

SqlSession 的设计理念是遵循模板方法模式,它提供了一个执行数据库操作的模板,而具体的执行逻辑由 Executor 实现。这种设计允许 MyBatis 轻松地支持不同的执行策略,如批处理执行、简单执行和缓存执行。

实现原理

  1. 配置解析:当使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory 时,MyBatis 首先解析配置文件,创建 Configuration 对象,并初始化所有配置信息。
  2. SqlSessionFactory 创建:SqlSessionFactoryBuilder 通过配置信息构建 DefaultSqlSessionFactory 实例,该实例将被用来创建 SqlSession
  3. SqlSession 创建:调用 SqlSessionFactory.openSession() 方法,会创建一个 DefaultSqlSession 实例。这个实例包含了执行 SQL 操作所需的所有方法,并且通过 Executor 实现实际的数据库交互。

设计理念

  • 分离关注点:通过将配置解析、会话工厂创建和数据库会话管理分离,MyBatis 使得每个组件都专注于自己的职责,从而提高了代码的可维护性和可扩展性。
  • 灵活性和控制权:MyBatis 允许开发者通过 XML 或注解自定义 SQL 语句,这提供了对 SQL 的细粒度控制,并且可以灵活地应对复杂的数据库操作。
  • 易用性和简洁性:MyBatis 的 API 设计简洁,易于理解和使用,同时隐藏了底层的复杂性,如事务管理和资源释放。

MyBatis 的设计理念和实现原理相结合,提供了一个既强大又灵活的持久层框架,它允许开发者以最接近 SQL 的方式进行数据库操作,同时提供了 ORM 框架的便利

要深入理解 SqlSessionFactoryBuilderSqlSessionFactorySqlSession 的实现原理,需要查看 MyBatis 源码。 以下是基于 MyBatis 3.4.5 版本的源码解读。请注意,由于源码较长,这里只提供关键部分的伪代码和解释。

SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {   
 public SqlSessionFactory build(Reader reader) {
        // 使用 XMLConfigBuilder 解析配置文件
        XMLConfigBuilder parser = new XMLConfigBuilder(reader);
        return build(parser.parse());
    }

    public SqlSessionFactory build(Configuration config) {
        // 创建 DefaultSqlSessionFactory 实例
        return new DefaultSqlSessionFactory(config);
    }
}

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession 在上述代码中,SqlSessionFactoryBuilder 提供了多个 build() 方法的重载,用于从不同的配置源创建 SqlSessionFactoryXMLConfigBuilder 负责解析 XML 配置文件,并将解析结果填充到 Configuration 对象中。 最终,SqlSessionFactoryBuilder 使用这个 Configuration 对象来创建 DefaultSqlSessionFactory 实例。

SqlSessionFactory

public interface SqlSessionFactory {

    SqlSession openSession();

    // ... 其他方法
}

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession SqlSessionFactory 接口类,定义各类实现openSession 方法

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

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

    public SqlSession openSession() {
        // 创建 Executor,Executor 是 MyBatis 的核心调度器
        Executor executor = configuration.newExecutor();
        // 创建 DefaultSqlSession 实例
        return new DefaultSqlSession(configuration, executor);
    }
}

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession

SqlSessionFactory 接口定义了 openSession() 方法,用于创建 SqlSessionDefaultSqlSessionFactory 是它的实现类,持有 Configuration 对象。在 openSession() 方法中,它首先创建一个 Executor 实例,然后使用这个 ExecutorConfiguration 创建 DefaultSqlSession

3.4.5版本中的mybatis openSession调用openSessionFromDataSource或openSessionFromConnection进行创建DefaultSqlSession

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession

SqlSession

public interface SqlSession extends Closeable {

    <T> T selectOne(String statement, Object parameter);

    // ... 其他方法
}

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession

public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;
    private final Executor executor;

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

    public <T> T selectOne(String statement, Object parameter) {
        // 调用 Executor 执行查询
        return executor.query(...);
    }

    // ... 实现其他方法
}

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession

SqlSession 接口定义了数据库操作所需的方法,如 selectOne()DefaultSqlSession 是它的实现类,它组合了 ConfigurationExecutor。在 selectOne() 方法中,它实际上委托给 Executor 来执行查询。

Executor

public interface Executor {

    <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler);

    // ... 其他方法
}

public class BaseExecutor implements Executor {

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

    // ... 实现其他方法
}

读懂MyBatis中的SqlSessionFactoryBuilder、SqlSessionFactory 和SqlSession Executor 接口定义了 SQL 执行的方法,BaseExecutor 是它的一个实现。在 query() 方法中,它负责处理查询逻辑,包括可能的缓存处理、参数处理和 SQL 语句构建。

重点讲解一下这个段代码:

这段代码是 MyBatis 中 Executor 接口的一个实现类BaseExecutor,它是执行查询操作的核心逻辑。下面是对这段代码的逐行解读:

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

这是 query 方法的签名,它被覆盖(Override)以提供具体的实现。这个方法返回一个 List<E>,即查询结果的列表。MappedStatement 包含了 SQL 语句及其相关配置信息。parameter 是 SQL 语句的参数。RowBounds 用于分页。ResultHandler 用于处理查询结果。CacheKey 是缓存键。BoundSql 包含了最终要执行的 SQL 语句和参数。

    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());

这行代码设置了错误上下文(ErrorContext),以便在发生异常时提供有关资源、正在执行的活动和对象(通常是 SQL 语句的 ID)的详细信息。

    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }

检查 Executor 是否已关闭,如果已关闭,则抛出 ExecutorException 异常。

    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }

在查询开始之前,如果这是最外层的查询(queryStack 为 0),并且 MappedStatement 指示需要清空缓存,则清空本地缓存。

    List<E> list;
    try {
      queryStack++;

声明一个用于存储结果的列表,并且在尝试执行查询之前,增加 queryStack 的值,表示当前有一个活跃的查询。

      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;

如果没有提供 ResultHandler,则尝试从本地缓存中获取结果。如果结果存在,则不需要访问数据库。

      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }

如果从缓存中获取到了结果,则处理缓存的输出参数。如果缓存中没有结果,则调用 queryFromDatabase 方法从数据库查询数据

    } finally {
      queryStack--;
    }

无论查询是否成功,最终都会减少 queryStack 的值,表示当前查询已完成。

    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();

如果所有的查询都已执行完毕(queryStack 为 0),则执行所有延迟加载的结果,并清空延迟加载列表。

      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }

如果配置了在语句级别清除本地缓存,则在查询结束后清除缓存。

    return list;
  }

返回查询结果列表。

这段代码展示了 MyBatis 如何处理查询请求,包括缓存的使用、延迟加载的处理以及错误上下文的管理。这是一个典型的模板方法模式的应用,其中 queryFromDatabase 方法可能被子类覆盖以提供不同的查询行为。

总结

SqlSessionFactoryBuilderSqlSessionFactorySqlSession 的设计和实现体现了 MyBatis 的核心原则:保持简单和灵活性。通过分离配置解析、会话工厂创建和数据库会话管理,MyBatis 提供了易于使用的 API,同时允许开发者对 SQL 保持完全控制。Executor 作为 SQL 执行的核心组件,隐藏了底层的复杂性,如事务控制和缓存管理,使得开发者可以专注于业务逻辑。

在实际应用中,开发者通常与 SqlSessionFactorySqlSession 直接交互,而 SqlSessionFactoryBuilder 主要在应用启动时使用一次。这种设计使得 MyBatis 在易用性和性能之间取得了平衡,为 Java 应用提供了一个高效的数据访问层。

转载自:https://juejin.cn/post/7363121890792161314
评论
请登录