Mybatis源码主流程分析

6,429 阅读5分钟

背景介绍

Mybatis 是一个 Data Mapper Framework,属于 ORM 框架;旨在提供更简单,更方便地完成操作数据库功能,减轻开发⼈员的⼯作量,消除程序冗余代码。

功能框架

主要从功能⽅面配合源码进行解析 Mybatis 的实现过程和原理,功能框架图如下:

接⼝层:

  1. 使用 Mybatis 提供 API 操作数据库,传递 statementId 和参数 map 给 sqlSession;

  2. 使⽤ mapper 接⼝⽅式操作数据库,每个接⼝口⽅方法对应⼀一个 mapper 节点<insert/select/update/delete>;

处理层:

  1. 处理传入参数,同时进行类型转换 —— ParameterHandler 分析

  2. 根据传入参数,使⽤ ognl 动态生成 SQL 语句 —— StatementHandler 分析

  3. 将 SQL 语句和参数交于执行器进⾏获取结果 —— Executor 分析

  4. 处理结果集,类型转换 —— ResultSetHandler 分析

框架层:

  1. 数据源和连接池管理理 —— 数据源与连接池分析

  2. 事务管理理 —— 事务分析

  3. 缓存管理理(⼀级缓存和二级缓存) —— 缓存分析

  4. SQL 语句配置⽅式管理(XML和注解) —— 配置⽅方式分析

Mybatis 中⽐较核心重要且常⽤的类

  • Configuration MyBatis 所有的配置信息都维持在 Configuration 对象之中。
  • TypeHandler 负责 java 数据类型和 jdbc 数据类型之间的映射和转换。
  • MappedStatement 维护了一条 <select|update|delete|insert> 节点的封装。
  • StatementHandler 封装了 JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数、将 Statement 结果集转换成 List 集合。
  • ParameterHandler 负责对⽤户传递的参数转换成 JDBC Statement 所需要的参数。
  • SqlSource 负责根据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 对象中,并返回。
  • BoundSql 表示动态生成的 SQL 语句以及相应的参数信息。
  • Executor Mybatis 执行器,是 Mybatis 调度的核心,负责 SQL 语句的生成和查询缓存的维护工作。
  • SqlSession 作为 Mybatis 工作的主要顶层 API,表示和数据库交互的会话,完成必要数据库增删改查功能。
  • ResultSetHandler 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。

Mybatis 使用

 // 1.初始化
 String resource = "mybatis-config.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); inputStream.close(); 
 // 2.获取sqlSession对象
 SqlSession session = sqlSessionFactory.openSession(); 
 // 3.获取dao代理对象
 DspUserDao dspUserDao = session.getMapper(DspUserDao.class); 
 // 4.执行sql
 DspUser user = dspUserDao.selectUserByName("test"); System.out.println(user.getUserName()); 

源码分析

配置文件初始化流程

1. SqlSessionFactoryBuilder

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 

SqlSessionFactoryBuilder 为⼊口点进行传⼊解析 xml。

2. XMLConfigBuilder

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 
build(parser.parse()); 

将 XML 进⾏节点解析,封装为 configuration 对象。

 private void parseConfiguration(XNode root) { 
     try { 
         // 解析properties
         this.propertiesElement(root.evalNode("properties"));
         // 解析settings,设置默认值
         Properties settings = this.settingsAsProperties(root.evalNode("settings")); 
         this.loadCustomVfs(settings);
         // 解析typeAliases,设置别名 
         this.typeAliasesElement(root.evalNode("typeAliases"));
         // plugins,设置拦截器链
         this.pluginElement(root.evalNode("plugins")); 
         this.objectFactoryElement(root.evalNode("objectFactory")); 
         this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectorFactoryElement(root.evalNode("reflectorFactory")); this.settingsElement(settings);
         // environments,设置数据源,tx相关信息 
         this.environmentsElement(root.evalNode("environments")); 
         this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
         // 解析typeHandlers,类型处理类 
         this.typeHandlerElement(root.evalNode("typeHandlers"));
         // 解析mapper,处理sql相关信息
         this.mapperElement(root.evalNode("mappers")); 
         } catch (Exception var3) {
             throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); 
         } 
} 

3.sql.xml解析⼊口

 public void parse() {
     if (!this.configuration.isResourceLoaded(this.resource)) { 
         this.configurationElement(this.parser.evalNode("/mapper"));//Map<String, MappedStatement> mappedStatements 
         this.configuration.addLoadedResource(this.resource);
         this.bindMapperForNamespace();//Map<Class<?>, MapperProxyFactory<?>> knownMappers 
     } 
     this.parsePendingResultMaps(); 
     this.parsePendingCacheRefs(); 
     this.parsePendingStatements(); 
 }  

获取 sqlSession 流程

1. DefaultSqlSessionFactory

 public SqlSession openSession() {
     return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); 
 }
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 
     Transaction tx = null; 
     DefaultSqlSession var8; 
     try { 
         Environment environment = this.configuration.getEnvironment();
         TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
         tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); 
         Executor executor = this.configuration.newExecutor(tx, execType);
         var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); 
     } catch (Exception var12) {
         this.closeTransaction(tx);
         throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); 
     } finally {
         ErrorContext.instance().reset(); 
     } 
     return var8; 
} 

2. configuration创建executor对象

 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 
 executorType = executorType == null ? this.defaultExecutorType : executorType; 
 executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Object 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 (this.cacheEnabled) {
         executor = new CachingExecutor((Executor)executor); 
     } 
     // 此处拦截器调用链加载
     Executor executor = (Executor)this.interceptorChain.pluginAll(executor); 
     return executor; 
}  

获取 dao 代理理对象流程

1. DefaultSqlSession获取mapper代理理对象

 public <T> T getMapper(Class<T> type) {
     return this.configuration.getMapper(type, this); 
 }
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 
     return this.mapperRegistry.getMapper(type, sqlSession); 
 } 
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); 
     if (mapperProxyFactory == null) { 
         throw new BindingException("Type " + type + " is not known to the MapperRegistry."); 
     } else { 
         try {
             return mapperProxyFactory.newInstance(sqlSession); 
         } catch (Exception var5) {
             throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); 
         } 
 } 

2. MapperProxyFactory 创建 mapperProxy 的代理理对象

 protected T newInstance(MapperProxy<T> mapperProxy) {
     return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); 
 } 
 public T newInstance(SqlSession sqlSession) {
     MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); 
     return this.newInstance(mapperProxy); 
 } 

执行查询流程

1. mapperProxy 代理理对象调⽤用 invoke

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
     try { 
         // 若执⾏的object类的方法,直接调⽤
         if (Object.class.equals(method.getDeclaringClass())) { 
             return method.invoke(this, args); 
          } 
         if (this.isDefaultMethod(method)) {
             return this.invokeDefaultMethod(proxy, method, args); 
         }
      } catch (Throwable var5) { 
         throw ExceptionUtil.unwrapThrowable(var5); 
      } 
      // 缓存中获取MapperMethod
     MapperMethod mapperMethod = this.cachedMapperMethod(method);
     return mapperMethod.execute(this.sqlSession, args); 
} 

2. 创建 MapperMethod 执⾏操作

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { 
    this.command = new MapperMethod.SqlCommand(config, mapperInterface, method); 
    this.method = new MapperMethod.MethodSignature(config, mapperInterface, method); 
 } 
 // 根据sql类型进⾏执行
 public Object execute(SqlSession sqlSession, Object[] args){ 
     ....省略 
     param = this.method.convertArgsToSqlCommandParam(args); 
     result = sqlSession.selectOne(this.command.getName(), param);
      ....省略 
 } 

3. DefaultSqlSession 执⾏ select 操作

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 
    List var5; 
     try {
         // 从configuration中获取MappedStatement对象 
         MappedStatement ms = this.configuration.getMappedStatement(statement);
         // 通过executor执行MappedStatement
         var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 
     } catch (Exception var9) {
         throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); 
     } finally { 
         ErrorContext.instance().reset(); 
     } 
     return var5; 
 } 

4. Executor

baseExecutor类

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { 
    // 获取动态⽣成的SQL语句以及相应的参数信息
     BoundSql boundSql = ms.getBoundSql(parameter);
     // 缓存当前查询 
     CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql); 
     return this.query(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 {
     ....省略
     // simpleExecutor执⾏查询 
     list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
      ....省略 
 }

simpleExecutor类

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { 
    Statement stmt = null; 
     List var9; 
     try { 
         Configuration configuration = ms.getConfiguration(); 
         // 创建PreparedStatementHandler预编译处理类,包含parameterHandler和resultSetHandler
         StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 
         // 设置参数信息
         stmt = this.prepareStatement(handler, ms.getStatementLog());
         // 执⾏行行sql,并处理理结果集
         var9 = handler.query(stmt, resultHandler); 
     } finally { 
         this.closeStatement(stmt); 
     } 
     return var9; 
 } 

5.创建StatementHandler

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 
     // 创建PreparedStatementHandler预编译处理类,包含parameterHandler和resultSetHandler
     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 
     StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
     return statementHandler; 
 } 

6.parameterHandler设置参数信息

public void parameterize(Statement statement) throws SQLException { 
    this.parameterHandler.setParameters((PreparedStatement)statement); 
 } 

7. PreparedStatementHandler执行sql,并处理结果集

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 
     PreparedStatement ps = (PreparedStatement)statement;
     ps.execute();
     return this.resultSetHandler.handleResultSets(ps); 
 }

总结

加入我们

我们来自字节跳动飞书商业应用研发部(Lark Business Applications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们关注的产品领域主要在企业经验管理软件上,包括飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 领域系统,也包括飞书审批、OA、法务、财务、采购、差旅与报销等系统。欢迎各位加入我们。

扫码发现职位&投递简历

官网投递:job.toutiao.com/s/FyL7DRg