深入了解MyBatis返回值 电玩女神 2022-04-15 03:29 299阅读 0赞 想了解返回值,我们需要了解resultType,resultMap以及接口方法中定义的返回值。 我们先看resultType和resultMapresultType和resultMap 大家应该都知道在MyBatis的标签中有两种设置返回值的方式,分别是resultMap和resultType。 处理resultMap和resultType的代码如下: private void setStatementResultMap( String resultMap, Class<?> resultType, ResultSetType resultSetType, MappedStatement.Builder statementBuilder) { resultMap = applyCurrentNamespace(resultMap, true); List<ResultMap> resultMaps = new ArrayList<ResultMap>(); if (resultMap != null) \{ String\[\] resultMapNames = resultMap.split(","); for (String resultMapName : resultMapNames) \{ try \{ resultMaps.add(configuration.getResultMap(resultMapName.trim())); \} catch (IllegalArgumentException e) \{ throw new IncompleteElementException("Could not find result map " + resultMapName, e); \} \} \} else if (resultType != null) \{ ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder( configuration, statementBuilder.id() + "-Inline", resultType, new ArrayList<ResultMapping>(), null); resultMaps.add(inlineResultMapBuilder.build()); \} statementBuilder.resultMaps(resultMaps); statementBuilder.resultSetType(resultSetType); } 可以看到这里会优先处理resultMap,但是也使用了resultType。 接下来看MyBatis获取数据后,如果处理一行结果(以简单数据为例,不考虑嵌套情况): private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null); if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(resultObject); boolean foundValues = resultMap.getConstructorResultMappings().size() > 0; if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues ? resultObject : null; return resultObject; } return resultObject;} 上面这段代码中重要的代码如下: if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;}foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; if中判断的是当前是否支持自动映射(可以配置),这一点很重要,如果不支持,那么没法使用resultType方式,必须用resultMap方式,如果支持,resultType方式和resultMap方式可以同时使用。 这里的基本逻辑是先对没有resultMap的属性自动映射赋值,通过applyAutomaticMappings实现。 如果对象有resultMap,那么还会进行applyPropertyMappings方法。 也就是先处理resultType中自动映射的字段,在处理resultMap中的配置的字段,两者可以同时使用! 下面按照顺序分别说两种方式。resultType方式 如果支持自动映射,那么会执行applyAutomaticMappings,这里面有metaObject参数。 final MetaObject metaObject = configuration.newMetaObject(resultObject); 我们看看创建metaObject最关键的一个地方,在Reflector类中: for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);} 这里将实体中的属性名,做了一个映射,是大写的对应实际的属性名。例如ID:id。 在applyAutomaticMappings中的第一行,首先获取没有映射的列名: final List unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix); 获取列名的时候: for (String columnName : columnNames) { final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH); if (mappedColumns.contains(upperColumnName)) { mappedColumnNames.add(upperColumnName); } else { unmappedColumnNames.add(columnName); }} 注意这里将列名转换为大写形式,同时保存了mappedColumnNames映射的列和unmappedColumnNames未映射的列。 因为不管是属性名还是查询列都是大写的,所以只要列名和属性名大写一致,就会匹配上。 因此我们在写sql的时候,不需要对查询列的大小写进行转换,自动匹配是不区分大小写的。resultMap方式 这种方式也很简单,上面提到了mappedColumnNames,在判断是否为映射列的时候,使用mappedColumns.contains(upperColumnName)进行判断,mappedColumns是我们配置的映射的列,那是不是我们配置的时候必须大写呢? 实际上不用,这里也不区分大小写,在的column也不区分大小写,看下面的代码: for (ResultMapping compositeResultMapping : resultMapping.getComposites()) { final String compositeColumn = compositeResultMapping.getColumn(); if (compositeColumn != null) { resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH)); }} 这里也转换为了大写。 到这里关于resultTypt和resultMap就结束了,但是有一个简单的问题,很多人不懂,是什么?看下个标题。MyBatis接口返回值 接口返回值通常是一个结果,或者是List和数组。MyBatis如何知道我想要返回一个结果还是多个结果? 在MapperMethod中的部分代码如下: 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 { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param);} 可以看到查询结果有4中情况,void,list(和array),map,one。 这里重要就是if的判断条件,这种判断条件计算方法: this.returnType = method.getReturnType();this.returnsVoid = void.class.equals(this.returnType);this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); 可以看到,这些条件完全就是通过方法的返回值决定的。所以如果你写的返回值是数组或者集合,返回的结果就是多个。 如果返回值本身有多个,但是返回值写了一个POJO,不是集合或者数组时会怎样? 答案是会报错TooManyResultsException(“Expected one result (or null) to be returned by selectOne(), but found: “ + list.size())。 不管是返回一个结果还是多个结果,MyBatis都是安装多个结果进行查询,selectOne是查询一个,selectList是查询多个,我们看看selectOne代码: public T selectOne(String statement, Object parameter) { List 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; }} 注意看: List list = this.selectList(statement, parameter); 实际上,不管查询一个还是多个结果,MyBatis都是先按多个结果进行查询。拿到list结果后在判断。 如果是查询一个结果的情况,那么list最多只能有一个返回值。通过上面代码的if else if esle可以很明白的理解。resultTyp,resultMap和返回值多少有关系吗? 没有任何关系。 通过前面resultType和resultMap的内容,我们应该知道,这个属性是配置JDBC查询结果如何映射到一个对象上的。 不管返回值是什么或者是几个,都是按照resultType和resultMap生成返回结果。 返回结果的类型由resultType和resultMap决定。返回结果的类型 返回结果的类型由resultType和resultMap决定,是不是很诧异??? 实际上就是这种情况。。 举个例子,有个实体Country和Country2。 接口中List selectAll(),xml中的. 当你通过接口调用的时候,返回值是什么?你以为自己的List中的对象类型是Country,但他们实际上都是Country2 如果接口方法为Country selectById(Integer id),xml中为,由于类型不一致,查询的时候才会报错:java.lang.ClassCastException: xx.Country2 cannot be cast to xx.Country为什么会这样呢? 这是因为接口调用方式是对命名空间方式调用的封装。 当你通过命名空间方式调用的时候,返回结果的类型是什么? 就是由resultType和resultMap决定的类型,这很容易理解。但是换成接口就觉得不一样了。 这是由于接口方法方式多了返回值,所以我们会认为返回的一定是这个类型。实际上是错的。特殊情况 当使用纯注解方式时,接口的返回值类型可以起到作用,如果没有使用@ResultType注解指定返回值类型,那么就会使用这里写的返回值类型作为resultType。
相关 Mybatis 返回值类型 MyBatis的返回参数类型分两种 1. 对应的分类为: resultMap: resultType: 2 .对应返回值类型: resultMap:结 亦凉/ 2024年02月18日 16:30/ 0 赞/ 148 阅读
相关 Mybatis的返回值深入 目录 resultType 配置结果类型 基本类型示例 Dao 接口 映射配置 实体类类型示例 约定不等于承诺〃/ 2022年10月29日 05:27/ 0 赞/ 250 阅读
相关 MyBatis —— 返回主键值 目录 1、使用 JDBC 方式返回主键自增的值 2、使用 selectKey 返回主键的值 -------------------- 1、使用 JDBC 方式返回主键 叁歲伎倆/ 2022年10月01日 12:57/ 0 赞/ 231 阅读
相关 myBatis返回integer值 经过 [测试][Link 1] ,将 resultMap="java.lang.Integer" 改成 resultType="java.lang.Integer" ゝ一世哀愁。/ 2022年09月30日 00:57/ 0 赞/ 243 阅读
相关 深入了解MyBatis二级缓存 一、创建Cache的完整过程 我们从SqlSessionFactoryBuilder解析mybatis-config.xml配置文件开始: Reader rea 待我称王封你为后i/ 2022年08月07日 07:46/ 0 赞/ 252 阅读
相关 深入了解MyBatis参数 深入了解MyBatis参数 相信很多人可能都遇到过下面这些异常: `"Parameter 'xxx' not found. Available parameter 古城微笑少年丶/ 2022年08月07日 02:49/ 0 赞/ 229 阅读
相关 深入了解MyBatis返回值 深入了解MyBatis返回值 想了解返回值,我们需要了解`resultType`,`resultMap`以及接口方法中定义的返回值。 我们先看`resultType`和 傷城~/ 2022年08月05日 07:25/ 0 赞/ 234 阅读
相关 深入了解MyBatis二级缓存 一、创建Cache的完整过程 我们从SqlSessionFactoryBuilder解析mybatis-config.xml配置文件开始: Reader rea 淩亂°似流年/ 2022年06月11日 02:45/ 0 赞/ 236 阅读
相关 Mybatis之 foreach深入了解 这篇文章主要对foreach里面的collection相关的内容做一些介绍,围绕foreach做一些数据插入和查询相关的研究 。 foreach属性 <tabl 男娘i/ 2022年05月26日 23:39/ 0 赞/ 307 阅读
相关 深入了解MyBatis返回值 想了解返回值,我们需要了解resultType,resultMap以及接口方法中定义的返回值。 我们先看resultType和resultMap resultType和r 电玩女神/ 2022年04月15日 03:29/ 0 赞/ 300 阅读
还没有评论,来说两句吧...