ResultMapping.java

  1. /*
  2.  *    Copyright 2009-2024 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.mapping;

  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.List;
  20. import java.util.Set;

  21. import org.apache.ibatis.session.Configuration;
  22. import org.apache.ibatis.type.JdbcType;
  23. import org.apache.ibatis.type.TypeHandler;
  24. import org.apache.ibatis.type.TypeHandlerRegistry;

  25. /**
  26.  * @author Clinton Begin
  27.  */
  28. public class ResultMapping {

  29.   private Configuration configuration;
  30.   private String property;
  31.   private String column;
  32.   private Class<?> javaType;
  33.   private JdbcType jdbcType;
  34.   private TypeHandler<?> typeHandler;
  35.   private String nestedResultMapId;
  36.   private String nestedQueryId;
  37.   private Set<String> notNullColumns;
  38.   private String columnPrefix;
  39.   private List<ResultFlag> flags;
  40.   private List<ResultMapping> composites;
  41.   private String resultSet;
  42.   private String foreignColumn;
  43.   private boolean lazy;

  44.   ResultMapping() {
  45.   }

  46.   public static class Builder {
  47.     private final ResultMapping resultMapping = new ResultMapping();

  48.     public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
  49.       this(configuration, property);
  50.       resultMapping.column = column;
  51.       resultMapping.typeHandler = typeHandler;
  52.     }

  53.     public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
  54.       this(configuration, property);
  55.       resultMapping.column = column;
  56.       resultMapping.javaType = javaType;
  57.     }

  58.     public Builder(Configuration configuration, String property) {
  59.       resultMapping.configuration = configuration;
  60.       resultMapping.property = property;
  61.       resultMapping.flags = new ArrayList<>();
  62.       resultMapping.composites = new ArrayList<>();
  63.       resultMapping.lazy = configuration.isLazyLoadingEnabled();
  64.     }

  65.     public Builder javaType(Class<?> javaType) {
  66.       resultMapping.javaType = javaType;
  67.       return this;
  68.     }

  69.     public Builder jdbcType(JdbcType jdbcType) {
  70.       resultMapping.jdbcType = jdbcType;
  71.       return this;
  72.     }

  73.     public Builder nestedResultMapId(String nestedResultMapId) {
  74.       resultMapping.nestedResultMapId = nestedResultMapId;
  75.       return this;
  76.     }

  77.     public Builder nestedQueryId(String nestedQueryId) {
  78.       resultMapping.nestedQueryId = nestedQueryId;
  79.       return this;
  80.     }

  81.     public Builder resultSet(String resultSet) {
  82.       resultMapping.resultSet = resultSet;
  83.       return this;
  84.     }

  85.     public Builder foreignColumn(String foreignColumn) {
  86.       resultMapping.foreignColumn = foreignColumn;
  87.       return this;
  88.     }

  89.     public Builder notNullColumns(Set<String> notNullColumns) {
  90.       resultMapping.notNullColumns = notNullColumns;
  91.       return this;
  92.     }

  93.     public Builder columnPrefix(String columnPrefix) {
  94.       resultMapping.columnPrefix = columnPrefix;
  95.       return this;
  96.     }

  97.     public Builder flags(List<ResultFlag> flags) {
  98.       resultMapping.flags = flags;
  99.       return this;
  100.     }

  101.     public Builder typeHandler(TypeHandler<?> typeHandler) {
  102.       resultMapping.typeHandler = typeHandler;
  103.       return this;
  104.     }

  105.     public Builder composites(List<ResultMapping> composites) {
  106.       resultMapping.composites = composites;
  107.       return this;
  108.     }

  109.     public Builder lazy(boolean lazy) {
  110.       resultMapping.lazy = lazy;
  111.       return this;
  112.     }

  113.     public ResultMapping build() {
  114.       // lock down collections
  115.       resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
  116.       resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
  117.       resolveTypeHandler();
  118.       validate();
  119.       return resultMapping;
  120.     }

  121.     private void validate() {
  122.       // Issue #697: cannot define both nestedQueryId and nestedResultMapId
  123.       if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
  124.         throw new IllegalStateException(
  125.             "Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
  126.       }
  127.       // Issue #5: there should be no mappings without typehandler
  128.       if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null
  129.           && resultMapping.typeHandler == null) {
  130.         throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
  131.       }
  132.       // Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest
  133.       if (resultMapping.nestedResultMapId == null && resultMapping.column == null
  134.           && resultMapping.composites.isEmpty()) {
  135.         throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
  136.       }
  137.       if (resultMapping.getResultSet() != null) {
  138.         int numColumns = 0;
  139.         if (resultMapping.column != null) {
  140.           numColumns = resultMapping.column.split(",").length;
  141.         }
  142.         int numForeignColumns = 0;
  143.         if (resultMapping.foreignColumn != null) {
  144.           numForeignColumns = resultMapping.foreignColumn.split(",").length;
  145.         }
  146.         if (numColumns != numForeignColumns) {
  147.           throw new IllegalStateException(
  148.               "There should be the same number of columns and foreignColumns in property " + resultMapping.property);
  149.         }
  150.       }
  151.     }

  152.     private void resolveTypeHandler() {
  153.       if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
  154.         Configuration configuration = resultMapping.configuration;
  155.         TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  156.         resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
  157.       }
  158.     }

  159.     public Builder column(String column) {
  160.       resultMapping.column = column;
  161.       return this;
  162.     }
  163.   }

  164.   public String getProperty() {
  165.     return property;
  166.   }

  167.   public String getColumn() {
  168.     return column;
  169.   }

  170.   public Class<?> getJavaType() {
  171.     return javaType;
  172.   }

  173.   public JdbcType getJdbcType() {
  174.     return jdbcType;
  175.   }

  176.   public TypeHandler<?> getTypeHandler() {
  177.     return typeHandler;
  178.   }

  179.   public String getNestedResultMapId() {
  180.     return nestedResultMapId;
  181.   }

  182.   public String getNestedQueryId() {
  183.     return nestedQueryId;
  184.   }

  185.   public Set<String> getNotNullColumns() {
  186.     return notNullColumns;
  187.   }

  188.   public String getColumnPrefix() {
  189.     return columnPrefix;
  190.   }

  191.   public List<ResultFlag> getFlags() {
  192.     return flags;
  193.   }

  194.   public List<ResultMapping> getComposites() {
  195.     return composites;
  196.   }

  197.   public boolean isCompositeResult() {
  198.     return this.composites != null && !this.composites.isEmpty();
  199.   }

  200.   public String getResultSet() {
  201.     return this.resultSet;
  202.   }

  203.   public String getForeignColumn() {
  204.     return foreignColumn;
  205.   }

  206.   public void setForeignColumn(String foreignColumn) {
  207.     this.foreignColumn = foreignColumn;
  208.   }

  209.   public boolean isLazy() {
  210.     return lazy;
  211.   }

  212.   public void setLazy(boolean lazy) {
  213.     this.lazy = lazy;
  214.   }

  215.   public boolean isSimple() {
  216.     return this.nestedResultMapId == null && this.nestedQueryId == null && this.resultSet == null;
  217.   }

  218.   @Override
  219.   public boolean equals(Object o) {
  220.     if (this == o) {
  221.       return true;
  222.     }
  223.     if (o == null || getClass() != o.getClass()) {
  224.       return false;
  225.     }

  226.     ResultMapping that = (ResultMapping) o;

  227.     return property != null && property.equals(that.property);
  228.   }

  229.   @Override
  230.   public int hashCode() {
  231.     if (property != null) {
  232.       return property.hashCode();
  233.     }
  234.     if (column != null) {
  235.       return column.hashCode();
  236.     } else {
  237.       return 0;
  238.     }
  239.   }

  240.   @Override
  241.   public String toString() {
  242.     final StringBuilder sb = new StringBuilder("ResultMapping{");
  243.     // sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
  244.     sb.append("property='").append(property).append('\'');
  245.     sb.append(", column='").append(column).append('\'');
  246.     sb.append(", javaType=").append(javaType);
  247.     sb.append(", jdbcType=").append(jdbcType);
  248.     // sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
  249.     sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\'');
  250.     sb.append(", nestedQueryId='").append(nestedQueryId).append('\'');
  251.     sb.append(", notNullColumns=").append(notNullColumns);
  252.     sb.append(", columnPrefix='").append(columnPrefix).append('\'');
  253.     sb.append(", flags=").append(flags);
  254.     sb.append(", composites=").append(composites);
  255.     sb.append(", resultSet='").append(resultSet).append('\'');
  256.     sb.append(", foreignColumn='").append(foreignColumn).append('\'');
  257.     sb.append(", lazy=").append(lazy);
  258.     sb.append('}');
  259.     return sb.toString();
  260.   }

  261. }