小白架构师成长之路9-Mybatis核心概念与源码解析

Mybatis核心概念

Mybatis核心概念

在手写Mybatis前我们先了解一些Mybatis的核心类的用处

Mybaits的核心类有一下几个:Configuration、SqlSessionFactory、SqlSession、Executor、MappedStatement、 StatementHandler、ResultSetHandler

名称 意义
Configuration 管理,读取,解析mybatis-config全局配置关系类
SqlSessionFactory Session管理工厂接口,用来获取SqlSession
SqlSession SqlSession 是一个面向用户(程序员)的接口。SqlSession 中提 供了很多操作数据库的方法
Executor SqlSession 内部通过执行器操作数据库
MappedStatement 对操作数据库存储封装,包括 sql 语句、输入输出参数
StatementHandler 具体操作数据库相关的 handler 接口
ResultSetHandler 具体操作数据库返回结果的 handler 接口

整体认识 mybatis 源码包

​ ├─annotations ->注解相关 比如 @select @insert
​ ├─binding -> mapper 相关
​ ├─builder ->解析 xml 相关
​ ├─cache ->缓存
​ ├─cursor -> 返回结果 resultset
​ ├─datasourcer ->数据管理
​ ├─exceptionsr -> 异常
​ ├─executorr -> 执行器
​ ├─io ->classloader
​ ├─jdbc ->jdbc
​ ├─lang ->jdk7 jdk8
​ ├─logging ->日志相关
​ ├─mapping ->mapper 相关的封装
​ ├─parsing ->xml 相关解析
​ ├─plugin ->拦截器
​ ├─reflection ->反射相关
​ ├─scripting ->数据厂家
​ ├─session ->sessiomn
​ ├─transaction ->事务
​ └─type ->返回类型对应

Mybatis是如何运行的

在这里插入图片描述

大概流程mybatis执行时sqlSessionFactoryBuilder调paese方法解析你传入的mybatis配置文件,解析后会拿到一个Configuration的对象,调用一个build把解析好的Configuration传入你会获取到一个SqlSessionFactory对象,这个对象已经存着Configuration的配置了因为你在用build方法时已经传入,在通过openSession方法时他会获取我们配置文件中的数据库连接参数,开启一个事务,获取一个执行器SimpleExecutor,在获取完SimpleExecutor后还会获取一个CachingExecutor执行器,CachingExecutor非常非常重要,CachingExecutor就是一级缓存,这里可以说明myabtis一级缓存是默认开启的,然后还会通过pluginAll遍历配置文件判断是否有插件,最后我们会获取到一个executor,把executor装回SqlSession中这样我们的SqlSession就已经可以做数据库操作了,接下来我们调用selectOne方法其实底层调用的是executor的query方法,调用query方法前会通过一个你传入的statement去获取MappedStatement存放返回值类型,已经sql的索引,然后调用query方法,通过索取去获取sql语句,并且创建一个缓存createCacheKey,继续调用query调之前他会去先去通过id查询Cache看有没有值,因为我们是第一次查询所以里面是没有值的所以他会继续调用delegat.query方法真正的查询数据库方法,这时候他会new一个StatementHandler并调用他的query方法(真正的查询在这里)最后会返回ResultSetHandler我们的结果集

debug跑一遍Mybatis

经过上面的简介想必大家也大概了解了mybatis是怎么运作,但是光说还是比较抽象,现在跟着我的脚本我们来一段一段的debu跑一遍mybatis

首先我们编写一个test方法

@Test //使用xml编写sql查询
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //第一步:读取加载resources下的mybatis-config.xml配置文件
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //第二步:通过sqlSessionFactory获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //第三步:com.lzj.test.mybatis.mybatis.mapper.UserMapper  UserMapper.xml下的id为selectUser的语句
        User user = sqlSession.selectOne("com.lzj.test.mybatis.mybatis.mapper.UserMapper.selectUser", 1);
        System.out.println("查询结果:"+user);
    }

第一步获取sqlSessionFactory

首先我们进入SqlSessionFactoryBuilder.build方法(idea使用鼠标的中键点击方法即可进入该类下的方法)

//首先我们会去到org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)
public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
}
//继续点build我们会去到org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)这里就是我们的buid的主体方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //把配置文件的流放入XMLConfigBuilder中
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //parser.parse() 读取配置文件读取完后会返回一个Configuration对象 调用build方法
      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.
      }
    }
  }
//org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)
public SqlSessionFactory build(Configuration config) {
    //parser.parse()调用完后 会把结果buid进来 这里new DefaultSqlSessionFactory返回SqlSessionFactory
    return new DefaultSqlSessionFactory(config);
  }

所以我们第一步会得到一个sqlSessionFactory

//第一步:读取加载resources下的mybatis-config.xml配置文件
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

第二步获取sqlSession

首先我们进入sqlSessionFactory.openSession()方法(idea使用鼠标的中键点击方法即可进入该类下的方法)

//我们会发现他其实是一个接口org.apache.ibatis.session.SqlSessionFactory#openSession()
//我们进入他的实现类DefaultSqlSessionFactory
//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()
@Override
public SqlSession openSession() {
    //我们可以看到他调用了openSessionFromDataSource方法
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
//返回一个sqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //通过configuration获取environment对象该对象里面存着DataSource
      final Environment environment = configuration.getEnvironment();
       //开启一个事务 把数据库连接信息传入事务
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //获取executor执行器 
      final Executor executor = configuration.newExecutor(tx, execType);
      //调用DefaultSqlSession构造方法
      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();
    }
  }

所以我们第二步会得到一个sqlSession

//第二步:通过sqlSessionFactory获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

第三步获取结果

首先我们进入sqlSession.selectOne()方法(idea使用鼠标的中键点击方法即可进入该类下的方法)

//进入后我们发现是一个接口我们进入他的实现类DefaultSqlSessionFactory
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)
@Override
  public <T> T selectOne(String statement, Object parameter) {
    // 我们进入selectList方法
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)
@Override
public <E> List<E> selectList(String statement, Object parameter) {
    //继续往下找
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //statement="com.lzj.test.mybatis.mybatis.mapper.UserMapper.selectUser" 这是我们传进来的
      //这里就通过getMappedStatement获取我们UserMapper.xml文件下id=selectUser中的信息,信息内容可查看MappedStatement对象
        MappedStatement ms = configuration.getMappedStatement(statement);
        //调用执行器的query方法
        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();
    }
  }
//org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //这里就取出我们的sql语句了
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //创建一级缓存key=id+Offset+Limit+Sql 如果想看这个方法可以去到org.apache.ibatis.executor.BaseExecutor#createCacheKey
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
   	//调用另外一个query方法
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
    //通过MappedStatement取cache信息判断sql是否有调过
    Cache cache = ms.getCache();
    //如果有信息表示sql有调用过
    if (cache != null) {
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
                list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    //不管有没有最后还是调用了delegate.<E> query 我们去到query下
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
@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++;
        //这里是判断resultHandler有没有参数是走缓存还是走数据库
        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;
}
//org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
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 {
        //调用doQuery查询
        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;
}
//org.apache.ibatis.executor.SimpleExecutor#doQuery
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        //创建一个 StatementHandler 里面有具体的操作数据库
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = prepareStatement(handler, ms.getStatementLog());
        //调用StatementHandler 的query 方法
        return handler.<E>query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}
//org.apache.ibatis.executor.statement.SimpleStatementHandler#query
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //把sql语句获取出来
    String sql = boundSql.getSql();
    //这里的execute用的是package java.sql的包 真正的查询
    statement.execute(sql);
    //调用handleResultSets 用于映射
    return resultSetHandler.<E>handleResultSets(statement);
}
//org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets
//这里一大串就不说了使用了反射把获取到的值反射到Object
@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
	//调用collapseSingleResultList
    return collapseSingleResultList(multipleResults);
  }
//org.apache.ibatis.executor.resultset.DefaultResultSetHandler#collapseSingleResultList
private List<Object> collapseSingleResultList(List<Object> multipleResults) {
    //判断有没有值 有就返回object
    return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}

所以我们第三步会得到一个填充好数据的user

//com.lzj.test.mybatis.mybatis.mapper.UserMapper  UserMapper.xml下的id为selectUser的语句
        User user = sqlSession.selectOne("com.lzj.test.mybatis.mybatis.mapper.UserMapper.selectUser", 1);
        System.out.println("查询结果:"+user);

总结

经过第一第二第三步的简介大家对mybatis应该有更加深入的了解了把,下一篇文章我们一起来实现一个简单查询版的mybatis

小白架构师成长之路10-实现自己Mybatis

发布了33 篇原创文章 · 获赞 22 · 访问量 962

猜你喜欢

转载自blog.csdn.net/weixin_44642403/article/details/103747091