mybatis架构原理

2020-01-01  本文已影响0人  月伴沧海

mybatis是大家经常用到的ORM框架,之前一直使用却没有静下来心来好好整理下,今天抽空来整理整理框架以及流程,以便后续回顾学习使用。

本文参考的mybatis版本为:mybatis-3.5.2

1、首先来看下核心架构图

image

核心模块图

接口层:主要是sqlSession封装增删改查接口,提供给应用调用

核心层:核心层主要功能为配置解析、参数映射处理、动态SQL、核心执行引擎、以及结果映射处理等

基础层:包括缓存的封装、日志、反射机制、连接池管理等

2、执行流程图

1、贴一段正确调用的代码,方便理解流程图:

public static void main(String[] args) {
SqlSession sqlSession =null;
try {

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");

SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
// 打开session
sqlSession = sqlSessionFactory.openSession();
//查询数据库中数据
sqlSession.selectOne("XXX.getOrder",1);

}catch (Exception e) {
    e.printStackTrace();
}finally {
    if (sqlSession !=null) {
    sqlSession.close();
}
}
}

下面是主要流程:

image.png

3、主要类、接口以及职责

上面的流程一些同学看了觉得怎么那么简单?是的,主流程就是这么简单,但是里面的执行逻辑还是有点复杂,下面咱们分析主要核心流程内部是怎么处理的。

image

加载XML主流程入口

1、解析mybatis.xml,通过一组ClassLoaders以及文件路径,解析mybatis.xml并返回InputStream流

image

通过classload解析XML

2、创建SqlSessionFactory,通过SqlSessionFactoryBuilder的名字可以看出这是一个建造者设计模式。

进入核心代码:


public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        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) {
    }
  }
}

通过XMLConfigBuilder的parse方法,生成Configuration对象,然后生成SqlSessionFactory


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

3、打开SqlSession


public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(),null,false);
}

通过getDefaultExecutorType我们可以跟踪到,默认使用的执行器类型是simple
protected ExecutorTypedefaultExecutorType = ExecutorType.SIMPLE;
下面是核心的获取sqlSession的过程:


private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,boolean autoCommit) {

    Transaction tx =null;

    try {
        //从configuration中获取以及解析完成的环境变量信息,Environment中包含事务 工厂以及数据源对象
        final Environment environment =configuration.getEnvironment();
        //如果environment的事务工厂对象为空,则新建一个,否则返回environment中的事务对象
        final TransactionFactory transactionFactory =getTransactionFactoryFromEnvironment(environment);
        //根据数据源,事务级别以及是否自动提交标识,创建事务对象
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //创建核心SQL执行器
        final Executor executor =configuration.newExecutor(tx, execType);
        //返回SqlSession
        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();
    }
}

下面看下final Executor executor =configuration.newExecutor(tx, execType);具体做了哪些事情

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        //下面2行代码没太看明白,第一行已经判断是非为空并设置默认值了,第二行executorType肯定不为空,这目的是什么?哪位老铁看懂了奥妙可以留言,不胜感激!!!
        executorType = executorType ==null ?defaultExecutorType : executorType;//1

        executorType = executorType ==null ? ExecutorType.SIMPLE : executorType;//2

        Executor executor;

        if (ExecutorType.BATCH == executorType) {
            //是否批量处理
            executor =new BatchExecutor(this, transaction);
        }else if (ExecutorType.REUSE == executorType) {
            //ReuseExecutor为可重用执行器,无需释放
            executor =new ReuseExecutor(this, transaction);
        }else {
            //最后兜底的就是SimpleExecutor啦,也是默认类型
            executor =new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
            //如果开启了缓存,则使用缓存执行器,上面的流程就忽略了
            executor =new CachingExecutor(executor);
        }
            //此处需要注意,是将执行器添加到所有插件拦截器里,所有的拦截器都能拦截到excutor的执行信息,具体看源码是通过JDK的动态代理实现的,此处就不展开讲解了
            executor = (Executor)interceptorChain.pluginAll(executor);
            return executor;
}

4、通过DefaultSqlSession执行SQL,以查询一条数据为例


@Override
public T selectOne(String statement, Object parameter) {
      // Popular vote was to return null on 0 results and throw exception on too many.
      List list =this.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;
     }
}

public List selectList(String statement, Object parameter, RowBounds rowBounds) {

    try {
        MappedStatement ms =configuration.getMappedStatement(statement);
        //executor终于开始干活了
        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();
    }
}

@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)throws SQLException {
    //获取装订好的SQL
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

执行查询


public List 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 list;
    try {
        queryStack++;
        list = resultHandler ==null ? (List)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();
    }
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      clearLocalCache();
    }
  }
    return list;
}

小结:

核心类以及职责:

SqlSessionFactoryBuilder:创建SqlSessionFactory工厂,此类可以被实例化、使用和丢弃,最佳使用就是用在局部方法变量

SqlSessionFactory:创建SqlSession,SqlSessionFactory最佳实践是在应用运行期间不要重复创建多次。

SqlSession :包含了面向数据库执行 SQL 命令所需的所有方法,增删改查等操作。另外SqlSession 非线程安全,所以不能线程间共享

Configuration:配置类,目的是为了在调用过程中从JVM的Heap内存中获取,防止每次都需要加载XML文件

Executor:SQL执行器,包括:BatchExecutor、ReuseExecutor、SimpleExecutor、CachingExecutor。 SqlSession是对外提供接口使用,Executor则是对DB使用

StatementHandler:Statement处理器,封装了Statement的各种数据库操作方法execute()

ResultSetHandler:返回结果集处理器,对返回的结果包装成具体的对象,完成ORM操作

上一篇 下一篇

猜你喜欢

热点阅读