关于mybatis的源码分析

  1. 学习的项目结构

  • 对TestMybatis的代码的展示
public class TestMybatis {
 
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session=sqlSessionFactory.openSession();
         
        List<Category> cs=session.selectList("listCategory");
        for (Category c : cs) {
            System.out.println(c.getName());
        }
         
    }
}
  • 第一步获取inputStream 首先会调用Resources的getResourceAsStream(resource)
public static InputStream getResourceAsStream(String resource) throws IOException {
    return getResourceAsStream(null, resource);
  }


//这是真正调用的方法
 public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return in;
  }
  • 在上面代码中可以看到是调用ClassLoaderWrapper中的getResourceAsStream(resource, loader)
 public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
    return getResourceAsStream(resource, getClassLoaders(classLoader));
  }

//真正调用的方法
 InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {
      if (null != cl) {

        // try to find the resource as passed
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) {
          returnValue = cl.getResourceAsStream("/" + resource);
        }

        if (null != returnValue) {
          return returnValue;
        }
      }
    }
    return null;
  }

在上层的getClassLoaders(classLoader)方法中获取Loader的数组

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }

在上面代码中可以看到其实获取inputstream是调用classLoader中的getResourceAsStream(resource)获得我们想要得到的读入,

ClassLoader的获取是根据我们当前运行的线程和class文件获取的。

2、第二部获取SqlSessionFactory其中的目的就是解析mybatis-config.xml

 public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

//调用的方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
//处理读取到的xml配置文件
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

上面的代码可以看到先实例化XMLConfigBuilder然后调用返回build(parser.parse())<build的方法参数是Configation类型的参数>

下面是parser.parse()介绍

//获取标签configuration中的值
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

下面再来看看上面代码中parseConfiguration(parser.evalNode("/configuration"));做了什么

private void parseConfiguration(XNode root) {
    try {
//获取settings标签里的值并用键值对的方式保存
      Properties settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(settings);
//获取配置文件的pojo类信息
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
//获取mapper中的信息
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

下面对调用的settingsAsPropertiess(root.evalNode("settings"))方法查看:


    if (context == null) {
      return new Properties();
    }
//遍历获取所有的property标签中的值
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
//从数据库读取Boolean字符串判定是否设置mybatis的缓存
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  

下面的代码是XMLConfigBuilder然后调用返回build(parser.parse())的build的源码

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

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

可以看到返回的是DefaultSqlSessionFactory

3、获取sqlSession

在获取sqlsession需要调用openSession()

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

}

//openSession()的实现方法
@Override
  public SqlSession openSession() {
//打开对应类型的Executor
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
//上层调用的getDefaultExecutorType()方法
public ExecutorType getDefaultExecutorType() {
    return defaultExecutorType;
  }
//这是mybatis默认的设置
 protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
//ExecutorType 的三个类型
public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}

通过上面的代码获取的是ExecutorType.SIMPLE的类型的session它是处理数据库连接的问题

下面是openSessionFromDataSource()的方法

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
//数据源相关的设置如:数据源的大小时间等
      final Environment environment = configuration.getEnvironment();
//获取TransactionFactory<connection>
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//获取TransactionFactory<connection>
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//将获取到的数据源和SIMPL(ExecutorType)进行处理设置执行sql的缓存和初始化增删改数据库操作的执行器
      final Executor executor = configuration.newExecutor(tx, execType);
//将获取到的参数传入到构造方法中获得session
      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();
    }
  }

对于获取tx的方法详解

//在这里设置sql的数据缓存 
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 {
//初始化SimpleExecutor注意:ExecutorType的类型可以在mybatis中配置
      executor = new SimpleExecutor(this, transaction);
    }
//通过判断cacheEnabled来判断是否开启二级缓存(有配置文件得到)的执行器
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

4、用session执行需要查找的语句List<Category> cs=session.selectList("listCategory");

 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
//获取mapperxml中的信息:sql语句和resultmap
      MappedStatement ms = configuration.getMappedStatement(statement);
//用session中初始化得到的执行器执行查询
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

下面代码查看wrapCollection(parameter)方法做了什么

//如果阐述中包含list或array都封装到map中否则直接返回参数
private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("array", object);
      return map;
    }
    return object;
  }

下面是executor.queryCursor(ms, wrapCollection(parameter), rowBounds)的执行代码

public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
//对从mapperxml中获取的sql语句进行处理
    BoundSql boundSql = ms.getBoundSql(parameter);
    return doQueryCursor(ms, parameter, rowBounds, boundSql);
  }





//调用getBoundSql(parameter)处理sql语句以及resultmap的验证
public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
//将需要跟换的参数放到list容器中
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//若果没有直接返回
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

接下来看执行操作的return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);的源代码

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
//跟新缓存
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

《———   在cache中唯一确定一个缓存项需要使用缓存项的key,Mybatis中因为涉及到动态SQL等多方面因素,其缓存项的key不等仅仅通过一个String表示,所以MyBatis 提供了CacheKey类来表示缓存项的key,在一个CacheKey对象中可以封装多个影响缓存项的因素。

怎么样的查询条件算和上一次查询是一样的查询,从而返回同样的结果回去?这个问题,得从CacheKey说起。 
我们先看一下CacheKey的数据结构      ———》

//对缓存操作 createCacheKey(ms, parameter, rowBounds, boundSql)
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

然后又对她查询return query(ms, parameter, rowBounds, resultHandler, key, boundSql);

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++;
//先判断localCache中有没有有没有什么编辑问key的缓存
      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;
  }

查看list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);中的代码

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
//设置当地缓存作为标记
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
//执行完成移除
      localCache.removeObject(key);
    }
//再重新放到内存中
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }



//doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      flushStatements();
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }



//handler.<E>query(stmt, resultHandler);
 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
//最后到达真正执行sql的地方
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);
  }

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值