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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值