前一章節已經介紹了,把方法分解成什麼樣子來分析,這裏先來分析一個方法resultMapElements,在這個方法前還有cacheRefElement與cacheElement,可是因爲配置文件中沒有配置這2項,因此這裏就先跳過。而parameterMapElement在最新的MyBatis中是過期的,也先跳過,之後能夠在補充。html
1 配置文件java
<resultMap id="BaseResultMap" type="cn.vansky.schedule.time.menu.bo.Menu"> <!-- WARNING - @mbggenerated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Fri Aug 14 16:08:36 CST 2015. --> <id column="Id" property="id" jdbcType="INTEGER" /> <result column="menu_name" property="menuName" jdbcType="VARCHAR" /> <result column="menu_remark" property="menuRemark" jdbcType="VARCHAR" /> <result column="menu_parent_id" property="menuParentId" jdbcType="INTEGER" /> <result column="menu_url" property="menuUrl" jdbcType="VARCHAR" /> <result column="is_show" property="isShow" jdbcType="TINYINT" /> <result column="is_delete" property="isDelete" jdbcType="TINYINT" /> <result column="operation_user_name" property="operationUserName" jdbcType="VARCHAR" /> <result column="operation_time" property="operationTime" jdbcType="TIMESTAMP" /> </resultMap>
這裏是使用本身擴展MyBatis的自動生成代碼,生成的resultMap配置。git
2 代碼github
private void resultMapElements(List<XNode> list) throws Exception { for (XNode resultMapNode : list) { try { resultMapElement(resultMapNode); } catch (IncompleteElementException e) { // ignore, it will be retried } } } private ResultMap resultMapElement(XNode resultMapNode) throws Exception { return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList()); }
這裏是獲取當前文件中全部resultMap對應的XML,而後分別對每一個resultMap進行處理。這裏會去捕獲一個專門的異常,目前還不清楚是起什麼做用,先繼續往下分析。
3 方法resultMapElementsql
// resultMapNode是對應的resuleMap的XML信息,這裏首先獲取配置中的id,若是沒有就自動生成一個id // BaseResultMap String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); // 那麼這行很明顯是獲取type了,若是沒有就獲取ofType,尚未就獲取resultType,仍是沒有就獲取javaType // cn.vansky.schedule.time.menu.bo.Menu String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); // 猜測是獲取繼承的父類 // null String extend = resultMapNode.getStringAttribute("extends"); // null Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 獲取type對應的Class Class<?> typeClass = resolveClass(type); Discriminator discriminator = null; List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); // 這裏additionalResultMappings是空列表,而不是null resultMappings.addAll(additionalResultMappings); // 獲取子節點id及result List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { if ("constructor".equals(resultChild.getName())) { processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { // id與result都走這裏 ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } }
4 buildResultMappingFromContextmybatis
如下都是以ID爲例,進行分析。app
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception { // id String property = context.getStringAttribute("property"); // Id String column = context.getStringAttribute("column"); // null String javaType = context.getStringAttribute("javaType"); // INTEGER String jdbcType = context.getStringAttribute("jdbcType"); // null String nestedSelect = context.getStringAttribute("select"); // null String nestedResultMap = context.getStringAttribute("resultMap", processNestedResultMappings(context, Collections.<ResultMapping> emptyList())); // null String notNullColumn = context.getStringAttribute("notNullColumn"); // null String columnPrefix = context.getStringAttribute("columnPrefix"); // null String typeHandler = context.getStringAttribute("typeHandler"); // null String resulSet = context.getStringAttribute("resultSet"); // null String foreignColumn = context.getStringAttribute("foreignColumn"); // false boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager")); // null Class<?> javaTypeClass = resolveClass(javaType); @SuppressWarnings("unchecked") // null Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler); // 獲取到JdbcType JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); // 這裏就會構建生成一個ResultMapping return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy); }
5 MapperBuilderAssistant的buildResultMapping方法fetch
public ResultMapping buildResultMapping( Class<?> resultType, String property, String column, Class<?> javaType, JdbcType jdbcType, String nestedSelect, String nestedResultMap, String notNullColumn, String columnPrefix, Class<? extends TypeHandler<?>> typeHandler, List<ResultFlag> flags, String resultSet, String foreignColumn, boolean lazy) { // 這裏若是配置中有javaType屬性直接返回,不然經過type配置的類及屬性獲取對應的Class Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType); // 獲取類型處理器 null TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); // 0 List<ResultMapping> composites = parseCompositeColumnName(column); if (composites.size() > 0) column = null; // 初始化一些內部信息 ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass); builder.jdbcType(jdbcType); builder.nestedQueryId(applyCurrentNamespace(nestedSelect, true)); builder.nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)); builder.resultSet(resultSet); builder.typeHandler(typeHandlerInstance); builder.flags(flags == null ? new ArrayList<ResultFlag>() : flags); builder.composites(composites); builder.notNullColumns(parseMultipleColumnNames(notNullColumn)); builder.columnPrefix(columnPrefix); builder.foreignColumn(foreignColumn); builder.lazy(lazy); return builder.build(); }
以上生成的ResultMapping的typeHandler仍是爲null的,由於這裏獲取的是在配置文件中對應的TypeHandler。那麼就看一下builder.build()裏面是什麼代碼。ui
public ResultMapping build() { // lock down collections resultMapping.flags = Collections.unmodifiableList(resultMapping.flags); resultMapping.composites = Collections.unmodifiableList(resultMapping.composites); resolveTypeHandler(); validate(); return resultMapping; }
原來這裏根據配置信息解析出默認使用的TypeHandler,還對一些ResultMapping的信息作了驗證,具體代碼,自行研究就能夠了。
this
6 ResultMapping
6.1 屬性
ID最後對應的ResultMapping的信息。
/** 全局配置類 */ private Configuration configuration; /** id */ private String property; /** Id */ private String column; /** java.lang.Integer */ private Class<?> javaType; /** INTEGER */ private JdbcType jdbcType; /** IntegerTypeHandler */ private TypeHandler<?> typeHandler; /** null */ private String nestedResultMapId; /** null */ private String nestedQueryId; /** 空列表 */ private Set<String> notNullColumns; /** null */ private String columnPrefix; /** ID */ private List<ResultFlag> flags; /** 空列表 */ private List<ResultMapping> composites; /** null */ private String resultSet; /** null */ private String foreignColumn; /** false */ private boolean lazy;
menu_name最後對應的ResultMapping的信息。
/** 全局配置類 */ private Configuration configuration; /** menuName */ private String property; /** menu_name */ private String column; /** java.lang.String */ private Class<?> javaType; /** VARCHAR */ private JdbcType jdbcType; /** StringTypeHandler */ private TypeHandler<?> typeHandler; /** null */ private String nestedResultMapId; /** null */ private String nestedQueryId; /** 空列表 */ private Set<String> notNullColumns; /** null */ private String columnPrefix; /** 空列表 */ private List<ResultFlag> flags; /** 空列表 */ private List<ResultMapping> composites; /** null */ private String resultSet; /** null */ private String foreignColumn; /** false */ private boolean lazy;
MyBatis的地址http://mybatis.github.io/mybatis-3/zh/sqlmap-xml.html#Result_Maps這裏有不少例子,喜歡的童鞋能夠多去研究一下。
當對id及result都遍歷之後,會生成List<ResultMapping>。
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; }
若是這裏解析失敗會拋出前面出現的捕獲異常,並把錯誤的解析放入Configuration(全局配置類)的Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>()。
7 MapperBuilderAssistant的addResultMap方法
public ResultMap addResultMap( String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) { // 這裏把命名空間與id合併 // cn.vansky.schedule.time.menu.dao.MenuMapper.BaseResultMap id = applyCurrentNamespace(id, false); // null extend = applyCurrentNamespace(extend, true); ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping); if (extend != null) { if (!configuration.hasResultMap(extend)) { throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'"); } ResultMap resultMap = configuration.getResultMap(extend); List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings()); extendedResultMappings.removeAll(resultMappings); // Remove parent constructor if this resultMap declares a constructor. boolean declaresConstructor = false; for (ResultMapping resultMapping : resultMappings) { if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) { declaresConstructor = true; break; } } if (declaresConstructor) { Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator(); while (extendedResultMappingsIter.hasNext()) { if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) { extendedResultMappingsIter.remove(); } } } resultMappings.addAll(extendedResultMappings); } resultMapBuilder.discriminator(discriminator); // 這裏把新增的ResultMapping作了處理 ResultMap resultMap = resultMapBuilder.build(); configuration.addResultMap(resultMap); return resultMap; }
這裏沒有具體介紹,還須要慢慢深究。下面來看最終的ResultMap信息。
8 ResultMap屬性
// cn.vansky.schedule.time.menu.dao.MenuMapper.BaseResultMap private String id; // cn.vansky.schedule.time.menu.bo.Menu private Class<?> type; // 對應配置中的全部結果列,這裏9個對象 private List<ResultMapping> resultMappings; // 對應配置中的id列,這裏1個對象 private List<ResultMapping> idResultMappings; // 對應構造器的列,這裏爲0個對象 private List<ResultMapping> constructorResultMappings; // 看代碼得出,只要不是構造器列,就屬於這裏,也就是包括id和result private List<ResultMapping> propertyResultMappings; // 對應的配置的列名所有大寫,這裏使用的set因此也就不是按順序來排列的了 // MENU_PARENT_ID,MENU_NAME,MENU_URL,OPERATION_TIME,IS_SHOW,OPERATION_USER_NAME,ID,IS_DELETE,MENU_REMARK private Set<String> mappedColumns; // null private Discriminator discriminator; // false private boolean hasNestedResultMaps; // false private boolean hasNestedQueries; // null private Boolean autoMapping;
至此最終的ResultMap信息出來了。
總結:
這裏不少東西,做者也是一點一點去研究,並且實際項目中的配置也不是特別的多,因此有些還待完善。