MyBatis源碼深入分析

以下面幾行代碼進行深度分析:

 		String resource = "mybatis-config.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
        employeeDao.queryById(3);
//        List empList = employeeDao.queryAll();
//        for (Object e : empList)
//            System.out.println(e);



一、前三行代碼

       主要功能:讀取並解析mybatis核心配置文件到內存中

Alt

       分析mapper的解析情況,核心配置文件中註冊接口,跟蹤源碼也可看到分爲package標籤批量註冊mapper標籤的url或class或resource屬性單個註冊

Alt

       使用package標籤批量註冊的時候,會多一步掃描當前包,最後都是讀入SqlSessionFactory的configuration屬性的

Alt

Alt
       HashMap中,key爲接口的類,value則是代理工廠。

Alt

       MapperAnnotationBuilder的parse( )方法:

Alt

Alt

Alt

Alt
Alt
Alt
       XMLStatementBuilder中parseStatementNode( )方法的源碼:

		public void parseStatementNode() {
			//取到id
	        String id = this.context.getStringAttribute("id");
	        
	        String databaseId = this.context.getStringAttribute("databaseId");
	        if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
	            String nodeName = this.context.getNode().getNodeName();
	            SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
	            boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
	            boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
	            boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
	            boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
	            XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
	            includeParser.applyIncludes(this.context.getNode());
	            String parameterType = this.context.getStringAttribute("parameterType");
	            Class<?> parameterTypeClass = this.resolveClass(parameterType);
	            String lang = this.context.getStringAttribute("lang");
	            LanguageDriver langDriver = this.getLanguageDriver(lang);
	            this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
	            String keyStatementId = id + "!selectKey";
	            keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
	            Object keyGenerator;
	            if (this.configuration.hasKeyGenerator(keyStatementId)) {
	                keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
	            } else {
	                keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
	            }
	
				//解析sql語句
	            SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
	            
	            StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
	            Integer fetchSize = this.context.getIntAttribute("fetchSize");
	            Integer timeout = this.context.getIntAttribute("timeout");
	            String parameterMap = this.context.getStringAttribute("parameterMap");
	            String resultType = this.context.getStringAttribute("resultType");
	            Class<?> resultTypeClass = this.resolveClass(resultType);
	            String resultMap = this.context.getStringAttribute("resultMap");
	            String resultSetType = this.context.getStringAttribute("resultSetType");
	            ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
	            if (resultSetTypeEnum == null) {
	                resultSetTypeEnum = this.configuration.getDefaultResultSetType();
	            }
	
	            String keyProperty = this.context.getStringAttribute("keyProperty");
	            String keyColumn = this.context.getStringAttribute("keyColumn");
	            String resultSets = this.context.getStringAttribute("resultSets");
	            this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
	        }
	    }

Alt

       這裏解析sql語句的時候,明顯分了動態sql和非靜態兩種情況解析。

Alt
Alt
       Mybatis讀取參數的#{ }都出來了,有種守得雲開見月明的感覺了。

Alt
       GenericTokenParser的parse( … )方法源碼(生成預編譯的sql語句將#{ }轉成?):

		public String parse(String text) {
	        if (text != null && !text.isEmpty()) {
	            int start = text.indexOf(this.openToken);
	            if (start == -1) {
	                return text;
	            } else {
	                char[] src = text.toCharArray();
	                int offset = 0;
	                StringBuilder builder = new StringBuilder();
	
	                for(StringBuilder expression = null; start > -1; start = text.indexOf(this.openToken, offset)) {
	                    if (start > 0 && src[start - 1] == '\\') {
	                        builder.append(src, offset, start - offset - 1).append(this.openToken);
	                        offset = start + this.openToken.length();
	                    } else {
	                        if (expression == null) {
	                            expression = new StringBuilder();
	                        } else {
	                            expression.setLength(0);
	                        }
	
	                        builder.append(src, offset, start - offset);
	                        offset = start + this.openToken.length();
	
	                        int end;
	                        for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
	                            if (end <= offset || src[end - 1] != '\\') {
	                                expression.append(src, offset, end - offset);
	                                break;
	                            }
	
	                            expression.append(src, offset, end - offset - 1).append(this.closeToken);
	                            offset = end + this.closeToken.length();
	                        }
	
	                        if (end == -1) {
	                            builder.append(src, start, src.length - start);
	                            offset = src.length;
	                        } else {
	                            builder.append(this.handler.handleToken(expression.toString()));
	                            offset = end + this.closeToken.length();
	                        }
	                    }
	                }
	
	                if (offset < src.length) {
	                    builder.append(src, offset, src.length - offset);
	                }
	
	                return builder.toString();
	            }
	        } else {
	            return "";
	        }
	    }




二、第四行代碼

Alt
       新建一個SqlSession感覺就是創建一個非自動提交的事務,事務隔離級別爲空(默認)。

Alt
Alt




三、第五行代碼

       這時候通知JVM要對之前接口中的所有方法都進行代理,利用代理工廠創建代理對象即MapperProxy。

Alt
Alt





四、第六行及以後代碼

       接口方法執行前,會調用MapperProxy的invoke方法,通過該類匿名內部類又交給靜態內部類去代理。

Alt
AAlt


       主要還是看一下查詢,Mybatis是如何將查詢到的字段名和實體類的屬性名對應的。


1、XML中自定義了resultMap

       測試的具體sql語句如下:

		<mapper namespace="com.cj.dao.EmployeeDao">
		    <resultMap id="empsMap" type="employee">
		        <id column="emp_id" property="empId"/>
		        <result column="emp_name" property="empName"/>
		        <result column="emp_sex" typeHandler="com.cj.handler.MyTypeHandler" property="empSex"/>
		        <result column="emp_salary" property="empSalary"/>
		        <result column="emp_manager_id" property="empManagerId"/>
		        <result column="emp_dept_id" property="empDeptId"/>
		    </resultMap>
		    <select id="queryAll" resultMap="empsMap">
		        select * from emp
		    </select>
		</mapper>

Alt
       this.method.hasRowBounds()是判斷rowBoundsIndex是否爲空,這個可能和Mybatis分頁插件有關。

Alt

Alt

Alt

Alt

Alt
       BaseExecutor類的doQuery方法是抽象方法,子類中實現。

Alt

       執行器的prepareStatement( … )方法創建了JDBC的連接。

Alt
       因爲使用了log4j日誌工具,所以Connection也被代理了。

Alt

Alt

       ResultSetWrapper類對象rsw就是查詢的結果集,放在循環裏面,一次取一條出來。

Alt
Alt
Alt

Alt
       自定義了resultMap之後,applyAutomaticMappings函數不會執行主邏輯。只有當resultType的時候纔會執行主邏輯。

       Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);這行代碼大概就是反射創建對象。

Alt
       調用configuration的newMetaObject方法,使得MetaObject類的objectWrapper中的object屬性的和傳入參數的object指向同一個堆空間中的對象。

Alt

Alt

       分析自定義resultMap情況下不會執行applyAutomaticMappings函數主邏輯的原因

Alt
Alt
Alt
       mappedColumns的集合需要從resultMap裏面尋找映射的列名,現在是自定義的resultMap,當然可以找到,所以unMappedColumnNamesMap這個哈希表裏面肯定是空的。

Alt
Alt
       再來看applyAutomaticMappings( … )方法,迭代器爲空直接返回。

Alt

       所以當自定義resultMap的時候,映射邏輯還是在applyPropertyMappings( … )方法中執行。

       applyPropertyMappings函數先讀取mappedColumnNamesMap哈希表中的列名。主要就是根據ResultMappings裏面的字段類型與實體類的一對一關係,通過迭代器將查詢出來的結果賦值(經過相應的TypeHandler)。返回類型爲resultType時,迭代器爲空就直接返回。

		private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
	        List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
	        boolean foundValues = false;
	        List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
	        Iterator var9 = propertyMappings.iterator();
	
	        while(true) {
	            while(true) {
	                Object value;
	                String property;
	                do {
	                    ResultMapping propertyMapping;
	                    String column;
	                    do {
	                        if (!var9.hasNext()) {
	                            return foundValues;
	                        }
	
	                        propertyMapping = (ResultMapping)var9.next();
	                        column = this.prependPrefix(propertyMapping.getColumn(), columnPrefix);
	                        if (propertyMapping.getNestedResultMapId() != null) {
	                            column = null;
	                        }
	                    } while(!propertyMapping.isCompositeResult() && (column == null || !mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) && propertyMapping.getResultSet() == null);
	
	                    value = this.getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
	                    property = propertyMapping.getProperty();
	                } while(property == null);
	
	                if (value == DEFERRED) {
	                    foundValues = true;
	                } else {
	                    if (value != null) {
	                        foundValues = true;
	                    }
	
	                    if (value != null || this.configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()) {
	                        metaObject.setValue(property, value);
	                    }
	                }
	            }
	        }
	    }

Alt

       查詢結果最後還是要經過相應的TypeHandler。

Alt

Alt
       最後返回rowValue。這也可以應證resultMap如果不寫出相應對象的字段映射標籤的話其值會是空的

		private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
	        ResultLoaderMap lazyLoader = new ResultLoaderMap();
	        Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
	        if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
	            MetaObject metaObject = this.configuration.newMetaObject(rowValue);
	            boolean foundValues = this.useConstructorMappings;
	            if (this.shouldApplyAutomaticMappings(resultMap, false)) {
	                foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
	            }
	
	            foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
	            foundValues = lazyLoader.size() > 0 || foundValues;
	            rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
	        }
	
	        return rowValue;
	    }



2、XML使用默認的resultType

		<mapper namespace="com.cj.dao.EmployeeDao">
		    <select id="queryById" resultType="employee">
		        select emp_name from emp where emp_id=#{id}
		    </select>
		</mapper>

Alt
       具體執行流程和之前自定義resultMap是絕大部分都是相同的。

Alt
Alt

       返回結果集處理那裏,基本也是一樣的。只是此時執行的映射主邏輯是applyAutomaticMappings( … )方法裏面的。

Alt
       此時resultMap爲空,unMappedColumnNamesMap中有值。而且映射的時候,會經過TypeHandler。

		private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
	        List<DefaultResultSetHandler.UnMappedColumnAutoMapping> autoMapping = this.createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
	        boolean foundValues = false;
	        if (!autoMapping.isEmpty()) {
	            Iterator var7 = autoMapping.iterator();
	
	            while(true) {
	                DefaultResultSetHandler.UnMappedColumnAutoMapping mapping;
	                Object value;
	                do {
	                    if (!var7.hasNext()) {
	                        return foundValues;
	                    }
	
	                    mapping = (DefaultResultSetHandler.UnMappedColumnAutoMapping)var7.next();
	                    
	                    //經過TypeHandler
	                    value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
	                    
	                    if (value != null) {
	                        foundValues = true;
	                    }
	                } while(value == null && (!this.configuration.isCallSettersOnNulls() || mapping.primitive));
	
	                metaObject.setValue(mapping.property, value);
	            }
	        } else {
	            return foundValues;
	        }
	    }

       再看看createAutomaticMappings( … )方法的源碼,駝峯命名法

	private List<DefaultResultSetHandler.UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        String mapKey = resultMap.getId() + ":" + columnPrefix;
        List<DefaultResultSetHandler.UnMappedColumnAutoMapping> autoMapping = (List)this.autoMappingsCache.get(mapKey);
        if (autoMapping == null) {
            autoMapping = new ArrayList();
            List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
            Iterator var8 = unmappedColumnNames.iterator();

            while(true) {
                while(true) {
                    String columnName;
                    String propertyName;
                    while(true) {
                        if (!var8.hasNext()) {
                            this.autoMappingsCache.put(mapKey, autoMapping);
                            return (List)autoMapping;
                        }

                        columnName = (String)var8.next();
                        propertyName = columnName;
                        if (columnPrefix == null || columnPrefix.isEmpty()) {
                            break;
                        }

                        if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                            propertyName = columnName.substring(columnPrefix.length());
                            break;
                        }
                    }

                    //熟悉的駝峯命名法配置
                    String property = metaObject.findProperty(propertyName, this.configuration.isMapUnderscoreToCamelCase());
                    
                    if (property != null && metaObject.hasSetter(property)) {
                        if (!resultMap.getMappedProperties().contains(property)) {
                            Class<?> propertyType = metaObject.getSetterType(property);
                            if (this.typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
                                TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
                                ((List)autoMapping).add(new DefaultResultSetHandler.UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
                            } else {
                                this.configuration.getAutoMappingUnknownColumnBehavior().doAction(this.mappedStatement, columnName, property, propertyType);
                            }
                        }
                    } else {
                        this.configuration.getAutoMappingUnknownColumnBehavior().doAction(this.mappedStatement, columnName, property != null ? property : propertyName, (Class)null);
                    }
                }
            }
        } else {
            return (List)autoMapping;
        }
    }

       最後返回foundValues。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章