• Builder模式实例 MyBatis的ResultMapping和ResultMap


    MyBatis的ResultMapping和ResultMap

    CSDN LoveLion
    复杂对象的组装与创建——建造者模式(一)
    Effective java 第3版中描述的Builder模式
    Java设计模式14:建造者模式
    2个类都使用了Builder来构建对象。

    ResultMapping

    package org.apache.ibatis.mapping;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Set;
    
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.TypeHandler;
    import org.apache.ibatis.type.TypeHandlerRegistry;
    
    public class ResultMapping {
    
      private Configuration configuration;
      private String property;
      private String column;
      private Class<?> javaType;
      private JdbcType jdbcType;
      private TypeHandler<?> typeHandler;
      private String nestedResultMapId;
      private String nestedQueryId;
      private Set<String> notNullColumns;
      private String columnPrefix;
      private List<ResultFlag> flags;
      private List<ResultMapping> composites;
      private String resultSet;
      private String foreignColumn;
      private boolean lazy;
    
      ResultMapping() {
      }
    
      public static class Builder {
        private ResultMapping resultMapping = new ResultMapping();
    
        public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
          this(configuration, property);
          resultMapping.column = column;
          resultMapping.typeHandler = typeHandler;
        }
    
        public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
          this(configuration, property);
          resultMapping.column = column;
          resultMapping.javaType = javaType;
        }
    
        public Builder(Configuration configuration, String property) {
          resultMapping.configuration = configuration;
          resultMapping.property = property;
          resultMapping.flags = new ArrayList<>();
          resultMapping.composites = new ArrayList<>();
          resultMapping.lazy = configuration.isLazyLoadingEnabled();
        }
    
        public Builder javaType(Class<?> javaType) {
          resultMapping.javaType = javaType;
          return this;
        }
    
        public Builder jdbcType(JdbcType jdbcType) {
          resultMapping.jdbcType = jdbcType;
          return this;
        }
    
        public Builder nestedResultMapId(String nestedResultMapId) {
          resultMapping.nestedResultMapId = nestedResultMapId;
          return this;
        }
    
        public Builder nestedQueryId(String nestedQueryId) {
          resultMapping.nestedQueryId = nestedQueryId;
          return this;
        }
    
        public Builder resultSet(String resultSet) {
          resultMapping.resultSet = resultSet;
          return this;
        }
    
        public Builder foreignColumn(String foreignColumn) {
          resultMapping.foreignColumn = foreignColumn;
          return this;
        }
    
        public Builder notNullColumns(Set<String> notNullColumns) {
          resultMapping.notNullColumns = notNullColumns;
          return this;
        }
    
        public Builder columnPrefix(String columnPrefix) {
          resultMapping.columnPrefix = columnPrefix;
          return this;
        }
    
        public Builder flags(List<ResultFlag> flags) {
          resultMapping.flags = flags;
          return this;
        }
    
        public Builder typeHandler(TypeHandler<?> typeHandler) {
          resultMapping.typeHandler = typeHandler;
          return this;
        }
    
        public Builder composites(List<ResultMapping> composites) {
          resultMapping.composites = composites;
          return this;
        }
    
        public Builder lazy(boolean lazy) {
          resultMapping.lazy = lazy;
          return this;
        }
    
        public ResultMapping build() {
          // lock down collections
          resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
          resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
          resolveTypeHandler();
          validate();
          return resultMapping;
        }
    
        private void validate() {
          // Issue #697: cannot define both nestedQueryId and nestedResultMapId
          if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
            throw new IllegalStateException("Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
          }
          // Issue #5: there should be no mappings without typehandler
          if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null && resultMapping.typeHandler == null) {
            throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
          }
          // Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest
          if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) {
            throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
          }
          if (resultMapping.getResultSet() != null) {
            int numColumns = 0;
            if (resultMapping.column != null) {
              numColumns = resultMapping.column.split(",").length;
            }
            int numForeignColumns = 0;
            if (resultMapping.foreignColumn != null) {
              numForeignColumns = resultMapping.foreignColumn.split(",").length;
            }
            if (numColumns != numForeignColumns) {
              throw new IllegalStateException("There should be the same number of columns and foreignColumns in property " + resultMapping.property);
            }
          }
        }
    
        private void resolveTypeHandler() {
          if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
            Configuration configuration = resultMapping.configuration;
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
          }
        }
    
        public Builder column(String column) {
          resultMapping.column = column;
          return this;
        }
      }
    
      public String getProperty() {
        return property;
      }
    
      public String getColumn() {
        return column;
      }
    
      public Class<?> getJavaType() {
        return javaType;
      }
    
      public JdbcType getJdbcType() {
        return jdbcType;
      }
    
      public TypeHandler<?> getTypeHandler() {
        return typeHandler;
      }
    
      public String getNestedResultMapId() {
        return nestedResultMapId;
      }
    
      public String getNestedQueryId() {
        return nestedQueryId;
      }
    
      public Set<String> getNotNullColumns() {
        return notNullColumns;
      }
    
      public String getColumnPrefix() {
        return columnPrefix;
      }
    
      public List<ResultFlag> getFlags() {
        return flags;
      }
    
      public List<ResultMapping> getComposites() {
        return composites;
      }
    
      public boolean isCompositeResult() {
        return this.composites != null && !this.composites.isEmpty();
      }
    
      public String getResultSet() {
        return this.resultSet;
      }
    
      public String getForeignColumn() {
        return foreignColumn;
      }
    
      public void setForeignColumn(String foreignColumn) {
        this.foreignColumn = foreignColumn;
      }
    
      public boolean isLazy() {
        return lazy;
      }
    
      public void setLazy(boolean lazy) {
        this.lazy = lazy;
      }
    
      @Override
      public boolean equals(Object o) {
        if (this == o) {
          return true;
        }
        if (o == null || getClass() != o.getClass()) {
          return false;
        }
    
        ResultMapping that = (ResultMapping) o;
    
        return property != null && property.equals(that.property);
      }
    
      @Override
      public int hashCode() {
        if (property != null) {
          return property.hashCode();
        } else if (column != null) {
          return column.hashCode();
        } else {
          return 0;
        }
      }
    }
    

    ResultMap

    package org.apache.ibatis.mapping;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Constructor;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Locale;
    import java.util.Set;
    
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.builder.BuilderException;
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.logging.LogFactory;
    import org.apache.ibatis.reflection.ParamNameUtil;
    import org.apache.ibatis.session.Configuration;
    
    public class ResultMap {
      private Configuration configuration;
    
      private String id;
      private Class<?> type;
      private List<ResultMapping> resultMappings;
      private List<ResultMapping> idResultMappings;
      private List<ResultMapping> constructorResultMappings;
      private List<ResultMapping> propertyResultMappings;
      private Set<String> mappedColumns;
      private Set<String> mappedProperties;
      private Discriminator discriminator;
      private boolean hasNestedResultMaps;
      private boolean hasNestedQueries;
      private Boolean autoMapping;
    
      private ResultMap() {
      }
    
      public static class Builder {
        private static final Log log = LogFactory.getLog(Builder.class);
    
        private ResultMap resultMap = new ResultMap();
    
        public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
          this(configuration, id, type, resultMappings, null);
        }
    
        public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings, Boolean autoMapping) {
          resultMap.configuration = configuration;
          resultMap.id = id;
          resultMap.type = type;
          resultMap.resultMappings = resultMappings;
          resultMap.autoMapping = autoMapping;
        }
    
        public Builder discriminator(Discriminator discriminator) {
          resultMap.discriminator = discriminator;
          return this;
        }
    
        public Class<?> type() {
          return resultMap.type;
        }
    
        public ResultMap build() {
          if (resultMap.id == null) {
            throw new IllegalArgumentException("ResultMaps must have an id");
          }
          resultMap.mappedColumns = new HashSet<>();
          resultMap.mappedProperties = new HashSet<>();
          resultMap.idResultMappings = new ArrayList<>();
          resultMap.constructorResultMappings = new ArrayList<>();
          resultMap.propertyResultMappings = new ArrayList<>();
          final List<String> constructorArgNames = new ArrayList<>();
          for (ResultMapping resultMapping : resultMap.resultMappings) {
            resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
            resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
            final String column = resultMapping.getColumn();
            if (column != null) {
              resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
            } else if (resultMapping.isCompositeResult()) {
              for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
                final String compositeColumn = compositeResultMapping.getColumn();
                if (compositeColumn != null) {
                  resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
                }
              }
            }
            final String property = resultMapping.getProperty();
            if (property != null) {
              resultMap.mappedProperties.add(property);
            }
            if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
              resultMap.constructorResultMappings.add(resultMapping);
              if (resultMapping.getProperty() != null) {
                constructorArgNames.add(resultMapping.getProperty());
              }
            } else {
              resultMap.propertyResultMappings.add(resultMapping);
            }
            if (resultMapping.getFlags().contains(ResultFlag.ID)) {
              resultMap.idResultMappings.add(resultMapping);
            }
          }
          if (resultMap.idResultMappings.isEmpty()) {
            resultMap.idResultMappings.addAll(resultMap.resultMappings);
          }
          if (!constructorArgNames.isEmpty()) {
            final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
            if (actualArgNames == null) {
              throw new BuilderException("Error in result map '" + resultMap.id
                  + "'. Failed to find a constructor in '"
                  + resultMap.getType().getName() + "' by arg names " + constructorArgNames
                  + ". There might be more info in debug log.");
            }
            resultMap.constructorResultMappings.sort((o1, o2) -> {
              int paramIdx1 = actualArgNames.indexOf(o1.getProperty());
              int paramIdx2 = actualArgNames.indexOf(o2.getProperty());
              return paramIdx1 - paramIdx2;
            });
          }
          // lock down collections
          resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
          resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
          resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
          resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
          resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
          return resultMap;
        }
    
        private List<String> argNamesOfMatchingConstructor(List<String> constructorArgNames) {
          Constructor<?>[] constructors = resultMap.type.getDeclaredConstructors();
          for (Constructor<?> constructor : constructors) {
            Class<?>[] paramTypes = constructor.getParameterTypes();
            if (constructorArgNames.size() == paramTypes.length) {
              List<String> paramNames = getArgNames(constructor);
              if (constructorArgNames.containsAll(paramNames)
                  && argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
                return paramNames;
              }
            }
          }
          return null;
        }
    
        private boolean argTypesMatch(final List<String> constructorArgNames,
            Class<?>[] paramTypes, List<String> paramNames) {
          for (int i = 0; i < constructorArgNames.size(); i++) {
            Class<?> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
            Class<?> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
            if (!actualType.equals(specifiedType)) {
              if (log.isDebugEnabled()) {
                log.debug("While building result map '" + resultMap.id
                    + "', found a constructor with arg names " + constructorArgNames
                    + ", but the type of '" + constructorArgNames.get(i)
                    + "' did not match. Specified: [" + specifiedType.getName() + "] Declared: ["
                    + actualType.getName() + "]");
              }
              return false;
            }
          }
          return true;
        }
    
        private List<String> getArgNames(Constructor<?> constructor) {
          List<String> paramNames = new ArrayList<>();
          List<String> actualParamNames = null;
          final Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
          int paramCount = paramAnnotations.length;
          for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
            String name = null;
            for (Annotation annotation : paramAnnotations[paramIndex]) {
              if (annotation instanceof Param) {
                name = ((Param) annotation).value();
                break;
              }
            }
            if (name == null && resultMap.configuration.isUseActualParamName()) {
              if (actualParamNames == null) {
                actualParamNames = ParamNameUtil.getParamNames(constructor);
              }
              if (actualParamNames.size() > paramIndex) {
                name = actualParamNames.get(paramIndex);
              }
            }
            paramNames.add(name != null ? name : "arg" + paramIndex);
          }
          return paramNames;
        }
      }
    
      public String getId() {
        return id;
      }
    
      public boolean hasNestedResultMaps() {
        return hasNestedResultMaps;
      }
    
      public boolean hasNestedQueries() {
        return hasNestedQueries;
      }
    
      public Class<?> getType() {
        return type;
      }
    
      public List<ResultMapping> getResultMappings() {
        return resultMappings;
      }
    
      public List<ResultMapping> getConstructorResultMappings() {
        return constructorResultMappings;
      }
    
      public List<ResultMapping> getPropertyResultMappings() {
        return propertyResultMappings;
      }
    
      public List<ResultMapping> getIdResultMappings() {
        return idResultMappings;
      }
    
      public Set<String> getMappedColumns() {
        return mappedColumns;
      }
    
      public Set<String> getMappedProperties() {
        return mappedProperties;
      }
    
      public Discriminator getDiscriminator() {
        return discriminator;
      }
    
      public void forceNestedResultMaps() {
        hasNestedResultMaps = true;
      }
    
      public Boolean getAutoMapping() {
        return autoMapping;
      }
    
    }
    
  • 相关阅读:
    仿QQ消息弹出框
    WPF,Silverlight与XAML读书笔记第十六 资源之二进制资源
    WPF,Silverlight与XAML读书笔记第十七 资源之逻辑资源
    WPF,Silverlight与XAML读书笔记第十 WPF框架与工作方式介绍
    WPF,Silverlight与XAML读书笔记第五 XAML与传统过程式代码的关系
    再谈ASP.NET第七 跨应用、跨服务器的表单验证
    WPF,Silverlight与XAML读书笔记第六 WPF新概念之一逻辑树与可视树
    WPF,Silverlight与XAML读书笔记第八 WPF新概念之三路由事件
    WPF,Silverlight与XAML读书笔记第十四 独立存储&部署
    WPF,Silverlight与XAML读书笔记第七 WPF新概念之二依赖属性
  • 原文地址:https://www.cnblogs.com/mozq/p/12079746.html
Copyright © 2020-2023  润新知