Mybatis源码笔记(一)、SqlSessionFactory

MyBatis 是基于 Java 的数据持久层框架,主要解决数据和对象的映射关系。相较于 Hibernate,因为其简单轻量,所以使用也比较广泛,是主流的 ORM 框架之一。与 hibernate 相比,mybatis 不能完全算得上是一个完全的 ORM 框架。因为他只完成了一半。不过话说回来,之所以大家喜欢用它,很大一部分原因就是基于它的轻量便捷。

闲言少叙,开刀问斩。首先我们谈一谈传统方式是怎样使用的。传统意义上讲,连接数据库的方式比较常用 JDBC 大致如下:

        ......
        try {
            // 加载驱动
            Class.forName(driver);
            // 获取connection连接
            Connection conn = DriverManager.getConnection(url,user,password);
            // 获取statement对象
            Statement statement = conn.createStatement();
            try {
                // 控制事务提交
                conn.setAutoCommit(false);
                // 执行SQL语句
                statement.executeUpdate("UPDATE USER SET AGE=1 WHERE ID=1");
                // 事务提交
                conn.commit();
            } catch (Exception e) {
                // 异常回滚
                conn.rollback();
                throw e;
            } finally {
                // 关闭statement
                if (statement != null) {
                    statement.close();
                    statement = null;
                }
                // 关闭connection
                if (conn != null) {
                    conn.close();
                    conn = null;
                }
            }

相较于传统的 JDBC 方式,我们使用 mybatis 略有不同,不过本质还是一样使用 JDBC。使用 mybatis 我们不在手动获取 Connection 连接,而是将它交由 mybatis 来管理。在 mybatis 中使用 SqlSession 来管理资源的连接与关闭,同时通过 SqlSession 来进行各种增删改查操作。

具体如何实现呢,我们需要使用 SqlSessionFactory 来管理 SqlSession,SqlSessionFactory 接口主要是用于打开 session 获取 SqlSession 对象的,具体的实现方法在实现类对象中实现


// SqlSessionFactory接口 public interface SqlSessionFactory { SqlSession openSession(); SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration(); }

那 SqlSession 究竟是如创建的呢?我们以 spring 容器管理为例,通过该 SqlSessionFactoryBean 创建 SqlSessionFactory,通过 SqlSessionFactory 获取 SqlSession:

public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean,
ApplicationListener<ApplicationEvent> {

实现了 InitializingBean 接口,追踪到 afterPropertiesSet()方法

  @Override
  public void afterPropertiesSet() throws Exception {
    ......
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

这里的 sqlSessionFactory 通过 buildSqlSessionFactory 方法创建

  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
// configuration不为空,直接加载
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
//   否则通过configLocation加载
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
//   以上均不能加载,则默认new一个Configuration对象塞进去
    } else {
      LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
// 注册类型别名包
    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType)
          .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }
// 注册类型别名
    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }
// 注册各种插件
    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }
// 注册类型处理包以及类型处理器
    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream()
          .filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .filter(clazz -> ClassUtils.getConstructorIfAvailable(clazz) != null)
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }
// 根据databaseIdProvider获取数据库类型
    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }
// 注册事务
    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));
// 通过mapperLocations加载**.Mapper.xml文件,解析mapper文件存入Configuration对象的属性中
    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }
// 通过上面的配置创建SqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }
// 这里返回的是SqlSessionFactory默认实现类DefaultSqlSessionFactory,通过SqlSessionFactory我们就可以拿到sqlSession了
    public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

总结:

如果我们自己在单个项目中使用 mybatis 也是很简单的,通过上面的代码可以看到,我们无非就是想获取一个 SqlSessionFactory,通过 SqlSessionFactory 进一步获取 SqlSession,从而执行具体的增删改查。所以我们可以按照上述思路,构建好 Configuration 对象,直接 new 出 SqlSessionFactory。下一节,我们将分析一下 SqlSession。

发表评论

邮箱地址不会被公开。 必填项已用*标注