Mybatis源码分析

Mybatis源码分析## 标题
首先我们要从加载配置文件、解析配置文件、创建四大核心对象(Exxcutor、ParameterHandler、ResultSetHandler、StatementHandler)的详细过程,以及在这个过程中如何设置参数?如何执行查询?如何封装结果集?同时关注mybatis中所常用的几个设计模式:工厂模式、建造者模式等。

第一步:加载配置文件

//1、加载配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

往下跟代码,会进到一个ClassLoderWrapper类中,并找到配置文件最终返回一个BufferedInput Stream带有缓冲的输入流,它继承于FilterInputStream

第二步:创建SqlSessionFactory
这一步通过解析加载的配置文件,创建一个配置类configuration,而且这一步中也把映射文件一并解析,最终返回DefaultSqlSessionFactory对象,是一个工厂对象,工厂模式在mybatis中用的是相当多,以及建造者模式

//2、创建SqlSessionFactory对象,实际创建的是DefaultSqlSessionFactory对象
SqlSessionFactory builder = new SqlSessionFactoryBuilder().build(inputStream);

进入build方法

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
	try {
		//创建XMLConfigBuilder,用来解析xml配置文件,使用的是建造者模式
		XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
		//解析配置文件,构建SqlSessionFactory
		return build(parser.parse());
	} catch (Exception e) {
		throw ExceptionFactory.wrapException("Error buliding SqlSession", e);
	} finally {
		ErrorContext.instance().reset();
		try {
			inputStream,close();
		} catch (IOException e) {
			// Intentionally ignore. Prefer previous error.
      }
		}
	}
}

最重要的是parser.parse()这个方法,这里面是真正来解析核心配置文件的,进到这个方法中

public Configuration parse() {
	//判断配置文件是否被解析过,一个配置文件只能被解析一次
	if(parsed) {
		throw new BuilderException("Each XMLConfigBuilder can only be used once.");
	}
	parsed = true;
	//解析核心配置文件的configuration节点
	parseConfiguration(parser.evalNode("/configuration"));
	return configuration;
}

最终返回的是一个configuration对象,这个对象封装了所有配置相关的信息,这里可以看到

parseConfiguration(parser.evalNode("/configuration"));

这段代码已经开始解析配置文件的configuration节点,并且从上往下开始解析,我们进到这个方法中

private void parseConfiguration(XNode root) {
	try {
		//解析properties节点
		propertiesElement(root.evalNode("properties"));
		//解析settings并将其转换为Properties对象
		Properties settings = settingsAsProperties(root.evalNode("settings"));
		//加载vfs虚拟文件系统
		loadCustonVfs(settings);
		//解析settings
		loadCustomLogImpl(settings);
		//解析typeAliases
		typeAliasesElement(root.evalNode("typeAliases"));
		//解析plugins
		pluginElement(root.evalNode("plugins"));
		//解析objectFactory
		objectFactoryElement(root.evalNode("objectFactory"));
		//解析objectWrapperFactory
		objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
		//解析reflectorFactory
		reflectorFactoryElement(root.evalNode("reflectorFactory"));
		// settings 中的信息设置到 Configuration 对象中
      	settingsElement(settings);
      	// read it after objectFactory and objectWrapperFactory issue #631
        // 解析environments
     	environmentsElement(root.evalNode("environments"));
        // 解析 databaseIdProvider
      	databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      	typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析mappers
      	mapperElement(root.evalNode("mappers"));
	} catch (Exception e) {
		throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
	}
}

至此,我们就明白了mybatis-config.xml核心配置文件的解析过程,跟所有的xmlo配置文件的解析都是一样的,一层一层的解析里面的子节点或属性,重点是关于mappers节点的解析,这个是加载映射文件的关键地方

private void mapperElement(XNode parent) throws Exception {
	if (parent != null) {
		for (XNode child : parent.getChildren()) {
			//解析扫描包的配置方式
			if("package".equals(child.getName())) {
				String mapperPackage = child.getStringAttribute("name");
				//扫描包里面的映射文件,最终放到一个map中备用,type作为Key,创建的代理对象作为value
				configuration.addMappers(mapperPackage);
            	// 其他配置方方式resource、url、class
			} else {
				//解析resource的配置方式
				String resource = child.getStringAttribute("resource");
				String url = child.getStringAttribute("url");
				String mapperClass = child.getStringAttribute("class");
				if (resource != null && url == null && mapperClass == null) {
					ErrorContext.instance().resource(resource);
					InputStream inputStream = Resources.getResourceAsStream(resource);
					XMLMapperBuilder mapperParser = new XMLMapperBuilder (inputStream,  configuration, resource, 				       configuration.getSqlFragments());
					//解析映射文件
					mapperParser.parse();
				} else if (resource == null && url != null & mapperClass == null) {
					ErrorContext.instance().resource(url);
					InputStream inputStream = Resources.getUrlAsStream(url);
            		XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, 		configuration.getSqlFragments());
            		mapperParser.parse();
				} else if (resource == null && url == null && mapperClass != null) {
					Class<?> mapperInterface = Resources.classForName(mapperClass);
            		configuration.addMapper(mapperInterface);
         		 } else {
         		 	throw new BuilderException("A mapper element may only specify a url, resource or class, but not 	more than one.");
				}
			}
		}
	}
}

这里可以看出这个方法主要是根据不同的配置方式去解析映射文件,进行解析映射文件的方法mapperParser.parse()

public void parse() {
	if (!configuration.isResourceLoaded(resource)) {
		//解析mapper节点
		configurationElement(parser.evalNode("/mapper"));
		//添加到configuration中的集合中
		configuration.addLoadedResource(resource);
		//绑定命名空间
		bindMapperForNamespace();
	}
	parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

跟下去,看看怎么解析映射文件的内容

private void configurationElement(XNode context) {
	try {
		//拿到命名空间
		String namespace = context.getStringAttribute("namespace");
		if (namespace == null || namespace.equals("")) {
			throw new BuilderException("Mapper's namespace cannot be empty");
		}
		builderAssistant.setCurrentNamespace(namespace);
		//处理缓存的配置标签
		cacheRefElement(context.evalNode("cache-ref"));
		cacheElement(context.evalNode("cache"));
		// 解析parameterMap
     	parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // 解析resultMap
      	resultMapElements(context.evalNodes("/mapper/resultMap"));
       	// 解析 sql标签
      	sqlElement(context.evalNodes("/mapper/sql"));
        // 解析增删改查标签
      	buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
	} catch (Exception e) {
		throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
	}
}

这段代码的作用是解析映射文件的,和之前的解析核心配置文件的逻辑是一样的,重要的是解析增删改查标签的方法,跟着往下走

private void buildStatementfromContext(List<XNode> list, String requiredDatebaseId) {
	for (XNode context : list) {
		//创建XMLStatemntBuilder
		final XMLStatemntBuilder statementParser = new XMLStatemntBuilder(configuration, builderAssistant, context, requiredDatabaseId);
		try {
          // 解析sql标签
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
	}
}

进来之后

public void parseStatementNode() {
	String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
	
	//解析<select>标签中属性
	String nodeName = context.getNode().getNodeName();
	SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
	boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
	//是否刷新
	boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
	//是否使用缓存
	boolean useCache = context.getBooleanAttribute("useCache", isSelect);
	boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // 省略不必要的代码
    // ......
	// 解析好的属性设置到MapperBuilderAssistant中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

解析完标签中的具体属性,进到这个方法addMappedStatement中

public MappedStatement addMappedStatemen() {
	// 构建一个MappedStatement对象
	MappedStatement statement = statementBuilder.build();
	// 最终设置到configuration的map中,key是标签的id,value是MappedStatement对象
    configuration.addMappedStatement(statement);
	// 返回MappedStatement
    return statement;
}

添加到map中,这里的map是StrictMap,是继承了HashMap在Configuration中的内部类

public void addMappedStatement(MappedStatement ms) {
    // key是标签的id,value是MappedStatement对象
     mappedStatements.put(ms.getId(), ms);
  }
Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")

这里顺便说下,Mybatis中的很多用来存储参数映射、缓存都是用的StrictMap
StrictMap引用
到此,mybatis解析核心配置文件和映射文件的过程就走完了,最后回到,开始的地方SqlSessionFactoryBuilder中的build(parser.parse())方法返回一个DefaultSqlSessionFactory对象

 public SqlSessionFactory build(Configuration config) {
    // 实际创建的是DefaultSqlSessionFactory对象
    return new DefaultSqlSessionFactory(config);
 }

到这里第二步构建DefaultSqlSessionFactory的过程就走完了。

第三步:创建SqlSession
通过上一步的工厂对象创建DefaultSqlSession,这个过程还会创建一个最重要的Executor对象

 // 3. 创建SqlSession对象实际创建的是DefaultSqlSession对象
 SqlSession sqlSession = builder.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);
        // 创建Executor
      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();
    }
  }

这里的Executor对象是Mybatis的核心对象,也是真正用来执行增删改查的对象,进到方法里
SimpleExecutor:简单执行器,没啥特殊,默认用它
CachingExecutor:缓存执行器,执行的也会用到,Mybatis的一级缓存是默认开启的
ReuseExecutor: 批处理执行器

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

到此,这一步创建DefaultSqlSession和Executor的过程就完成了。

第四步:获取代理对象
获取代理对象之前,回忆一下上一步解析mapper节点的时候,解析完成之后,有一个bindMapperForNamespace()方法

 private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
          // 获取命名空间,使用发射机制实例化对象
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
          // 判断是否已经存在绑定的命名空间
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResoue
          configuration.addLoadedResource("namespace:" + namespace);
            // mapper加载到对象的配置中
          configuration.addMapper(boundType);
        }
      }
    }
  }

跟着这个加载的方法addMapper()方法进去,这个方法是在MapperRegistry类中

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
          // 存储到集合中,key是type ,vaule就是对应的工厂类
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

这里的knownMappers和前面提到的mappedStatements都是一样的逻辑,放到map中,到这里明白了在解析命名空间的时候,就已经把mapper接口放到了map中,下面创建代理对象的内容就更好理解了
跟着第4步往下走,获取代理对象,断点往下走进入到MapperRegistry类中

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从上一步存入的map中获取代理工厂
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        // mapper代理工厂类创建mapper代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

进到这个newInstance看都了new了一个MapperProxy代理对象,调用自己MapperProxyFactory类自己的newInstance方法

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
   // 创建代理对象
     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

这里就通过JDK的动态代理方式创建了mapper代理对象,这里的mapper代理对象的创建就完成了。

第五步:执行查询
经过前面三部曲的准备i,核心配置文件准备好了,映射文件准备好了,代理对象也有了;接下来,最后一步关键的步骤执行查询,这里也是比较复杂的地方,参数的封装,sql的预编译,结果集的封装等都是在这里处理的,接着进入到代理对象的查询方法,代理对象首先会执行invoke方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	try {
		//如果方法是定义在Object类中,则直接调用
		if (Object,class.equals(methods.getDeclaringClass())) {
			return method.invoke(this, args);
		} else if (method.isDefault()) { //这对jdk8、9新特性做的处理
			if (privateLookupInMethod == null) {
				return invokeDefaultMethodJava8(proxy, methdo, args);
			} else {
				return invokeDefaultMethodJava9(proxy, method, args);
			}
		}
	} catch (Throwable t)) {
		throw ExceptionUtil.unwrapThrowable(t);
	}
	 // 从缓存中获取 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
     // 调用 execute 方法执行 SQL
    return mapperMethod.execute(sqlSession, args);
}

执行execute方法之前会先判断下sql是哪种类型

public Object execute (SqlSession sqlSession, Object[] args) {
	Object result;
	//判断sql类型
	switch (command.getType()) {
		case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
            // 查询操作里还会判断方法返回类型
      case SELECT:
      	//无返回值
      	if (method.returnsVoid() && method.hasResultHandler()) {
			executeWithResultHandler(sqlSession, args);
			result = null;
		} else if (method.returnsMany) {
			//这里查询的是返回多个结果
			result = executeForMany(sqlSession, args);
		} else if (method.returnsMap()) {
			//返回map
			result = executeForMap(sqlSession, args);
		} else {
			Object param = method.convertArgsToSqlCommandParam(args);
			result = sqlSession.selectOne(command.getName(), param);
			if (method.returnsOptional() && (result == null || !method,getReturnType().equals(result.getClass()))) {
				result = Optional.ofNullable(result);
			} 
		}
		break;
	case FLUSH:
		result = sqlSession.flushStatements();
		break;
	default:
		throw new BindingException("Unknown execution method for: " + command.getName());
	}
	if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
		throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
	}
	return result;
}

接着进入到executeForMany方法中

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    // 参数处理
    Object param = method.convertArgsToSqlCommandParam(args);
  // 是否逻辑分页
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
    // 执行查询
        result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
        // 执行查询
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

这里的执行方法实际就是调用的前面所创建的DefaultSqlSession的方法继续往下走看到

 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 从配置类中获取MappedStatement对象
      MappedStatement ms = configuration.getMappedStatement(statement);
        // 执行查询
      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();
    }
  }

这里获取MappedStatement对象,就是前面存储到map中的,这里突然明白之前很对对象都存到map中,是备用

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 从MappedStatement获取BoundSql对象拿到获取绑定的sql,并将参数对象与sql语句的#{}对应
      BoundSql boundSql = ms.getBoundSql(parameterObject);
      // 创建缓存的key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
      // 执行查询
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

sql语句是在解析标签的时候,封装成对应的MappedStatement对象,其中标签里的sql就是封装成了sqlSource对象,这里的BoundSql实际是从绑定动态sql,继续执行查询,这里的查询其实用到的是CacheExecutor

 @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
  // 获取缓存,这是先使用的二级缓存
      Cache cache = ms.getCache();
// 判断缓存是否为空    
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.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
      // 执行查询的委托给了SimplExecutor对象
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

继续跟着源码进去,发现是进到了BaseExecutor里面

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++;
        // 去本地缓存也就是一级缓存去拿
      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);
      }
    }
    // 省略不必要代码
    return list;
  }

接下来就是去数据库查询了

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()方法中

@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对象,这里是PreparedStatementHandler对象,进行预处理
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
     	// 预处理和jdbc的prepareStatement就是一样的
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行查询
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

在创建PreparedStatementHandler对象的过程中,还涉及到了插件、参数处理器(ParameterHandler)、结果处理器(ResultSetHandler),大家可以跟着断点进去看下,在预处理的阶段对参数进行了设置

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取连接
    Connection connection = getConnection(statementLog);
    // 解析参数
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 设置参数  
     handler.parameterize(stmt);
    return stmt;
  }

在参数设置的时候使用的typeHandler进行设置

@Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 		// 省略不必要代码...
          try {
              // 根据参数索引位置进行设置参数
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {

          }
        }
      }
    }
  }

接下来我们继续看查询的步骤

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
   // 执行查询,这里就是jdbc的来具体的执行了
      ps.execute();
	// 处理结果
    return resultSetHandler.handleResultSets(ps);
  }

第六部:封装结果集
最后咱么再来看下一下处理结果集的过程,Mybatis可以自动将查询的结果映射成实体类对象,这也是mybatis最大的一个特点

public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

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

    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++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

我们进到handleResultSet()的方法

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
        //  处理嵌套查询结果集
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        // 处理简单查询结果
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

进到handleRowValuesForNestedResultMap()方法

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
   // 处理逻辑分页用到这个方法
      skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
        // 获取结果
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      // 存储结果
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

进到getRowValue()方法中

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 创建实体类对象
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
     // 进行自动映射
     foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 根据 <resultMap> 节点中配置的映射关系进行映射
     foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

跟着进入到应用自动映射applyAutomaticMappings()的方法 继续跟进createAutomaticMappings()创建自动映射的方法

private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    final String mapKey = resultMap.getId() + ":" + columnPrefix;
    List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
    if (autoMapping == null) {
      autoMapping = new ArrayList<>();
        // 加载未映射的属性
      final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
      for (String columnName : unmappedColumnNames) {
        String propertyName = columnName;
        if (columnPrefix != null && !columnPrefix.isEmpty()) {
          // When columnPrefix is specified,
          // ignore columns without the prefix.
          if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
            propertyName = columnName.substring(columnPrefix.length());
          } else {
            continue;
          }
        }
          // 获取属性
        final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
        if (property != null && metaObject.hasSetter(property)) {
          if (resultMap.getMappedProperties().contains(property)) {
            continue;
          }
          final Class<?> propertyType = metaObject.getSetterType(property);
          if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
            final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
             // 封装上面获取到的信息到 UnMappedColumnAutoMapping 对象中
            autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
          } else {
            configuration.getAutoMappingUnknownColumnBehavior()
                .doAction(mappedStatement, columnName, property, propertyType);
          }
        } else {
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
        }
      }
        // 放入缓存
      autoMappingsCache.put(mapKey, autoMapping);
    }
    return autoMapping;
  }

最后回到getRowValue()方法,往下走进入到applyPropertyMappings()方法

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    // 获取已映射的列名
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    // 获取 ResultMapping
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.getNestedResultMapId() != null) {
        // the user added a column attribute to a nested result map, ignore it
        column = null;
      }
      if (propertyMapping.isCompositeResult()
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
          || propertyMapping.getResultSet() != null) {
          // 从结果集中获取指定列的数据
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue #541 make property optional
        final String property = propertyMapping.getProperty();
        if (property == null) {
          continue;
        } else if (value == DEFERRED) {
          foundValues = true;
          continue;
        }
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
          // gcode issue #377, call setter on nulls (value is not 'found')
            // 将获取到的值设置到实体类对象中
          metaObject.setValue(property, value);
        }
      }
    }
    // 返回结果
    return foundValues;
  }

至此,就把结果集的封装搞定

小结
这篇文章主要是撸一下MyBatis在执行的查询时底层的代码逻辑,也能看到一些常用的设计模式应用的比较广泛,明白了这些经典框架的写法,以后自己在写代码的时候就可以从模仿开始,开启自己的优秀程序员之路

这里主要根据最原始的Mybatis的执行步奏分析其流程,主要有

加载配置文件,包括核心配置文件和映射文件
创建工厂对象(创建一次,声明周期是应用级别)
创建SqlSession对象
使用JDK的动态代理方式获取代理对象
最后执行查询和封装结果集(这里包含参数的处理,SQL的预编译,结果集的映射)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值