一.结果映射介绍
在映射文件中,Mybatis可以根据配置,自动将查询结果封装成对象。常见的结果配置有:
1.通过 resultType 映射
<select id="selectFromAuthor" parameterType="java.lang.String" resultType="org.apache.ibatis.domain.blog.Author"> select * from Author where id = #{id} and username = #{username} </select>
2.通过 resultMap 映射
<resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author"> <id column="id" property="id" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="email" column="email" /> <result property="bio" column="bio" /> <result property="favouriteSection" column="favourite_section" /> </resultMap>
<select id="selectAuthor" parameterMap="selectAuthor" resultMap="selectAuthor"> select * from Author where id = #{id} and username = #{username} </select>
二.结果映射标签解析过程
在映射文件加载过程中,上述两种方式结果映射会被解析成什么样呢?简单回顾一下
首先回顾一下resultMap标签解析过程
XMLMapperBuilder: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); // 解析 <cache-ref> 节点 //在 MyBatis 中,二级缓存是可以共用的。这需要通过<cache-ref>节点为命名空间配置参 //照缓存,引用其他命名空间的缓存,在一个mapper.xml文件中,cache 和 cache-ref一般只会存在一个 //如果都存在,看具体解析的顺序 cacheRefElement(context.evalNode("cache-ref")); //解析<cache> 节点,二级缓存,在解析 select|insert|update|delete 会将二级缓存设置进去 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); } }private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); //解析type属性 String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); Class<?> typeClass = resolveClass(type); if (typeClass == null) { typeClass = inheritEnclosingType(resultMapNode, enclosingType); } Discriminator discriminator = null; List<ResultMapping> resultMappings = new ArrayList<>(); resultMappings.addAll(additionalResultMappings); List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) {//解析子标签<result> </result>,<id> if ("constructor".equals(resultChild.getName())) { processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { //discriminator 根据数据库返回结果值,决定引用 哪个resultMap discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { List<ResultFlag> flags = new ArrayList<>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } //解析id和type, 创建ResultMapping,获取<result> </result>标签中的所有属性 resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } //解析id属性 String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); String extend = resultMapNode.getStringAttribute("extends"); Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { // 根据前面获取到的信息构建 ResultMap 对象, id --> ResultMap return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } }ResultMapResolver:public ResultMap resolve() { return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping); }MapperBuilderAssistant:public ResultMap addResultMap( String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) { id = applyCurrentNamespace(id, false); extend = applyCurrentNamespace(extend, true); 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<>(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) { extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)); } resultMappings.addAll(extendedResultMappings); } ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping) .discriminator(discriminator) .build(); configuration.addResultMap(resultMap); return resultMap; }
最终,ResultMap会被解析成ResultMap对象,简单的看一下这个对象的一下属性:
private Configuration configuration; /** * <resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author"> */ private String id; private Class<?> type; /** * <result></result> * <result></result> * <result></result> */ private List<ResultMapping> resultMappings; private List<ResultMapping> idResultMappings; private List<ResultMapping> constructorResultMappings; private List<ResultMapping> propertyResultMappings; /** * column 集合 */ private Set<String> mappedColumns; /** * property 集合 */ private Set<String> mappedProperties; private Discriminator discriminator; private boolean hasNestedResultMaps; private boolean hasNestedQueries; private Boolean autoMapping;
接下来看看 resultType 这种方式,因为这个属性属于 <select>标签中的,所有解析过程在 解析<select>标签过程中
XMLMapperBuilder:private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { //开始解析 statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }XMLStatementBuilder:public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing,解析include标签 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); // Parse selectKey after includes and remove them. //解析<selectKey>标签 // <selectKey keyProperty="id" resultType="int" order="BEFORE"> // select author_seq.nextval from dual // </selectKey> processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } //解析sql语句 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); // resultType属性,最终解析成 ResultMap 对象 String resultType = context.getStringAttribute("resultType"); //通过别名解析 resultType 对应的类型 Class<?> resultTypeClass = resolveClass(resultType); String resultMap = context.getStringAttribute("resultMap"); String resultSetType = context.getStringAttribute("resultSetType"); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); if (resultSetTypeEnum == null) { resultSetTypeEnum = configuration.getDefaultResultSetType(); } String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); String resultSets = context.getStringAttribute("resultSets"); //创建 MappedStatement,包含了sql的所有信息,如sql语句,入参,结果映射,缓存, 保存到configuration中, builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }MapperBuilderAssistant:public MappedStatement addMappedStatement( String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) { if (unresolvedCacheRef) {//抛异常,后续会继续创建MappedStatement throw new IncompleteElementException("Cache-ref not yet resolved"); } id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id))//处理结果映射 .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache);//设置二级缓存 ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); return statement; }private List<ResultMap> getStatementResultMaps( String resultMap, Class<?> resultType, String statementId) { resultMap = applyCurrentNamespace(resultMap, true); List<ResultMap> resultMaps = new ArrayList<>(); if (resultMap != null) {//处理<resultMap> 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 + "' referenced from '" + statementId + "'", e); } } } else if (resultType != null) {//处理 resultType 属性 ResultMap inlineResultMap = new ResultMap.Builder( configuration, statementId + "-Inline", resultType, new ArrayList<>(), null).build(); resultMaps.add(inlineResultMap); } return resultMaps; }
可以看到,resultType最终也会被封装成 ResultMap对象,这样方便了之后对结果集的处理。
三.处理结果集
在执行完sql后,Mybatis根据配置将结果映射到相应的对象中。
基本思路:
1.封装结果集中的信息,如收集列名集合,列个数等
2.获取对应的ResultMap对象,根据配置获取结果集中的结果,并设置到最终对象中
PreparedStatementHandler:@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; //执行sql ps.execute(); //处理结果 return resultSetHandler.handleResultSets(ps); }DefaultResultSetHandler:@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; //获取第一个结果集 ResultSetWrapper rsw = getFirstResultSet(stmt); //获取配置的 <ResultMap>结果映射标签 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); //处理结果集 handleResultSet(rsw, resultMap, multipleResults, null); //获取下一个结果 rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults); }private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException { //获取结果集 ResultSet rs = stmt.getResultSet(); while (rs == null) { /* * 移动 ResultSet 指针到下一个上,有些数据库驱动可能需要使用者 * 先调用 getMoreResults 方法,然后才能调用 getResultSet 方法 * 获取到第一个 ResultSet */ // move forward to get the first resultset in case the driver // doesn't return the resultset as the first result (HSQLDB 2.1) if (stmt.getMoreResults()) { rs = stmt.getResultSet(); } else { if (stmt.getUpdateCount() == -1) { // no more results. Must be no resultset break; } } } //对结果集信息进行包装,如列名集合 return rs != null ? new ResultSetWrapper(rs, configuration) : null; }ResultSetWrapper:public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException { super(); this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.resultSet = rs; //获取结果集的 元数据,如类型,个数 final ResultSetMetaData metaData = rs.getMetaData(); final int columnCount = metaData.getColumnCount(); for (int i = 1; i <= columnCount; i++) { //数据库返回的字段名集合 columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i)); jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i))); //数据库返回的字段类型集合 classNames.add(metaData.getColumnClassName(i)); } }
结果集有了,ResultMap也有了,接下来就是根据ResultMap中的信息 在结果集中查找结果,关注handleResultSet(rsw, resultMap, multipleResults, null)
DefaultResultSetHandler:private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else { if (resultHandler == null) { DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); //处理结果集 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); multipleResults.add(defaultResultHandler.getResultList()); } else { handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); } } } finally { // issue #228 (close resultsets) closeResultSet(rsw.getResultSet()); } }public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { if (resultMap.hasNestedResultMaps()) { //处理嵌套映射,嵌套查询是指<ResultMap>中嵌套了一个<ResultMap> ensureNoRowBounds(); checkResultHandler(); handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } else { //处理简单映射 handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } }private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<>(); ResultSet resultSet = rsw.getResultSet(); // 根据 RowBounds 定位到指定行记录,可以根据RowBounds进行逻辑分页(一般不会这样做) skipRows(resultSet, rowBounds); // 检测是否还有更多行的数据需要处理,可以根据RowBounds进行逻辑分页(一般不会这样做) while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) { // 获取经过鉴别器处理后的 ResultMap ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null); //rowValue 最终封装的对象结果 Object rowValue = getRowValue(rsw, discriminatedResultMap, null); //保存结果 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet); } }简单看一下 skipRows(resultSet, rowBounds)
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException { // 检测 rs 的类型,不同的类型行数据定位方式是不同的 if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) { rs.absolute(rowBounds.getOffset()); } } else { for (int i = 0; i < rowBounds.getOffset(); i++) { if (!rs.next()) { break; } } } }private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); //创建实体类对象 Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { //对于简单类型,如int等有 typeHander处理器,则不会进入此处,rowValue已是最终结果 final MetaObject metaObject = configuration.newMetaObject(rowValue); boolean foundValues = this.useConstructorMappings; // 检测是否应该自动映射结果集 if (shouldApplyAutomaticMappings(resultMap, false)) { //自动映射,未在<resultMap>中配置的属性,如hashMap,自动映射返回对象(只配置resultType) foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } // 根据 <resultMap> 节点中配置的映射关系进行映射 foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } return rowValue; }
getRowValue中,先创建返回对象,然后再进行结果映射。对应一下简单的返回类型,如 String,Integer等已经注册过TypeHandler,在创建返回对象时值已经设置到对象中,无需进行结果映射。
DefaultResultSetHandler:private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { this.useConstructorMappings = false; // reset previous mapping result final List<Class<?>> constructorArgTypes = new ArrayList<>(); final List<Object> constructorArgs = new ArrayList<>(); // 调用重载方法创建实体类对象 Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix); if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { // issue gcode #109 && issue #149 if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { //延迟加载,返回代理对象 resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); break; } } } this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result return resultObject; }private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException { final Class<?> resultType = resultMap.getType(); final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory); // 获取 <constructor> 节点对应的 ResultMapping final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings(); // 检测是否有与返回值类型相对应的 TypeHandler,若有则直接从 // 通过 TypeHandler 从结果集中提取数据,并生成返回值对象(后续无需再填充值) if (hasTypeHandlerForResultObject(rsw, resultType)) { // 通过 TypeHandler 获取取,并生成返回值对象 return createPrimitiveResultObject(rsw, resultMap, columnPrefix); } else if (!constructorMappings.isEmpty()) { // 通过 <constructor> 节点配置的映射信息从 ResultSet 中ᨀ取数据, // 然后将这些数据传给指定构造方法,即可创建实体类对象 return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix); } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { // 通过 ObjectFactory 调用目标类的默认构造方法创建实例 return objectFactory.create(resultType); } else if (shouldApplyAutomaticMappings(resultMap, false)) {//检查是否自动映射 // 通过自动映射查找合适的构造方法创建实例 return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs); } throw new ExecutorException("Do not know how to create an instance of " + resultType); }
Mybatis默认注册了很多TypeHandler,TypeHandler 接口用来设置入参,获取结果集
public interface TypeHandler<T> { void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; /** * @param columnName Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code> */ T getResult(ResultSet rs, String columnName) throws SQLException; T getResult(ResultSet rs, int columnIndex) throws SQLException; T getResult(CallableStatement cs, int columnIndex) throws SQLException; }public TypeHandlerRegistry() { register(Boolean.class, new BooleanTypeHandler()); register(boolean.class, new BooleanTypeHandler()); register(JdbcType.BOOLEAN, new BooleanTypeHandler()); register(JdbcType.BIT, new BooleanTypeHandler()); register(Byte.class, new ByteTypeHandler()); register(byte.class, new ByteTypeHandler()); register(JdbcType.TINYINT, new ByteTypeHandler()); register(Short.class, new ShortTypeHandler()); register(short.class, new ShortTypeHandler()); register(JdbcType.SMALLINT, new ShortTypeHandler()); register(Integer.class, new IntegerTypeHandler()); register(int.class, new IntegerTypeHandler()); register(JdbcType.INTEGER, new IntegerTypeHandler()); register(Long.class, new LongTypeHandler()); register(long.class, new LongTypeHandler()); register(Float.class, new FloatTypeHandler()); register(float.class, new FloatTypeHandler()); register(JdbcType.FLOAT, new FloatTypeHandler()); register(Double.class, new DoubleTypeHandler()); register(double.class, new DoubleTypeHandler()); register(JdbcType.DOUBLE, new DoubleTypeHandler()); register(Reader.class, new ClobReaderTypeHandler()); register(String.class, new StringTypeHandler()); register(String.class, JdbcType.CHAR, new StringTypeHandler()); register(String.class, JdbcType.CLOB, new ClobTypeHandler()); register(String.class, JdbcType.VARCHAR, new StringTypeHandler()); register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler()); register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler()); register(String.class, JdbcType.NCHAR, new NStringTypeHandler()); register(String.class, JdbcType.NCLOB, new NClobTypeHandler()); register(JdbcType.CHAR, new StringTypeHandler()); register(JdbcType.VARCHAR, new StringTypeHandler()); register(JdbcType.CLOB, new ClobTypeHandler()); register(JdbcType.LONGVARCHAR, new StringTypeHandler()); register(JdbcType.NVARCHAR, new NStringTypeHandler()); register(JdbcType.NCHAR, new NStringTypeHandler()); register(JdbcType.NCLOB, new NClobTypeHandler()); register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); register(JdbcType.ARRAY, new ArrayTypeHandler()); register(BigInteger.class, new BigIntegerTypeHandler()); register(JdbcType.BIGINT, new LongTypeHandler()); register(BigDecimal.class, new BigDecimalTypeHandler()); register(JdbcType.REAL, new BigDecimalTypeHandler()); register(JdbcType.DECIMAL, new BigDecimalTypeHandler()); register(JdbcType.NUMERIC, new BigDecimalTypeHandler()); register(InputStream.class, new BlobInputStreamTypeHandler()); register(Byte[].class, new ByteObjectArrayTypeHandler()); register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler()); register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler()); register(byte[].class, new ByteArrayTypeHandler()); register(byte[].class, JdbcType.BLOB, new BlobTypeHandler()); register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); register(JdbcType.LONGVARBINARY, new BlobTypeHandler()); register(JdbcType.BLOB, new BlobTypeHandler()); register(Object.class, unknownTypeHandler); register(Object.class, JdbcType.OTHER, unknownTypeHandler); register(JdbcType.OTHER, unknownTypeHandler); register(Date.class, new DateTypeHandler()); register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler()); register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler()); register(JdbcType.TIMESTAMP, new DateTypeHandler()); register(JdbcType.DATE, new DateOnlyTypeHandler()); register(JdbcType.TIME, new TimeOnlyTypeHandler()); register(java.sql.Date.class, new SqlDateTypeHandler()); register(java.sql.Time.class, new SqlTimeTypeHandler()); register(java.sql.Timestamp.class, new SqlTimestampTypeHandler()); register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); register(Instant.class, new InstantTypeHandler()); register(LocalDateTime.class, new LocalDateTimeTypeHandler()); register(LocalDate.class, new LocalDateTypeHandler()); register(LocalTime.class, new LocalTimeTypeHandler()); register(OffsetDateTime.class, new OffsetDateTimeTypeHandler()); register(OffsetTime.class, new OffsetTimeTypeHandler()); register(ZonedDateTime.class, new ZonedDateTimeTypeHandler()); register(Month.class, new MonthTypeHandler()); register(Year.class, new YearTypeHandler()); register(YearMonth.class, new YearMonthTypeHandler()); register(JapaneseDate.class, new JapaneseDateTypeHandler()); // issue #273 register(Character.class, new CharacterTypeHandler()); register(char.class, new CharacterTypeHandler()); }
可以看到大部分常见的类型,都默认注册进去了,接下来看看 TypeHandler如何填充结果集
DefaultResultSetHandler:private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final Class<?> resultType = resultMap.getType(); final String columnName; if (!resultMap.getResultMappings().isEmpty()) { final List<ResultMapping> resultMappingList = resultMap.getResultMappings(); final ResultMapping mapping = resultMappingList.get(0); columnName = prependPrefix(mapping.getColumn(), columnPrefix); } else { columnName = rsw.getColumnNames().get(0); } //获取 resultType 对应的 类型处理器 final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName); //获取 columnName 对应的 值 return typeHandler.getResult(rsw.getResultSet(), columnName); }BaseTypeHandler:public T getResult(ResultSet rs, String columnName) throws SQLException { try { //模板方法,子类具体实现,以String为例 return getNullableResult(rs, columnName); } catch (Exception e) { throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); } }StringTypeHandler:public String getNullableResult(ResultSet rs, String columnName) throws SQLException { //获取结果集中的结果 return rs.getString(columnName); }
对于没有注册TypeHandler 的返回类型,如自定义bean对象,则需要通过反射创建对象 objectFactory.create(resultType);
ExampleObjectFactory:@Override public <T> T create(Class<T> type) { return super.<T> create(type); }DefaultObjectFactory:public <T> T create(Class<T> type) { return create(type, null, null); }@Override public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { Class<?> classToCreate = resolveInterface(type); // we know types are assignable return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); }private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null) { //默认的无参构造函数 constructor = type.getDeclaredConstructor(); try { //反射创建实例 return constructor.newInstance(); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { constructor.setAccessible(true); return constructor.newInstance(); } else { throw e; } } } //有参构造函数 constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); try { return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (IllegalAccessException e) { if (Reflector.canControlMemberAccessible()) { constructor.setAccessible(true); return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } else { throw e; } } } catch (Exception e) { String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList) .stream().map(Class::getSimpleName).collect(Collectors.joining(",")); String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList) .stream().map(String::valueOf).collect(Collectors.joining(",")); throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); } }
创建好了返回实体对象,可以根据结果集和ResultMap进行映射了
DefaultResultSetHandler:private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); //创建实体类对象 Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { //对于简单类型,如int等有 typeHander处理器,则不会进入此处,rowValue已是最终结果 final MetaObject metaObject = configuration.newMetaObject(rowValue); boolean foundValues = this.useConstructorMappings; // 检测是否应该自动映射结果集 if (shouldApplyAutomaticMappings(resultMap, false)) { //自动映射,未在<resultMap>中配置的属性,如hashMap,自动映射返回对象(只配置resultType) foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } // 根据 <resultMap> 节点中配置的映射关系进行映射 foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } return rowValue; }
什么是自动映射,例如:
<select id="selectFromAuthor" parameterType="java.lang.String" resultType="org.apache.ibatis.domain.blog.Author"> select * from Author where id = #{id} and username = #{username} </select>
返回类型并没有使用 resultMap,且 resultType的类型 并没有特殊的TypeHandler处理,这个时候就需要自动映射了。Mybatis默认开启自动映射
DefaultResultSetHandler:
/** * 在 MyBatis 中,结果集自动映射有三种等级。这三种等级官方文档上有所说明,这里直 * 接引用一下。如下: * NONE - 禁用自动映射。仅设置手动映射属性 * PARTIAL - 将自动映射结果除了那些有内部定义内嵌结果映射的(joins) * FULL - 自动映射所有 * 除了以上三种等级,我们还可以显示配置<resultMap>节点的 autoMapping 属性,以启用 * 或者禁用指定 ResultMap 的自动映射设定。 * @param resultMap * @param isNested * @return */ private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) { if (resultMap.getAutoMapping() != null) {//检查是否设置自动映射属性 return resultMap.getAutoMapping(); } else { // 对于嵌套 resultMap,仅当全局的映射行为为 FULL 时,才进行自动映射 if (isNested) { return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior(); } else { // 对于普通的 resultMap,只要全局的映射行为不为 NONE,即可进行自动映射 return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior(); } } }private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { // 获取 UnMappedColumnAutoMapping 列表,即需要自动映射的自动信息 List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (!autoMapping.isEmpty()) { for (UnMappedColumnAutoMapping mapping : autoMapping) { // 根据column获取结果集中的值 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { foundValues = true; } if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) { // gcode issue #377, call setter on nulls (value is not 'found') //设置返回对象中的属性值 metaObject.setValue(mapping.property, value); } } } return foundValues; }private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { final String mapKey = resultMap.getId() + ":" + columnPrefix; List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey); //未命中缓存 if (autoMapping == null) { autoMapping = new ArrayList<>(); // 从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名 final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix); for (String columnName : unmappedColumnNames) { String propertyName = columnName; if (columnPrefix != null && !columnPrefix.isEmpty()) { // When columnPrefix is specified, // ignore columns without the prefix. if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) { propertyName = columnName.substring(columnPrefix.length()); } else { continue; } } // 将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase()); if (property != null && metaObject.hasSetter(property)) { if (resultMap.getMappedProperties().contains(property)) { // 检测当前属性是否存在于 resultMap 中 continue; } final Class<?> propertyType = metaObject.getSetterType(property); if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) { //获取类型处理器 final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName); //封装到UnMappedColumnAutoMapping autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive())); } else { configuration.getAutoMappingUnknownColumnBehavior() .doAction(mappedStatement, columnName, property, propertyType); } } else { // 若 property 为空,或实体类中无 property 属性,此时无法完成 // 列名与实体类属性建立映射关系。针对这种情况,有三种处理方式, // 1. 什么都不做 // 2. 仅打印日志 // 3. 抛出异常 // 默认情况下,是什么都不做 configuration.getAutoMappingUnknownColumnBehavior() .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null); } } //写入缓存 autoMappingsCache.put(mapKey, autoMapping); } return autoMapping; }ResultSetWrapper:public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException { List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix)); if (unMappedColumnNames == null) { // 加载已映射与未映射列名 loadMappedAndUnmappedColumnNames(resultMap, columnPrefix); unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix)); } return unMappedColumnNames; }private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException { List<String> mappedColumnNames = new ArrayList<>(); List<String> unmappedColumnNames = new ArrayList<>(); final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH); final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix); for (String columnName : columnNames) {//columnNames是结果集 final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH); if (mappedColumns.contains(upperColumnName)) {//已有映射关系,即在<resultMap>中有配置 mappedColumnNames.add(upperColumnName); } else {//没有映射关系 unmappedColumnNames.add(columnName); } } mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames); unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames); }
自动映射结束,接下来就是<resultMap>相关映射了,过程和自动映射基本相似
DefaultResultSetHandler:private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { //获取结果集中的 映射名称集合 final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix); boolean foundValues = false; final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) {//循环填充对象属性 String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); if (propertyMapping.getNestedResultMapId() != null) { // the user added a column attribute to a nested result map, ignore it column = null; } if (propertyMapping.isCompositeResult() || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) || propertyMapping.getResultSet() != null) { //获取resultMap 中 <result> column对应在 结果集中的值 Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix); // issue #541 make property optional,获取 封装对象中的 属性名称 final String property = propertyMapping.getProperty(); if (property == null) { continue; } else if (value == DEFERRED) { foundValues = true; continue; } if (value != null) { foundValues = true; } if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) { // gcode issue #377, call setter on nulls (value is not 'found') //将 value设置到 property 上 metaObject.setValue(property, value); } } } return foundValues; }
四.总结
本节结果映射只是分析了最简单的结果映射,对于更高级的嵌套结果映射并没有进行分析,大家自行分析吧。结果映射流程整体还是清晰的:包装结果集,创建返回对象,填充返回对象属性。
下一章准备分析Mybatis的数据源,一起期待吧!
结语:人生得意须尽欢,莫使金樽空对月