在 org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration 中进行配置解析,其中这一行就是解析 mappers1 mapperElement(root.evalNode("mappers" ));
具体的代码会执行到这1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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); try (InputStream inputStream = Resources.getResourceAsStream(resource)) { XMLMapperBuilder mapperParser = new XMLMapperBuilder (inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); }
然后再是 parse 过程1 2 3 4 5 6 7 8 9 10 11 12 public void parse () { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper" )); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
继续往下走1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void configurationElement (XNode context) { try { String namespace = context.getStringAttribute("namespace" ); if (namespace == null || namespace.isEmpty()) { 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); } }
然后是1 2 3 4 5 6 7 private void buildStatementFromContext (List<XNode> list) { if (configuration.getDatabaseId() != null ) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null ); }
继续1 2 3 4 5 6 7 8 9 10 11 12 private void buildStatementFromContext (List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder (configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
这个代码比较长,做下简略,只保留相关代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void parseStatementNode () { String id = context.getStringAttribute("id" ); String databaseId = context.getStringAttribute("databaseId" ); if (!databaseIdMatchesCurrent(id, databaseId, this .requiredDatabaseId)) { return ; } 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 ); SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
然后根据 LanguageDriver,我们这用的 XMLLanguageDriver,先是初始化1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Override public SqlSource createSqlSource (Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder (configuration, script, parameterType); return builder.parseScriptNode(); } public XMLScriptBuilder (Configuration configuration, XNode context, Class<?> parameterType) { super (configuration); this .context = context; this .parameterType = parameterType; initNodeHandlerMap(); } private void initNodeHandlerMap () { nodeHandlerMap.put("trim" , new TrimHandler ()); nodeHandlerMap.put("where" , new WhereHandler ()); nodeHandlerMap.put("set" , new SetHandler ()); nodeHandlerMap.put("foreach" , new ForEachHandler ()); nodeHandlerMap.put("if" , new IfHandler ()); nodeHandlerMap.put("choose" , new ChooseHandler ()); nodeHandlerMap.put("when" , new IfHandler ()); nodeHandlerMap.put("otherwise" , new OtherwiseHandler ()); nodeHandlerMap.put("bind" , new BindHandler ()); }
初始化解析器以后就开始解析了1 2 3 4 5 6 7 8 9 10 11 public SqlSource parseScriptNode () { MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { sqlSource = new DynamicSqlSource (configuration, rootSqlNode); } else { sqlSource = new RawSqlSource (configuration, rootSqlNode, parameterType); } return sqlSource; }
但是这里可能做的事情比较多1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protected MixedSqlNode parseDynamicTags (XNode node) { List<SqlNode> contents = new ArrayList <>(); NodeList children = node.getNode().getChildNodes(); for (int i = 0 ; i < children.getLength(); i++) { XNode child = node.newXNode(children.item(i)); if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { String data = child.getStringBody("" ); TextSqlNode textSqlNode = new TextSqlNode (data); if (textSqlNode.isDynamic()) { contents.add(textSqlNode); isDynamic = true ; } else { contents.add(new StaticTextSqlNode (data)); } } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { String nodeName = child.getNode().getNodeName(); NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler == null ) { throw new BuilderException ("Unknown element <" + nodeName + "> in SQL statement." ); } handler.handleNode(child, contents); isDynamic = true ; } } return new MixedSqlNode (contents); }
再看下 handleNode 的逻辑1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Override public void handleNode (XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String collection = nodeToHandle.getStringAttribute("collection" ); Boolean nullable = nodeToHandle.getBooleanAttribute("nullable" ); String item = nodeToHandle.getStringAttribute("item" ); String index = nodeToHandle.getStringAttribute("index" ); String open = nodeToHandle.getStringAttribute("open" ); String close = nodeToHandle.getStringAttribute("close" ); String separator = nodeToHandle.getStringAttribute("separator" ); ForEachSqlNode forEachSqlNode = new ForEachSqlNode (configuration, mixedSqlNode, collection, nullable, index, item, open, close, separator); targetContents.add(forEachSqlNode); } protected MixedSqlNode parseDynamicTags (XNode node) { List<SqlNode> contents = new ArrayList <>(); NodeList children = node.getNode().getChildNodes(); for (int i = 0 ; i < children.getLength(); i++) { XNode child = node.newXNode(children.item(i)); if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { String data = child.getStringBody("" ); TextSqlNode textSqlNode = new TextSqlNode (data); if (textSqlNode.isDynamic()) { contents.add(textSqlNode); isDynamic = true ; } else { contents.add(new StaticTextSqlNode (data)); } @Override public void handleNode (XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String collection = nodeToHandle.getStringAttribute("collection" ); Boolean nullable = nodeToHandle.getBooleanAttribute("nullable" ); String item = nodeToHandle.getStringAttribute("item" ); String index = nodeToHandle.getStringAttribute("index" ); String open = nodeToHandle.getStringAttribute("open" ); String close = nodeToHandle.getStringAttribute("close" ); String separator = nodeToHandle.getStringAttribute("separator" ); ForEachSqlNode forEachSqlNode = new ForEachSqlNode (configuration, mixedSqlNode, collection, nullable, index, item, open, close, separator); targetContents.add(forEachSqlNode); }
再回过来1 2 3 4 5 6 7 8 9 10 11 12 public SqlSource parseScriptNode () { MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { sqlSource = new DynamicSqlSource (configuration, rootSqlNode); } else { sqlSource = new RawSqlSource (configuration, rootSqlNode, parameterType); } return sqlSource; }
这里就做完了预处理工作,真正在执行的执行的时候还需要进一步解析
因为前面讲过很多了,所以直接跳到这里1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 @Override public <T> T selectOne (String statement, Object parameter) { List<T> list = this .selectList(statement, parameter); if (list.size() == 1 ) { return list.get(0 ); } else if (list.size() > 1 ) { throw new TooManyResultsException ("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null ; } } @Override public <E> List<E> selectList (String statement, Object parameter) { return this .selectList(statement, parameter, RowBounds.DEFAULT); } @Override public <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds) { return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER); } private <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } @Override public <E> List<E> query (MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } 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); } 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; } @Override public BoundSql getBoundSql (Object parameterObject) { DynamicContext context = new DynamicContext (configuration, parameterObject); rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder (configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; } public boolean apply (DynamicContext context) { contents.forEach(node -> node.apply(context)); return true ; }
我们重点看 foreach 的逻辑1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 @Override public boolean apply (DynamicContext context) { Map<String, Object> bindings = context.getBindings(); final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings, Optional.ofNullable(nullable).orElseGet(configuration::isNullableOnForEach)); if (iterable == null || !iterable.iterator().hasNext()) { return true ; } boolean first = true ; applyOpen(context); int i = 0 ; for (Object o : iterable) { DynamicContext oldContext = context; if (first || separator == null ) { context = new PrefixedContext (context, "" ); } else { context = new PrefixedContext (context, separator); } int uniqueNumber = context.getUniqueNumber(); if (o instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o; applyIndex(context, mapEntry.getKey(), uniqueNumber); applyItem(context, mapEntry.getValue(), uniqueNumber); } else { applyIndex(context, i, uniqueNumber); applyItem(context, o, uniqueNumber); } contents.apply(new FilteredDynamicContext (configuration, context, index, item, uniqueNumber)); if (first) { first = !((PrefixedContext) context).isPrefixApplied(); } context = oldContext; i++; } applyClose(context); context.getBindings().remove(item); context.getBindings().remove(index); return true ; } public SqlSource parse (String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler (configuration, parameterType, additionalParameters); GenericTokenParser parser = new GenericTokenParser ("#{" , "}" , handler); String sql; if (configuration.isShrinkWhitespacesInSql()) { sql = parser.parse(removeExtraWhitespaces(originalSql)); } else { sql = parser.parse(originalSql); } return new StaticSqlSource (configuration, sql, handler.getParameterMappings()); }
可以看到这里,然后再进行替换
真实的从 ?
替换成具体的变量值,是在这里org.apache.ibatis.executor.SimpleExecutor#doQuery
调用了1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 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; } @Override public void parameterize (Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } @Override public 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)) { value = 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 = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null ) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1 , value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException ("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }