View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  import java.util.Set;
22  
23  import org.apache.ibatis.session.Configuration;
24  import org.apache.ibatis.type.JdbcType;
25  import org.apache.ibatis.type.TypeHandler;
26  import org.apache.ibatis.type.TypeHandlerRegistry;
27  
28  /**
29   * @author Clinton Begin
30   */
31  public class ResultMapping {
32  
33    private Configuration configuration;
34    private String property;
35    private String column;
36    private Class<?> javaType;
37    private JdbcType jdbcType;
38    private TypeHandler<?> typeHandler;
39    private String nestedResultMapId;
40    private String nestedQueryId;
41    private Set<String> notNullColumns;
42    private String columnPrefix;
43    private List<ResultFlag> flags;
44    private List<ResultMapping> composites;
45    private String resultSet;
46    private String foreignColumn;
47    private boolean lazy;
48  
49    ResultMapping() {
50    }
51  
52    public static class Builder {
53      private ResultMapping resultMapping = new ResultMapping();
54  
55      public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
56        this(configuration, property);
57        resultMapping.column = column;
58        resultMapping.typeHandler = typeHandler;
59      }
60  
61      public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
62        this(configuration, property);
63        resultMapping.column = column;
64        resultMapping.javaType = javaType;
65      }
66  
67      public Builder(Configuration configuration, String property) {
68        resultMapping.configuration = configuration;
69        resultMapping.property = property;
70        resultMapping.flags = new ArrayList<>();
71        resultMapping.composites = new ArrayList<>();
72        resultMapping.lazy = configuration.isLazyLoadingEnabled();
73      }
74  
75      public Builder javaType(Class<?> javaType) {
76        resultMapping.javaType = javaType;
77        return this;
78      }
79  
80      public Builder jdbcType(JdbcType jdbcType) {
81        resultMapping.jdbcType = jdbcType;
82        return this;
83      }
84  
85      public Builder nestedResultMapId(String nestedResultMapId) {
86        resultMapping.nestedResultMapId = nestedResultMapId;
87        return this;
88      }
89  
90      public Builder nestedQueryId(String nestedQueryId) {
91        resultMapping.nestedQueryId = nestedQueryId;
92        return this;
93      }
94  
95      public Builder resultSet(String resultSet) {
96        resultMapping.resultSet = resultSet;
97        return this;
98      }
99  
100     public Builder foreignColumn(String foreignColumn) {
101       resultMapping.foreignColumn = foreignColumn;
102       return this;
103     }
104 
105     public Builder notNullColumns(Set<String> notNullColumns) {
106       resultMapping.notNullColumns = notNullColumns;
107       return this;
108     }
109 
110     public Builder columnPrefix(String columnPrefix) {
111       resultMapping.columnPrefix = columnPrefix;
112       return this;
113     }
114 
115     public Builder flags(List<ResultFlag> flags) {
116       resultMapping.flags = flags;
117       return this;
118     }
119 
120     public Builder typeHandler(TypeHandler<?> typeHandler) {
121       resultMapping.typeHandler = typeHandler;
122       return this;
123     }
124 
125     public Builder composites(List<ResultMapping> composites) {
126       resultMapping.composites = composites;
127       return this;
128     }
129 
130     public Builder lazy(boolean lazy) {
131       resultMapping.lazy = lazy;
132       return this;
133     }
134 
135     public ResultMapping build() {
136       // lock down collections
137       resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
138       resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
139       resolveTypeHandler();
140       validate();
141       return resultMapping;
142     }
143 
144     private void validate() {
145       // Issue #697: cannot define both nestedQueryId and nestedResultMapId
146       if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
147         throw new IllegalStateException("Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
148       }
149       // Issue #5: there should be no mappings without typehandler
150       if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null && resultMapping.typeHandler == null) {
151         throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
152       }
153       // Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest
154       if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) {
155         throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
156       }
157       if (resultMapping.getResultSet() != null) {
158         int numColumns = 0;
159         if (resultMapping.column != null) {
160           numColumns = resultMapping.column.split(",").length;
161         }
162         int numForeignColumns = 0;
163         if (resultMapping.foreignColumn != null) {
164           numForeignColumns = resultMapping.foreignColumn.split(",").length;
165         }
166         if (numColumns != numForeignColumns) {
167           throw new IllegalStateException("There should be the same number of columns and foreignColumns in property " + resultMapping.property);
168         }
169       }
170     }
171 
172     private void resolveTypeHandler() {
173       if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
174         Configuration configuration = resultMapping.configuration;
175         TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
176         resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
177       }
178     }
179 
180     public Builder column(String column) {
181       resultMapping.column = column;
182       return this;
183     }
184   }
185 
186   public String getProperty() {
187     return property;
188   }
189 
190   public String getColumn() {
191     return column;
192   }
193 
194   public Class<?> getJavaType() {
195     return javaType;
196   }
197 
198   public JdbcType getJdbcType() {
199     return jdbcType;
200   }
201 
202   public TypeHandler<?> getTypeHandler() {
203     return typeHandler;
204   }
205 
206   public String getNestedResultMapId() {
207     return nestedResultMapId;
208   }
209 
210   public String getNestedQueryId() {
211     return nestedQueryId;
212   }
213 
214   public Set<String> getNotNullColumns() {
215     return notNullColumns;
216   }
217 
218   public String getColumnPrefix() {
219     return columnPrefix;
220   }
221 
222   public List<ResultFlag> getFlags() {
223     return flags;
224   }
225 
226   public List<ResultMapping> getComposites() {
227     return composites;
228   }
229 
230   public boolean isCompositeResult() {
231     return this.composites != null && !this.composites.isEmpty();
232   }
233 
234   public String getResultSet() {
235     return this.resultSet;
236   }
237 
238   public String getForeignColumn() {
239     return foreignColumn;
240   }
241 
242   public void setForeignColumn(String foreignColumn) {
243     this.foreignColumn = foreignColumn;
244   }
245 
246   public boolean isLazy() {
247     return lazy;
248   }
249 
250   public void setLazy(boolean lazy) {
251     this.lazy = lazy;
252   }
253 
254   public boolean isSimple() {
255     return this.nestedResultMapId == null && this.nestedQueryId == null && this.resultSet == null;
256   }
257 
258   @Override
259   public boolean equals(Object o) {
260     if (this == o) {
261       return true;
262     }
263     if (o == null || getClass() != o.getClass()) {
264       return false;
265     }
266 
267     ResultMapping that = (ResultMapping) o;
268 
269     return property != null && property.equals(that.property);
270   }
271 
272   @Override
273   public int hashCode() {
274     if (property != null) {
275       return property.hashCode();
276     } else if (column != null) {
277       return column.hashCode();
278     } else {
279       return 0;
280     }
281   }
282 
283   @Override
284   public String toString() {
285     final StringBuilder sb = new StringBuilder("ResultMapping{");
286     //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
287     sb.append("property='").append(property).append('\'');
288     sb.append(", column='").append(column).append('\'');
289     sb.append(", javaType=").append(javaType);
290     sb.append(", jdbcType=").append(jdbcType);
291     //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
292     sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\'');
293     sb.append(", nestedQueryId='").append(nestedQueryId).append('\'');
294     sb.append(", notNullColumns=").append(notNullColumns);
295     sb.append(", columnPrefix='").append(columnPrefix).append('\'');
296     sb.append(", flags=").append(flags);
297     sb.append(", composites=").append(composites);
298     sb.append(", resultSet='").append(resultSet).append('\'');
299     sb.append(", foreignColumn='").append(foreignColumn).append('\'');
300     sb.append(", lazy=").append(lazy);
301     sb.append('}');
302     return sb.toString();
303   }
304 
305 }