看看Mybatis是怎么做的(一)

127 阅读3分钟

介绍哦

Mybatis十分重要的持久层框架,半Orm的代表人物,驰名中外,无敌哦。
文章立志于解析源码,弄懂原理。表述不好,还请见谅。

加载流程

Mybatis加载配置可以使用Xml配置或者代码配置。
结构👇

image.png

一个标准的Mybatis配置需要一个环境和可用的映射文件,而一个Mybatis环境需要一个数据源和一个事务的实例。

    //datasource
    DataSource dataSource = new PooledDataSource("Driver", "url", "username", "password");
    //transaction
    TransactionFactory transactionFactory = new JdbcTransactionFactory();
    //env
    Environment environment = new Environment("development", transactionFactory, dataSource);
    //config
    Configuration configuration = new Configuration(environment);
    configuration.addMappers("mappers");
    //builder
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(configuration);

Datasource

mybatis提供了三种DataSource方式。

image.png

突出一个见名知意 。
当然也可以使用第三方或者自行实现,这里主要突出原理,连接池是Env的一个属性。

Transaction

同样Mybatis提供了两种Transaction的实现方式。

image.png

第一个很好理解依赖于JDBC链接的事务处理方案。
第二个ManagedTransaction目的是使事务管理依赖于容器操作,在这个类中的commit()roolback()方法均为空方法,即不操作事务。那么事务的操作就由其他容器操作。

image.png

image.png

addMapper

添加映射,添加映射有很多重载方法:
public <T> void addMapper(Class<T> type)
public void addMappers(String packageName)
等...
以类的形式添加映射其本质是向Configration.mapperRegistry这个属性中添加类,这个类会创建MapperAnnotationBuilder解析类上的每一个方法的注解。

image.png

image.png

MapperAnnotationBuilder类的parse()方法中会首先根据类的名称去查找同名的.xml文件,如果找到了则创建XMLMapperBuilder去解析Xml文件。
无论是MapperAnnotationBuilder还是XMLMapperBuilder最终都会调用Configuration.addMappedStatement()方法添加回Configuration中。
Configuration中维护着一个Map属性mappedStatements

然而在Xml的配置方式中存在直接添加XMl.statement的方式。在XMLConfigBuilder会解析XMl其中就会创建XMLMapperBuilder解析<mapper/>节点最终也会添加到Configuration中

image.png

SqlSessionFactory

在调用builder方法后就会返回一个DefaultSqlSessionFactory

SqlSession

SqlSession是Mybatis的实际业务执行器,在调用factory.openSession()时做了如下操作

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();
  }
}

这里创建了两个东西,一个是Transaction一个是Executor,一个管理事务,一个处理Sql运行逻辑.
configuration.newExecutor()方法中

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) {
      //开启缓存
    executor = new CachingExecutor(executor);
  }
  //添加插件
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

会根据执行模式创建对应的执行器,以及判断是否开启缓存,以及以代理的形式添加插件。 这里的三个模式分别是:

  1. SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。(默认)
  2. REUSE: 这个执行器类型会复用预处理语句。
  3. BATCH: 这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是必须的,来保证一个简单并易于理解的行为。