1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.mapping;
17
18 import java.lang.annotation.Annotation;
19 import java.lang.reflect.Constructor;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Set;
26
27 import org.apache.ibatis.annotations.Param;
28 import org.apache.ibatis.builder.BuilderException;
29 import org.apache.ibatis.logging.Log;
30 import org.apache.ibatis.logging.LogFactory;
31 import org.apache.ibatis.reflection.ParamNameUtil;
32 import org.apache.ibatis.session.Configuration;
33
34
35
36
37 public class ResultMap {
38 private Configuration configuration;
39
40 private String id;
41 private Class<?> type;
42 private List<ResultMapping> resultMappings;
43 private List<ResultMapping> idResultMappings;
44 private List<ResultMapping> constructorResultMappings;
45 private List<ResultMapping> propertyResultMappings;
46 private Set<String> mappedColumns;
47 private Set<String> mappedProperties;
48 private Discriminator discriminator;
49 private boolean hasNestedResultMaps;
50 private boolean hasNestedQueries;
51 private Boolean autoMapping;
52
53 private ResultMap() {
54 }
55
56 public static class Builder {
57 private static final Log log = LogFactory.getLog(Builder.class);
58
59 private ResultMap resultMap = new ResultMap();
60
61 public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
62 this(configuration, id, type, resultMappings, null);
63 }
64
65 public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings, Boolean autoMapping) {
66 resultMap.configuration = configuration;
67 resultMap.id = id;
68 resultMap.type = type;
69 resultMap.resultMappings = resultMappings;
70 resultMap.autoMapping = autoMapping;
71 }
72
73 public Builder discriminator(Discriminator discriminator) {
74 resultMap.discriminator = discriminator;
75 return this;
76 }
77
78 public Class<?> type() {
79 return resultMap.type;
80 }
81
82 public ResultMap build() {
83 if (resultMap.id == null) {
84 throw new IllegalArgumentException("ResultMaps must have an id");
85 }
86 resultMap.mappedColumns = new HashSet<>();
87 resultMap.mappedProperties = new HashSet<>();
88 resultMap.idResultMappings = new ArrayList<>();
89 resultMap.constructorResultMappings = new ArrayList<>();
90 resultMap.propertyResultMappings = new ArrayList<>();
91 final List<String> constructorArgNames = new ArrayList<>();
92 for (ResultMapping resultMapping : resultMap.resultMappings) {
93 resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
94 resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
95 final String column = resultMapping.getColumn();
96 if (column != null) {
97 resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
98 } else if (resultMapping.isCompositeResult()) {
99 for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
100 final String compositeColumn = compositeResultMapping.getColumn();
101 if (compositeColumn != null) {
102 resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
103 }
104 }
105 }
106 final String property = resultMapping.getProperty();
107 if (property != null) {
108 resultMap.mappedProperties.add(property);
109 }
110 if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
111 resultMap.constructorResultMappings.add(resultMapping);
112 if (resultMapping.getProperty() != null) {
113 constructorArgNames.add(resultMapping.getProperty());
114 }
115 } else {
116 resultMap.propertyResultMappings.add(resultMapping);
117 }
118 if (resultMapping.getFlags().contains(ResultFlag.ID)) {
119 resultMap.idResultMappings.add(resultMapping);
120 }
121 }
122 if (resultMap.idResultMappings.isEmpty()) {
123 resultMap.idResultMappings.addAll(resultMap.resultMappings);
124 }
125 if (!constructorArgNames.isEmpty()) {
126 final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
127 if (actualArgNames == null) {
128 throw new BuilderException("Error in result map '" + resultMap.id
129 + "'. Failed to find a constructor in '"
130 + resultMap.getType().getName() + "' by arg names " + constructorArgNames
131 + ". There might be more info in debug log.");
132 }
133 resultMap.constructorResultMappings.sort((o1, o2) -> {
134 int paramIdx1 = actualArgNames.indexOf(o1.getProperty());
135 int paramIdx2 = actualArgNames.indexOf(o2.getProperty());
136 return paramIdx1 - paramIdx2;
137 });
138 }
139
140 resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
141 resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
142 resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
143 resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
144 resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
145 return resultMap;
146 }
147
148 private List<String> argNamesOfMatchingConstructor(List<String> constructorArgNames) {
149 Constructor<?>[] constructors = resultMap.type.getDeclaredConstructors();
150 for (Constructor<?> constructor : constructors) {
151 Class<?>[] paramTypes = constructor.getParameterTypes();
152 if (constructorArgNames.size() == paramTypes.length) {
153 List<String> paramNames = getArgNames(constructor);
154 if (constructorArgNames.containsAll(paramNames)
155 && argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
156 return paramNames;
157 }
158 }
159 }
160 return null;
161 }
162
163 private boolean argTypesMatch(final List<String> constructorArgNames,
164 Class<?>[] paramTypes, List<String> paramNames) {
165 for (int i = 0; i < constructorArgNames.size(); i++) {
166 Class<?> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
167 Class<?> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
168 if (!actualType.equals(specifiedType)) {
169 if (log.isDebugEnabled()) {
170 log.debug("While building result map '" + resultMap.id
171 + "', found a constructor with arg names " + constructorArgNames
172 + ", but the type of '" + constructorArgNames.get(i)
173 + "' did not match. Specified: [" + specifiedType.getName() + "] Declared: ["
174 + actualType.getName() + "]");
175 }
176 return false;
177 }
178 }
179 return true;
180 }
181
182 private List<String> getArgNames(Constructor<?> constructor) {
183 List<String> paramNames = new ArrayList<>();
184 List<String> actualParamNames = null;
185 final Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
186 int paramCount = paramAnnotations.length;
187 for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
188 String name = null;
189 for (Annotation annotation : paramAnnotations[paramIndex]) {
190 if (annotation instanceof Param) {
191 name = ((Param) annotation).value();
192 break;
193 }
194 }
195 if (name == null && resultMap.configuration.isUseActualParamName()) {
196 if (actualParamNames == null) {
197 actualParamNames = ParamNameUtil.getParamNames(constructor);
198 }
199 if (actualParamNames.size() > paramIndex) {
200 name = actualParamNames.get(paramIndex);
201 }
202 }
203 paramNames.add(name != null ? name : "arg" + paramIndex);
204 }
205 return paramNames;
206 }
207 }
208
209 public String getId() {
210 return id;
211 }
212
213 public boolean hasNestedResultMaps() {
214 return hasNestedResultMaps;
215 }
216
217 public boolean hasNestedQueries() {
218 return hasNestedQueries;
219 }
220
221 public Class<?> getType() {
222 return type;
223 }
224
225 public List<ResultMapping> getResultMappings() {
226 return resultMappings;
227 }
228
229 public List<ResultMapping> getConstructorResultMappings() {
230 return constructorResultMappings;
231 }
232
233 public List<ResultMapping> getPropertyResultMappings() {
234 return propertyResultMappings;
235 }
236
237 public List<ResultMapping> getIdResultMappings() {
238 return idResultMappings;
239 }
240
241 public Set<String> getMappedColumns() {
242 return mappedColumns;
243 }
244
245 public Set<String> getMappedProperties() {
246 return mappedProperties;
247 }
248
249 public Discriminator getDiscriminator() {
250 return discriminator;
251 }
252
253 public void forceNestedResultMaps() {
254 hasNestedResultMaps = true;
255 }
256
257 public Boolean getAutoMapping() {
258 return autoMapping;
259 }
260
261 }