15
15
*/
16
16
package org .apache .ibatis .mapping ;
17
17
18
+ import java .lang .annotation .Annotation ;
19
+ import java .lang .reflect .Constructor ;
18
20
import java .util .ArrayList ;
19
21
import java .util .Collections ;
22
+ import java .util .Comparator ;
20
23
import java .util .HashSet ;
21
24
import java .util .List ;
22
25
import java .util .Locale ;
23
26
import java .util .Set ;
24
27
28
+ import org .apache .ibatis .annotations .Param ;
29
+ import org .apache .ibatis .builder .BuilderException ;
30
+ import org .apache .ibatis .logging .Log ;
31
+ import org .apache .ibatis .logging .LogFactory ;
32
+ import org .apache .ibatis .reflection .Jdk ;
33
+ import org .apache .ibatis .reflection .ParamNameUtil ;
25
34
import org .apache .ibatis .session .Configuration ;
26
35
27
36
/**
28
37
* @author Clinton Begin
29
38
*/
30
39
public class ResultMap {
40
+ private Configuration configuration ;
41
+
31
42
private String id ;
32
43
private Class <?> type ;
33
44
private List <ResultMapping > resultMappings ;
@@ -45,13 +56,16 @@ private ResultMap() {
45
56
}
46
57
47
58
public static class Builder {
59
+ private static final Log log = LogFactory .getLog (Builder .class );
60
+
48
61
private ResultMap resultMap = new ResultMap ();
49
62
50
63
public Builder (Configuration configuration , String id , Class <?> type , List <ResultMapping > resultMappings ) {
51
64
this (configuration , id , type , resultMappings , null );
52
65
}
53
66
54
67
public Builder (Configuration configuration , String id , Class <?> type , List <ResultMapping > resultMappings , Boolean autoMapping ) {
68
+ resultMap .configuration = configuration ;
55
69
resultMap .id = id ;
56
70
resultMap .type = type ;
57
71
resultMap .resultMappings = resultMappings ;
@@ -76,6 +90,7 @@ public ResultMap build() {
76
90
resultMap .idResultMappings = new ArrayList <ResultMapping >();
77
91
resultMap .constructorResultMappings = new ArrayList <ResultMapping >();
78
92
resultMap .propertyResultMappings = new ArrayList <ResultMapping >();
93
+ final List <String > constructorArgNames = new ArrayList <String >();
79
94
for (ResultMapping resultMapping : resultMap .resultMappings ) {
80
95
resultMap .hasNestedQueries = resultMap .hasNestedQueries || resultMapping .getNestedQueryId () != null ;
81
96
resultMap .hasNestedResultMaps = resultMap .hasNestedResultMaps || (resultMapping .getNestedResultMapId () != null && resultMapping .getResultSet () == null );
@@ -96,6 +111,9 @@ public ResultMap build() {
96
111
}
97
112
if (resultMapping .getFlags ().contains (ResultFlag .CONSTRUCTOR )) {
98
113
resultMap .constructorResultMappings .add (resultMapping );
114
+ if (resultMapping .getProperty () != null ) {
115
+ constructorArgNames .add (resultMapping .getProperty ());
116
+ }
99
117
} else {
100
118
resultMap .propertyResultMappings .add (resultMapping );
101
119
}
@@ -106,6 +124,13 @@ public ResultMap build() {
106
124
if (resultMap .idResultMappings .isEmpty ()) {
107
125
resultMap .idResultMappings .addAll (resultMap .resultMappings );
108
126
}
127
+ if (!constructorArgNames .isEmpty ()) {
128
+ if (!sortConstructorResultMapping (constructorArgNames )) {
129
+ throw new BuilderException ("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
+ }
109
134
// lock down collections
110
135
resultMap .resultMappings = Collections .unmodifiableList (resultMap .resultMappings );
111
136
resultMap .idResultMappings = Collections .unmodifiableList (resultMap .idResultMappings );
@@ -114,6 +139,72 @@ public ResultMap build() {
114
139
resultMap .mappedColumns = Collections .unmodifiableSet (resultMap .mappedColumns );
115
140
return resultMap ;
116
141
}
142
+
143
+ private boolean sortConstructorResultMapping (final List <String > constructorArgNames ) {
144
+ Constructor <?>[] constructors = resultMap .type .getDeclaredConstructors ();
145
+ // Search constructors by arg names and types.
146
+ for (Constructor <?> constructor : constructors ) {
147
+ Class <?>[] paramTypes = constructor .getParameterTypes ();
148
+ if (constructorArgNames .size () == paramTypes .length ) {
149
+ final List <String > paramNames = getArgNames (constructor );
150
+ if (constructorArgNames .containsAll (paramNames )) {
151
+ if (!argTypesMatch (constructorArgNames , paramTypes , paramNames )) {
152
+ continue ;
153
+ }
154
+ // Found a matching constructor.
155
+ Collections .sort (resultMap .constructorResultMappings , new Comparator <ResultMapping >() {
156
+ @ Override
157
+ public int compare (ResultMapping o1 , ResultMapping o2 ) {
158
+ int paramIdx1 = paramNames .indexOf (o1 .getProperty ());
159
+ int paramIdx2 = paramNames .indexOf (o2 .getProperty ());
160
+ return paramIdx1 - paramIdx2 ;
161
+ }
162
+ });
163
+ return true ;
164
+ }
165
+ }
166
+ }
167
+ return false ;
168
+ }
169
+
170
+ private boolean argTypesMatch (final List <String > constructorArgNames ,
171
+ Class <?>[] paramTypes , List <String > paramNames ) {
172
+ for (int i = 0 ; i < constructorArgNames .size (); i ++) {
173
+ Class <?> actualType = paramTypes [paramNames .indexOf (constructorArgNames .get (i ))];
174
+ Class <?> specifiedType = resultMap .constructorResultMappings .get (i ).getJavaType ();
175
+ if (!actualType .equals (specifiedType )) {
176
+ if (log .isDebugEnabled ()) {
177
+ log .debug ("Found a constructor with arg names " + constructorArgNames
178
+ + ", but the type of '" + constructorArgNames .get (i )
179
+ + "' did not match. Specified: [" + specifiedType .getName () + "] Declared: ["
180
+ + actualType .getName () + "]" );
181
+ }
182
+ return false ;
183
+ }
184
+ }
185
+ return true ;
186
+ }
187
+
188
+ private List <String > getArgNames (Constructor <?> constructor ) {
189
+ if (resultMap .configuration .isUseActualParamName () && Jdk .parameterExists ) {
190
+ return ParamNameUtil .getParamNames (constructor );
191
+ } else {
192
+ List <String > paramNames = new ArrayList <String >();
193
+ final Annotation [][] paramAnnotations = constructor .getParameterAnnotations ();
194
+ int paramCount = paramAnnotations .length ;
195
+ for (int paramIndex = 0 ; paramIndex < paramCount ; paramIndex ++) {
196
+ String name = null ;
197
+ for (Annotation annotation : paramAnnotations [paramIndex ]) {
198
+ if (annotation instanceof Param ) {
199
+ name = ((Param ) annotation ).value ();
200
+ break ;
201
+ }
202
+ }
203
+ paramNames .add (name != null ? name : "arg" + paramIndex );
204
+ }
205
+ return paramNames ;
206
+ }
207
+ }
117
208
}
118
209
119
210
public String getId () {
0 commit comments