摘要:目標理清加載解析文件的過程理清執行的過程。先看源碼生成解析配置文件考慮到項目的配置,看下生成和的代碼。在生成的過程中,使用了,這個類繼承了。在該類的構造器中加載了和。下面看一下代碼從緩存中獲取對象執行下面是方法至此,執行完成。
目標:
理清mybatis加載解析mapper文件的過程;
理清mybatis執行SQL的過程。
上一篇文章分析mybatis加載配置的源碼時提到了org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration方法,現在繼續分析其中的mapperElement方法。先看源碼:
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 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."); } } } } }
考慮到項目的配置,看下生成XMLMapperBuilder和mapperParser.parse()的代碼。
在生成XMLMapperBuilder的過程中,使用了MapperBuilderAssistant,這個類繼承了BaseBuilder。在該類的構造器中加載了TypeAliasRegistry和TypeHandlerRegistry。
下面重點看mapperParserparse()
public void parse() { // 判斷是否已經加載資源 if (!configuration.isResourceLoaded(resource)) { // 配置/mapper節點下的子節點 configurationElement(parser.evalNode("/mapper")); // 加載resource資源 configuration.addLoadedResource(resource); // 綁定命名空間 bindMapperForNamespace(); } // 加載未加載完成的資源 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
下面主要看下configurationElement的代碼:
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); } }
可以看出主要是解析不同的節點,并放進builderAssistant里面去。
下面看下執行SQL的過程。
ClipsDAO clipsDAO = session.getMapper(ClipsDAO.class); ClipsEntity clipsEntity = clipsDAO.selectById(1);
查看session.getMapper()的實現:
// org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper @Override publicT getMapper(Class type) { return configuration. getMapper(type, this); } // org.apache.ibatis.session.Configuration#getMapper public T getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } // org.apache.ibatis.binding.MapperRegistry#getMapper public T getMapper(Class type, SqlSession sqlSession) { final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory ) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
可以看出,mybatis通過動態代理為接口生成了代理類,我們知道在加載配置時,bindMapperForNamespace方法調用了configuration.addMapper()方法把Class映射到org.apache.ibatis.binding.MapperRegistry#knownMappers中去的。
下面看一下MapperProxy代碼:
@Override public 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); } // 從緩存中獲取MapperMethod對象 final MapperMethod mapperMethod = cachedMapperMethod(method); // 執行SQL return mapperMethod.execute(sqlSession, args); }
下面是MapperMethod.execute()方法:
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); } 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; }
至此,SQL執行完成。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72929.html
摘要:跳槽時時刻刻都在發生,但是我建議大家跳槽之前,先想清楚為什么要跳槽。切不可跟風,看到同事一個個都走了,自己也盲目的開始面試起來期間也沒有準備充分,到底是因為技術原因影響自己的發展,偏移自己規劃的軌跡,還是錢給少了,不受重視。 跳槽時時刻刻都在發生,但是我建議大家跳槽之前,先想清楚為什么要跳槽。切不可跟風,看到同事一個個都走了,自己也盲目的開始面試起來(期間也沒有準備充分),到底是因為技...
摘要:我在面試前針對基礎也花了不少的時間,期間也將自己寫過的博文粗略地刷了一遍,同時也在網上找了不少比較好的資料部分是沒看完的。看面試題也是校驗自己是否真正理解了這個知識點,也很有可能會有新的收獲。 一、前言 只有光頭才能變強 回顧前面: 廣州三本找Java實習經歷 上一篇寫了自己面試的經歷和一些在面試的時候遇到的題目(筆試題和面試題)。 我在面試前針對Java基礎也花了不少的時間,期間也將...
摘要:執行沒有,批處理不支持,將所有都添加到批處理中,等待統一執行,它緩存了多個對象,每個對象都是完畢后,等待逐一執行批處理。 Mybatis常見面試題 #{}和${}的區別是什么? #{}和${}的區別是什么? 在Mybatis中,有兩種占位符 #{}解析傳遞進來的參數數據 ${}對傳遞進來的參數原樣拼接在SQL中 #{}是預編譯處理,${}是字符串替換。 使用#{}可以有效的防止...
摘要:從使用到原理學習線程池關于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實現在軟件開發中,分散于應用中多出的功能被稱為橫切關注點如事務安全緩存等。 Java 程序媛手把手教你設計模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經風雨慢慢變老,回首走過的點點滴滴,依然清楚的記得當初愛情萌芽的模樣…… Java 進階面試問題列表 -...
閱讀 2406·2021-11-18 10:02
閱讀 1922·2021-10-13 09:40
閱讀 2999·2021-09-07 10:07
閱讀 2106·2021-09-04 16:48
閱讀 1005·2019-08-30 13:18
閱讀 2452·2019-08-29 14:03
閱讀 2922·2019-08-29 12:54
閱讀 3155·2019-08-26 11:41