工具除了会用,还应该多做点。我觉得使用一个软件工具(开源类),一般会经历几个步骤:

1. 通过wiki了解大致作用,然后开始码代码;

2. 系统性地学习其特性,找出可能需要的点,用上去;

3. 通过阅读其源码,清楚其来龙去脉;

4. 有能力你就去超越别人;

mybatis作为orm框架给我们带来了很多方便,其定制功能也让我们惊喜!还是来看看别人怎么做到的吧!

  1. 下载git仓库, https://github.com/mybatis/mybatis-3

  2. 打开IDE, 找到 test 包

  3. 进入 org.apache.ibatis.autoconstructor.AutoConstructorTest, 有一个完整的sql 样例

public class AutoConstructorTest {private static SqlSessionFactory sqlSessionFactory;@BeforeClasspublic static void setUp() throws Exception {// create a SqlSessionFactorytry (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);}// populate in-memory database
    BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),"org/apache/ibatis/autoconstructor/CreateDB.sql");}@Testpublic void fullyPopulatedSubject() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);final Object subject = mapper.getSubject(1);assertNotNull(subject);}}@Test(expected = PersistenceException.class)public void primitiveSubjects() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);mapper.getSubjects();}}@Testpublic void annotatedSubject() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);verifySubjects(mapper.getAnnotatedSubjects());}}@Test(expected = PersistenceException.class)public void badSubject() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);mapper.getBadSubjects();}}@Testpublic void extensiveSubject() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);verifySubjects(mapper.getExtensiveSubject());}}private void verifySubjects(final List<?> subjects) {assertNotNull(subjects);Assertions.assertThat(subjects.size()).isEqualTo(3);}
}

目标方法:fullyPopulatedSubject(),最终进行一条 select 查询:

SELECT * FROM subject WHERE id = #{id}

首先执行的肯定 setup()方法了,setup()做了三件事:

1. 加载mybatis-config文件读取到reader中;

2. 将配置文件传递工厂builder中创建会话工厂;

3. 执行hsqldb内存数据库的初始化工作;(非本文重点)

接下来,咱们从源码的角度,主要看会话工厂的创建过程!

创建 sqlSessionFactory

   try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);}// SqlSessionFactoryBuilder.build(reader) -> XMLConfigBuilder.parse()
// org.apache.ibatis.session.SqlSessionFactoryBuilderpublic SqlSessionFactory build(Reader reader) {return build(reader, null, null);}// 建立factorypublic SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {// 加载完成后,上下文管理器重置
      ErrorContext.instance().reset();try {reader.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.
      }}}

标签解析:

/*** Offline entity resolver for the MyBatis DTDs* * @author Clinton Begin* @author Eduardo Macarron*/
public class XMLMapperEntityResolver implements EntityResolver {private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";/*** Converts a public DTD into a local one* * @param publicId The public id that is what comes after "PUBLIC"* @param systemId The system id that is what comes after the public id.* @return The InputSource for the DTD* * @throws org.xml.sax.SAXException If anything goes wrong*/@Overridepublic InputSource resolveEntity(String publicId, String systemId) throws SAXException {try {if (systemId != null) {String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);} else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);}}return null;} catch (Exception e) {throw new SAXException(e.toString());}}private InputSource getInputSource(String path, String publicId, String systemId) {InputSource source = null;if (path != null) {try {InputStream in = Resources.getResourceAsStream(path);source = new InputSource(in);source.setPublicId(publicId);source.setSystemId(systemId);        } catch (IOException e) {// ignore, null is ok
      }}return source;}}

parser

// org.apache.ibatis.parsing.XPathParser.XPathParser(), 设备一些必要参数public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {commonConstructor(validation, variables, entityResolver);this.document = createDocument(new InputSource(reader));}private Document createDocument(InputSource inputSource) {// important: this must only be called AFTER common constructortry {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setValidating(validation);factory.setNamespaceAware(false);factory.setIgnoringComments(true);factory.setIgnoringElementContentWhitespace(false);factory.setCoalescing(false);factory.setExpandEntityReferences(true);DocumentBuilder builder = factory.newDocumentBuilder();builder.setEntityResolver(entityResolver);builder.setErrorHandler(new ErrorHandler() {@Overridepublic void error(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic void fatalError(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic void warning(SAXParseException exception) throws SAXException {}});return builder.parse(inputSource);} catch (Exception e) {throw new BuilderException("Error creating document instance.  Cause: " + e, e);}}

// 在返回fatory的时候调用 return build(parser.parse()); 将 config.xml 的配置全部解析到 configuration 实例中

// 在返回fatory的时候调用 return build(parser.parse());  将 config.xml 的配置全部解析到 configuration 实例中
// org.apache.ibatis.builder.xml.XMLConfigBuilder.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;}// org.apache.ibatis.parsing.XPathParser.evalNode() 映射 xml 属性到 bean public XNode evalNode(Object root, String expression) {Node node = (Node) evaluate(expression, root, XPathConstants.NODE);if (node == null) {return null;}return new XNode(this, node, variables);}private Object evaluate(String expression, Object root, QName returnType) {try {return xpath.evaluate(expression, root, returnType);} catch (Exception e) {throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);}}

具体解析项如下,从这里也一目了然,配置项支持什么:

  // 解析各配置参数到实例中private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));// 这个配置没有用过吧
      loadCustomVfs(settings);// 解析别名设置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 #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));// 重要节点,解析 mappermapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}

下面依次来看一下都怎么解析各配置项的吧~

properties 解析

  // propertiesElement(root.evalNode("properties"));  url, resource 解析private void propertiesElement(XNode context) throws Exception {if (context != null) {Properties defaults = context.getChildrenAsProperties();String resource = context.getStringAttribute("resource");String url = context.getStringAttribute("url");if (resource != null && url != null) {throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");}if (resource != null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {defaults.putAll(Resources.getUrlAsProperties(url));}Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}// 解析完成后,放到 parser 和 configuration 实例中
      parser.setVariables(defaults);configuration.setVariables(defaults);}}

settings 配置项解析,返回内容待处理

  // Properties settings = settingsAsProperties(root.evalNode("settings"));private Properties settingsAsProperties(XNode context) {if (context == null) {return new Properties();}Properties props = context.getChildrenAsProperties();// Check that all settings are known to the configuration classMetaClass 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;}// loadCustomVfs(settings);  这个...private void loadCustomVfs(Properties props) throws ClassNotFoundException {String value = props.getProperty("vfsImpl");if (value != null) {String[] clazzes = value.split(",");for (String clazz : clazzes) {if (!clazz.isEmpty()) {@SuppressWarnings("unchecked")Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);configuration.setVfsImpl(vfsImpl);}}}}

typeAliases 别名设置解析,主要做类型检查,及别名的注册工作

  // typeAliasesElement(root.evalNode("typeAliases"));private void typeAliasesElement(XNode parent) {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {// 针对package配置,需注册一系列别名,以 simpleName 代替String typeAliasPackage = child.getStringAttribute("name");configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);} else {String alias = child.getStringAttribute("alias");String type = child.getStringAttribute("type");try {Class<?> clazz = Resources.classForName(type);if (alias == null) {typeAliasRegistry.registerAlias(clazz);} else {typeAliasRegistry.registerAlias(alias, clazz);}} catch (ClassNotFoundException e) {throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);}}}}}// org.apache.ibatis.type.TypeAliasRegistry.registerAlias(), 具体数据结构为 map 封装public void registerAlias(Class<?> type) {String alias = type.getSimpleName();Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) {alias = aliasAnnotation.value();} registerAlias(alias, type);}public void registerAlias(String alias, Class<?> value) {if (alias == null) {throw new TypeException("The parameter alias cannot be null");}// issue #748String key = alias.toLowerCase(Locale.ENGLISH);if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");}TYPE_ALIASES.put(key, value);}

plugins 配置项解析,将属性注入的plugin, 添加 plugin 到 configuration

  // pluginElement(root.evalNode("plugins"));private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();// 先创建一个plugin实例,备用Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();// 配置属性的注入,回调
        interceptorInstance.setProperties(properties);// 添加到 configuration
        configuration.addInterceptor(interceptorInstance);}}}
public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

objectFactory 配置项解析,与plugin原理相似,做替换自定义作用

  // objectFactoryElement(root.evalNode("objectFactory"));private void objectFactoryElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");Properties properties = context.getChildrenAsProperties();ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();factory.setProperties(properties);configuration.setObjectFactory(factory);}}// objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 没什么特别private void objectWrapperFactoryElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();configuration.setObjectWrapperFactory(factory);}}

  // reflectorFactoryElement(root.evalNode("reflectorFactory"));private void reflectorFactoryElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();configuration.setReflectorFactory(factory);}}

settings 选项解析,设置各种开关, 如缓存

  // settingsElement(settings);  设置各种开关, 如缓存private void settingsElement(Properties props) throws Exception {configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));@SuppressWarnings("unchecked")Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));configuration.setDefaultEnumTypeHandler(typeHandler);configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));configuration.setLogPrefix(props.getProperty("logPrefix"));@SuppressWarnings("unchecked")Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));configuration.setLogImpl(logImpl);configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));}

environments 环境配置设置,根据指定环境,获取相应配置,加载数据源配置

  // environmentsElement(root.evalNode("environments")); 环境配置private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {// 默认为 developmentenvironment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {String id = child.getStringAttribute("id");// 查找当前环境配置,指定加载if (isSpecifiedEnvironment(id)) {// 事务管理、数据源TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));DataSource dataSource = dsFactory.getDataSource();Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}// 环境配置包含, id, 事务管理, 数据源public Environment build() {return new Environment(this.id, this.transactionFactory, this.dataSource);}

databaseIdProvider 解析,对多数据源,包含多 机器,或多类型数据库 mysql,sqlserver

  // databaseIdProviderElement(root.evalNode("databaseIdProvider")); private void databaseIdProviderElement(XNode context) throws Exception {DatabaseIdProvider databaseIdProvider = null;if (context != null) {String type = context.getStringAttribute("type");// awful patch to keep backward compatibilityif ("VENDOR".equals(type)) {type = "DB_VENDOR";}Properties properties = context.getChildrenAsProperties();databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();databaseIdProvider.setProperties(properties);}Environment environment = configuration.getEnvironment();if (environment != null && databaseIdProvider != null) {String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());configuration.setDatabaseId(databaseId);}}

自定义 typeHandler

  // typeHandlerElement(root.evalNode("typeHandlers")); 自定义 typeHandlerprivate void typeHandlerElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String typeHandlerPackage = child.getStringAttribute("name");typeHandlerRegistry.register(typeHandlerPackage);} else {String javaTypeName = child.getStringAttribute("javaType");String jdbcTypeName = child.getStringAttribute("jdbcType");String handlerTypeName = child.getStringAttribute("handler");Class<?> javaTypeClass = resolveClass(javaTypeName);JdbcType jdbcType = resolveJdbcType(jdbcTypeName);Class<?> typeHandlerClass = resolveClass(handlerTypeName);if (javaTypeClass != null) {if (jdbcType == null) {typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);} else {typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);}} else {typeHandlerRegistry.register(typeHandlerClass);}}}}}// org.apache.ibatis.type.TypeHandlerRegistry.register(), 以map保存映射private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {if (javaType != null) {Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);if (map == null || map == NULL_TYPE_HANDLER_MAP) {map = new HashMap<>();TYPE_HANDLER_MAP.put(javaType, map);}map.put(jdbcType, handler);}ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);}

mapper 解析,crud

  // mapperElement(root.evalNode("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");configuration.addMappers(mapperPackage);} else {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.");}}}}}

mapper解析细节

  // org.apache.ibatis.builder.xml.XMLMapperBuilder.parse() mapper解析细节public void parse() {// 只解析一次 namespaceif (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}// 解析 resultMaps
    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"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));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 parsePendingResultMaps() {Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();synchronized (incompleteResultMaps) {Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();while (iter.hasNext()) {try {iter.next().resolve();iter.remove();} catch (IncompleteElementException e) {// ResultMap is still missing a resource...
        }}}}private void parsePendingCacheRefs() {Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();synchronized (incompleteCacheRefs) {Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();while (iter.hasNext()) {try {iter.next().resolveCacheRef();iter.remove();} catch (IncompleteElementException e) {// Cache ref is still missing a resource...
        }}}}private void parsePendingStatements() {Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();synchronized (incompleteStatements) {Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();while (iter.hasNext()) {try {iter.next().parseStatementNode();iter.remove();} catch (IncompleteElementException e) {// Statement is still missing a resource...
        }}}}

View Code

// 最后,返回一个 DefaultSqlSessionFactory, 载入配置项

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

// 会话创建好后,可以执行sql了

  public void fullyPopulatedSubject() {// 打开一个连接,使用完成后关闭try (SqlSession sqlSession = sqlSessionFactory.openSession()) {final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);final Object subject = mapper.getSubject(1);assertNotNull(subject);}}

2. 打开获得一个数据库会话连接

// org.apache.ibatis.session.defaults.SqlSession.openSession(); 打开一个数据库连接
@Overridepublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}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);// new Executorfinal 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();}}

  // org.apache.ibatis.session.Configuration.newExecutor()public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;// 根据不同请求类型创建不同的 executorif (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {// 默认为 SIMPLEexecutor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {// 创建缓存管理,将缓存请求委托给 executor 处理executor = new CachingExecutor(executor);}// 处理plugin 请求executor = (Executor) interceptorChain.pluginAll(executor);return executor;}// org.apache.ibatis.executor.CachingExecutorpublic CachingExecutor(Executor delegate) {this.delegate = delegate;delegate.setExecutorWrapper(this);}// 最后将加载好的各种配置和实际处理,由 DefaultSqlSession 包装好后返回public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {this.configuration = configuration;this.executor = executor;this.dirty = false;this.autoCommit = autoCommit;}

3. 获取mapper,准备调用查询

  // org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper()
  @Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}// org.apache.ibatis.binding.MapperRegistry.getMapper()@SuppressWarnings("unchecked")public <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 获取 MapperProxyFactoryfinal MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {// 创建 mapper 实例 return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}// org.apache.ibatis.binding.newInstance(); public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}

4. select, 查询数据

  //mapper.getSubject(1); 查询数据
  @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 加载缓存包装方法final MapperMethod mapperMethod = cachedMapperMethod(method);// 执行查询return mapperMethod.execute(sqlSession, args);}private MapperMethod cachedMapperMethod(Method method) {return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));}// org.apache.ibatis.binding.MapperMethod.execute(sqlSession, args); 针对不同类型语句,做不同调用public Object execute(SqlSession sqlSession, Object[] args) {Object result;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()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(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;}

  // org.apache.ibatis.reflection.ParamNameResolver.getNamedParams() 返回参数,如果是单个参数,直接返回参数值//  如果是多个参数,则以 map 形式返回public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {return args[names.firstKey()];} else {final Map<String, Object> param = new ParamMap<>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);// ensure not to overwrite parameter named with @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}}

View Code

selectOne

  // org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(command.getName(), param);
  @Overridepublic <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.// 查询单条记录时,默认也是先查询list,然后取第一条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;}}// 查询列表
  @Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {// 先获取执行语句,此处 statement 为 idMappedStatement 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();}}// org.apache.ibatis.session.Configuration.getMappedStatement(id), 获取初始化解析出来的语句public MappedStatement getMappedStatement(String id) {return this.getMappedStatement(id, true);}public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {if (validateIncompleteStatements) {buildAllStatements();}return mappedStatements.get(id);}

包装执行 query

  // executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);// org.apache.ibatis.executor.CachingExecutor.query()
  @Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 获取sqlBoundSql boundSql = ms.getBoundSql(parameterObject);CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}// org.apache.ibatis.mapping.MappedStatement.getBoundSql()public BoundSql getBoundSql(Object parameterObject) {BoundSql boundSql = sqlSource.getBoundSql(parameterObject);// 字段映射List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings == null || parameterMappings.isEmpty()) {boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);}// check for nested result maps in parameter mappings (issue #30)for (ParameterMapping pm : boundSql.getParameterMappings()) {String rmId = pm.getResultMapId();if (rmId != null) {ResultMap rm = configuration.getResultMap(rmId);if (rm != null) {hasNestedResultMaps |= rm.hasNestedResultMaps();}}}return boundSql;}// CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);// return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// org.apache.ibatis.executor.CachingExecutor.query()
  @Overridepublic <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.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116
        }return list;}}return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

前面的query是一个cache的包装类,其实际查询动作需要委托给excuttor,而我们主要关心 queryFromDb 逻辑!

  // org.apache.ibatis.executor.SimpleExecutor.query() / BaseExecutor@SuppressWarnings("unchecked")@Overridepublic <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 {// 没有缓存,走dblist = 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;}// BaseExecutorprivate <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,主要是获取完整的 statement,使handler有据可查!

  @Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {// 获取全局配置Configuration configuration = ms.getConfiguration();// 获取statement, 处理 JDBC 接口StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}// org.apache.ibatis.session.Configuration.newStatementHandler(), 兼顾 plugins, public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}

db查询

  // 为不同处理类型,建立不同的 handlerpublic RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}//stmt = prepareStatement(handler, ms.getStatementLog());// org.apache.ibatis.executor.SimpleExecutor.prepareStatement(), 获取 statement, JDBCprivate Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);// sql录入stmt = handler.prepare(connection, transaction.getTimeout());// 参数绑定
    handler.parameterize(stmt);return stmt;}// BaseExecutorprotected Connection getConnection(Log statementLog) throws SQLException {Connection connection = transaction.getConnection();if (statementLog.isDebugEnabled()) {return ConnectionLogger.newInstance(connection, statementLog, queryStack);} else {return connection;}}// org.apache.ibatis.transaction.jdbc.JdbcTransaction.getConnection()
  @Overridepublic Connection getConnection() throws SQLException {if (connection == null) {// 为空时新建 connection
      openConnection();}return connection;}protected void openConnection() throws SQLException {if (log.isDebugEnabled()) {log.debug("Opening JDBC Connection");}connection = dataSource.getConnection();if (level != null) {connection.setTransactionIsolation(level.getLevel());}setDesiredAutoCommit(autoCommit);}// org.apache.ibatis.datasource.unpooled.getConnection()
  @Overridepublic Connection getConnection() throws SQLException {return doGetConnection(username, password);}private Connection doGetConnection(String username, String password) throws SQLException {Properties props = new Properties();if (driverProperties != null) {props.putAll(driverProperties);}if (username != null) {props.setProperty("user", username);}if (password != null) {props.setProperty("password", password);}return doGetConnection(props);}private Connection doGetConnection(Properties properties) throws SQLException {// 如果没有初始化驱动,初始化
    initializeDriver();// Connection connection = DriverManager.getConnection(url, properties);configureConnection(connection);return connection;}private synchronized void initializeDriver() throws SQLException {if (!registeredDrivers.containsKey(driver)) {Class<?> driverType;try {// 加载驱动if (driverClassLoader != null) {driverType = Class.forName(driver, true, driverClassLoader);} else {driverType = Resources.classForName(driver);}// DriverManager requires the driver to be loaded via the system ClassLoader.// http://www.kfu.com/~nsayer/Java/dyn-jdbc.htmlDriver driverInstance = (Driver)driverType.newInstance();DriverManager.registerDriver(new DriverProxy(driverInstance));registeredDrivers.put(driver, driverInstance);} catch (Exception e) {throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);}}}private void configureConnection(Connection conn) throws SQLException {if (autoCommit != null && autoCommit != conn.getAutoCommit()) {conn.setAutoCommit(autoCommit);}if (defaultTransactionIsolationLevel != null) {conn.setTransactionIsolation(defaultTransactionIsolationLevel);}}// stmt = handler.prepare(connection, transaction.getTimeout()); 组装sql
// org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare()
  @Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {return delegate.prepare(connection, transactionTimeout);}// org.apache.ibatis.executor.statement.PreparedStatementHandler.
  @Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// select * from tab where id = ?statement = instantiateStatement(connection);// 设置超时
      setStatementTimeout(statement, transactionTimeout);// 设置查询大小,默认不限制
      setFetchSize(statement);return statement;} catch (SQLException e) {closeStatement(statement);throw e;} catch (Exception e) {closeStatement(statement);throw new ExecutorException("Error preparing statement.  Cause: " + e, e);}}// 调用jdbc connection.prepareStatement(sql), 初始化语句
  @Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);} else {return connection.prepareStatement(sql, keyColumnNames);}} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {return connection.prepareStatement(sql);} else {return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);}}// PreparedStatementHandler handler.parameterize(stmt);
  @Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}// org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters()
  @Overridepublic void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional paramsvalue = 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);}// TypeHandler 处理参数类型TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);} catch (SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}// return handler.<E>query(stmt, resultHandler);// org.apache.ibatis.executor.statement.RoutingStatementHandler.query()
  @Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {return delegate.<E>query(statement, resultHandler);}// PreparedStatementHandler.query()
  @Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;// 调用驱动程序执行 execute
    ps.execute();// 处理结果集映射return resultSetHandler.<E> handleResultSets(ps);}// org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(), 处理结果集映射
  @Overridepublic 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);}// org.apache.ibatis.executor.resultset.ResultSetWrapper, 根据第一行返回数据集,初始化字段名及字段类型public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {super();this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.resultSet = rs;final ResultSetMetaData metaData = rs.getMetaData();final int columnCount = metaData.getColumnCount();for (int i = 1; i <= columnCount; i++) {columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));// java.sql.Types, 处理返回的类型为具体的java类型映射到 org.apache.ibatis.type.JdbcType
      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));// java 类型全名
      classNames.add(metaData.getColumnClassName(i));}}

View Code

数据库结果映射到javabean

  // DefaultResultSetHandler.handleResultSet() 处理单条记录映射关系private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {if (parentMapping != null) {handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {if (resultHandler == null) {DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);multipleResults.add(defaultResultHandler.getResultList());} else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());}}// handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);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);}}// 简单数据映射private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<>();ResultSet resultSet = rsw.getResultSet();// 跳过行偏移,难道不是在数据sql中添加 limit 进行数据筛选的?
    skipRows(resultSet, rowBounds);while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {// 1. Discriminated 鉴别器ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);// Object rowValue = getRowValue(rsw, discriminatedResultMap, null);storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}}// 检测超过 limit 后,不再继续获取结果private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();}// Discriminatedpublic ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {Set<String> pastDiscriminators = new HashSet<>();Discriminator discriminator = resultMap.getDiscriminator();while (discriminator != null) {final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));if (configuration.hasResultMap(discriminatedMapId)) {resultMap = configuration.getResultMap(discriminatedMapId);Discriminator lastDiscriminator = discriminator;discriminator = resultMap.getDiscriminator();if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {break;}} else {break;}}return resultMap;}// 从结果set中获取值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;}foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;foundValues = lazyLoader.size() > 0 || foundValues;rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}return rowValue;}// DefaultResultSetHandlerprivate Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)throws SQLException {final Class<?> resultType = resultMap.getType();final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();if (hasTypeHandlerForResultObject(rsw, resultType)) {return createPrimitiveResultObject(rsw, resultMap, columnPrefix);} else if (!constructorMappings.isEmpty()) {return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {return objectFactory.create(resultType);} else if (shouldApplyAutomaticMappings(resultMap, false)) {return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);}throw new ExecutorException("Do not know how to create an instance of " + resultType);}// 构造器注入方式private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,String columnPrefix) throws SQLException {final Constructor<?>[] constructors = resultType.getDeclaredConstructors();final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);if (defaultConstructor != null) {return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, defaultConstructor);} else {for (Constructor<?> constructor : constructors) {if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, constructor);}}}throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());}private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) throws SQLException {boolean foundValues = false;for (int i = 0; i < constructor.getParameterTypes().length; i++) {Class<?> parameterType = constructor.getParameterTypes()[i];String columnName = rsw.getColumnNames().get(i);TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);Object value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(columnName, columnPrefix));constructorArgTypes.add(parameterType);constructorArgs.add(value);foundValues = value != null || foundValues;}// 调用构造方法注入值return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;}

  // org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(), 调用构造器返回 bean 实例@SuppressWarnings("unchecked")@Overridepublic <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {Class<?> classToCreate = resolveInterface(type);// we know types are assignablereturn (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);}private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {try {Constructor<T> constructor;if (constructorArgTypes == null || constructorArgs == null) {constructor = type.getDeclaredConstructor();try {return constructor.newInstance();} catch (IllegalAccessException e) {if (Reflector.canControlMemberAccessible()) {constructor.setAccessible(true);return constructor.newInstance();} else {throw e;}}}constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));try {return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));} catch (IllegalAccessException e) {if (Reflector.canControlMemberAccessible()) {constructor.setAccessible(true);return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));} else {throw e;}}} catch (Exception e) {StringBuilder argTypes = new StringBuilder();if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {for (Class<?> argType : constructorArgTypes) {argTypes.append(argType.getSimpleName());argTypes.append(",");}argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }StringBuilder argValues = new StringBuilder();if (constructorArgs != null && !constructorArgs.isEmpty()) {for (Object argValue : constructorArgs) {argValues.append(String.valueOf(argValue));argValues.append(",");}argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);}}

View Code

数据集获取

  // 保存结果private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {if (parentMapping != null) {linkToParents(rs, parentMapping, rowValue);} else {callResultHandler(resultHandler, resultContext, rowValue);}}@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {resultContext.nextResultObject(rowValue);((ResultHandler<Object>) resultHandler).handleResult(resultContext);}// 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;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 itcolumn = 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 optionalfinal String property = propertyMapping.getProperty();if (property == null) {continue;} else if (value == DEFERED) {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;}// collapseSingleResultList(multipleResults);  转换多级 list 为1级list@SuppressWarnings("unchecked")private List<Object> collapseSingleResultList(List<Object> multipleResults) {return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;}

至此,一条select 语句搞定!

主要经历几个阶段:
  1. 加载配置
  2. 创建sqlSessionFactory
  3. 获取sqlSession
  4. jdbc连接
  5. 获取mapper
  6. 查询参数绑定
  7. 获取db结果
  8. 处理返回集字段映射
  9. 缓存结果
  10. 返回结果

转载于:https://www.cnblogs.com/yougewe/p/9926638.html

简单读!Mybatis源码(一)一条select的一生相关推荐

  1. 读Mybatis源码

    一.架构图 1. mybatis配置 SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息. mapper.xml文件即sql映射文件,文件中 ...

  2. MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)

    文章目录 Pre Executor 执行器 接口继承关系 SimpleExecutor(简单执行器) 入门小demo 实例化SimpleExecutor doQuery方法 Pre MyBatis源码 ...

  3. MyBatis源码简单分析

    准备工作 Mybatis官网地址:https://blog.mybatis.org/ MyBatis官方文档地址:https://mybatis.org/mybatis-3/ MyBatis源码下载地 ...

  4. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  5. Mybatis源码阅读(二):动态节点解析2.1 —— SqlSource和SqlNode

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如 ...

  6. mybatis源码分析执行流程

    前言 在上一篇,我们了解了mybatis的整体执行流程,以及内部的各个组件在执行过程中的功能,下面我们来通过源码的方式对其中比较重要的几个组件在执行过程的使用进行简单的分析与说明 环境准备 基于第一篇 ...

  7. 【Mybatis源码】源码分析

    [Mybatis源码]源码分析 (一)Mybatis重要组件 [1]四大核心组件 (1)SqlSessionFactoryBuilder (2)SqlSessionFactory (3)SqlSess ...

  8. mybatis 源码系列(七) Java基础之数据库事务隔离级别

    更多mybatis 源码系列文章可关注我的博客,点击前往 正确设置数据库的事务访问级别,有助于我们的应用程序达到预期的效果 在mybatis中,提供了事务隔离级别的枚举类:org.apache.iba ...

  9. 我的架构梦:(三)MyBatis源码分析

    mybatis的源码分析 一.传统方式源码分析 二.Mapper代理方式源码分析 三.MyBatis源码中涉及到的设计模式 一.传统方式源码分析 分析之前我们来回顾下传统方式的写法: /*** 传统方 ...

  10. mybatis源码分析之事务管理器

    2019独角兽企业重金招聘Python工程师标准>>> 上一篇:mybatis源码分析之Configuration 主要分析了构建SqlSessionFactory的过程中配置文件的 ...

最新文章

  1. Nature:麻省理工人造「巨型原子」问世,量子处理和量子通信合二为一
  2. java实现排程算法_康托展开算法和逆康托展开算法[Java实现]
  3. Qt 控件渐变隐藏消失
  4. leetcode-3-无重复字符的最长子串
  5. 【Android】Theme.AppCompat.Light 问题
  6. 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
  7. Jsoup代码解读之四-parser(上)
  8. 计算机专业开学要带电脑吗,大学上课要带电脑吗
  9. python安全攻防---爬虫基础---BeautifulSoup解析
  10. Tiny Core Linux 4.5 发布,微型 Linux 操作系统
  11. 转:Python中的文件和目录操作
  12. Consul架构介绍
  13. Asp.Net MVC使用HtmlHelper渲染,并传递FormCollection参数的陷阱
  14. 在Windows中测试c语言单个函数运行时间方法
  15. python编程:从入门到实践学习笔记-文件和异常
  16. python爬取天气数据的header_[python爬虫]爬取天气网全国所有县市的天气数据
  17. 用计算机控制人造卫星和导弹发射,广西成人高校计算机实用基础统考理论试题(...
  18. Big5和Gb编码转换
  19. Mysql 导出表结构或数据
  20. 教你快速制作多张图片、多段视频的画中画特效

热门文章

  1. 一篇文章普及各种ios基本知识
  2. 判断二极管导通例题_通信电源 | 1个二极管是如何改变电流的?
  3. CISCO路由器配置完全手册
  4. 阿里的面试官都喜欢问哪些问题?
  5. 3G无线上网卡种类介绍
  6. 大工18秋计算机1答案,大工18秋《专业英语(计算机英语)》在线作业1.docx
  7. 国家标准《信息技术 人工智能 知识图谱技术框架》第三次编辑会成功召开
  8. UE4中Pak文件的读取规则
  9. 能量信号和功率信号的分别
  10. 电影 -- 碟中谍4