31
31
import org .apache .ibatis .mapping .ResultMap ;
32
32
import org .apache .ibatis .mapping .ResultMapping ;
33
33
import org .apache .ibatis .reflection .ReflectionException ;
34
+ import org .apache .ibatis .reflection .factory .DefaultObjectFactory ;
34
35
import org .apache .ibatis .reflection .factory .ObjectFactory ;
35
36
36
37
/**
@@ -43,16 +44,19 @@ final class PendingConstructorCreation {
43
44
private final Class <?> resultType ;
44
45
private final List <Class <?>> constructorArgTypes ;
45
46
private final List <Object > constructorArgs ;
47
+
46
48
private final Map <Integer , PendingCreationMetaInfo > linkedCollectionMetaInfo ;
47
- private final Map <String , Collection <Object >> linkedCollectionsByResultMapId ;
48
- private final Map <String , List <PendingConstructorCreation >> linkedCreationsByResultMapId ;
49
+ private final Map <PendingCreationKey , Collection <Object >> linkedCollectionsByKey ;
50
+ private final Map <PendingCreationKey , List <PendingConstructorCreation >> linkedCreationsByKey ;
49
51
50
52
PendingConstructorCreation (Class <?> resultType , List <Class <?>> types , List <Object > args ) {
51
53
// since all our keys are based on result map id, we know we will never go over args size
52
54
final int maxSize = types .size ();
55
+
53
56
this .linkedCollectionMetaInfo = new HashMap <>(maxSize );
54
- this .linkedCollectionsByResultMapId = new HashMap <>(maxSize );
55
- this .linkedCreationsByResultMapId = new HashMap <>(maxSize );
57
+ this .linkedCollectionsByKey = new HashMap <>(maxSize );
58
+ this .linkedCreationsByKey = new HashMap <>(maxSize );
59
+
56
60
this .resultType = resultType ;
57
61
this .constructorArgTypes = types ;
58
62
this .constructorArgs = args ;
@@ -63,55 +67,63 @@ Collection<Object> initializeCollectionForResultMapping(ObjectFactory objectFact
63
67
ResultMapping constructorMapping , Integer index ) {
64
68
final Class <?> parameterType = constructorMapping .getJavaType ();
65
69
if (!objectFactory .isCollection (parameterType )) {
66
- throw new ExecutorException (
70
+ throw new ReflectionException (
67
71
"Cannot add a collection result to non-collection based resultMapping: " + constructorMapping );
68
72
}
69
73
70
- final String resultMapId = constructorMapping . getNestedResultMapId ( );
71
- return linkedCollectionsByResultMapId .computeIfAbsent (resultMapId , (k ) -> {
74
+ final PendingCreationKey creationKey = new PendingCreationKey ( constructorMapping );
75
+ return linkedCollectionsByKey .computeIfAbsent (creationKey , (k ) -> {
72
76
// this will allow us to verify the types of the collection before creating the final object
73
- linkedCollectionMetaInfo .put (index , new PendingCreationMetaInfo (resultMap .getType (), resultMapId ));
77
+ linkedCollectionMetaInfo .put (index , new PendingCreationMetaInfo (resultMap .getType (), creationKey ));
74
78
75
79
// will be checked before we finally create the object) as we cannot reliably do that here
76
80
return (Collection <Object >) objectFactory .create (parameterType );
77
81
});
78
82
}
79
83
80
- void linkCreation (ResultMap nestedResultMap , PendingConstructorCreation pcc ) {
81
- final String resultMapId = nestedResultMap . getId ( );
82
- final List <PendingConstructorCreation > pendingConstructorCreations = linkedCreationsByResultMapId
83
- .computeIfAbsent (resultMapId , (k ) -> new ArrayList <>());
84
+ void linkCreation (ResultMapping constructorMapping , PendingConstructorCreation pcc ) {
85
+ final PendingCreationKey creationKey = new PendingCreationKey ( constructorMapping );
86
+ final List <PendingConstructorCreation > pendingConstructorCreations = linkedCreationsByKey
87
+ .computeIfAbsent (creationKey , (k ) -> new ArrayList <>());
84
88
85
89
if (pendingConstructorCreations .contains (pcc )) {
86
- throw new ExecutorException ("Cannot link inner pcc with same value, MyBatis programming error!" );
90
+ throw new ExecutorException ("Cannot link inner constructor creation with same value, MyBatis internal error!" );
87
91
}
88
92
89
93
pendingConstructorCreations .add (pcc );
90
94
}
91
95
92
96
void linkCollectionValue (ResultMapping constructorMapping , Object value ) {
93
- // not necessary to add null results to the collection (is this a config flag?)
97
+ // not necessary to add null results to the collection
94
98
if (value == null ) {
95
99
return ;
96
100
}
97
101
98
- final String resultMapId = constructorMapping . getNestedResultMapId ( );
99
- if (!linkedCollectionsByResultMapId .containsKey (resultMapId )) {
100
- throw new ExecutorException ("Cannot link collection value for resultMapping : " + constructorMapping
101
- + ", resultMap has not been seen/initialized yet! Internal error" );
102
+ final PendingCreationKey creationKey = new PendingCreationKey ( constructorMapping );
103
+ if (!linkedCollectionsByKey .containsKey (creationKey )) {
104
+ throw new ExecutorException ("Cannot link collection value for key : " + constructorMapping
105
+ + ", resultMap has not been seen/initialized yet! Mybatis internal error! " );
102
106
}
103
107
104
- linkedCollectionsByResultMapId .get (resultMapId ).add (value );
108
+ linkedCollectionsByKey .get (creationKey ).add (value );
105
109
}
106
110
107
111
/**
108
112
* Verifies preconditions before we can actually create the result object, this is more of a sanity check to ensure
109
113
* all the mappings are as we expect them to be.
114
+ * <p>
115
+ * And if anything went wrong, provide the user with more information as to what went wrong
110
116
*
111
117
* @param objectFactory
112
118
* the object factory
113
119
*/
114
120
private void verifyCanCreate (ObjectFactory objectFactory ) {
121
+ // if a custom object factory was supplied, we cannot reasionably verify that creation will work
122
+ // thus, we disable verification and leave it up to the end user.
123
+ if (!DefaultObjectFactory .class .equals (objectFactory .getClass ())) {
124
+ return ;
125
+ }
126
+
115
127
// before we create, we need to get the constructor to be used and verify our types match
116
128
// since we added to the collection completely unchecked
117
129
final Constructor <?> resolvedConstructor = resolveConstructor (resultType , constructorArgTypes );
@@ -125,24 +137,24 @@ private void verifyCanCreate(ObjectFactory objectFactory) {
125
137
final Class <?> resolvedItemType = checkResolvedItemType (creationMetaInfo , genericParameterTypes [i ]);
126
138
127
139
// ensure we have an empty collection if there are linked creations for this arg
128
- final String resultMapId = creationMetaInfo .getResultMapId ();
129
- if (linkedCreationsByResultMapId .containsKey (resultMapId )) {
140
+ final PendingCreationKey pendingCreationKey = creationMetaInfo .getPendingCreationKey ();
141
+ if (linkedCreationsByKey .containsKey (pendingCreationKey )) {
130
142
final Object emptyCollection = constructorArgs .get (i );
131
143
if (emptyCollection == null || !objectFactory .isCollection (emptyCollection .getClass ())) {
132
144
throw new ExecutorException (
133
- "Expected empty collection for '" + resolvedItemType + "', this is a MyBatis internal error!" );
145
+ "Expected empty collection for '" + resolvedItemType + "', MyBatis internal error!" );
134
146
}
135
147
} else {
136
148
final Object linkedCollection = constructorArgs .get (i );
137
- if (!linkedCollectionsByResultMapId .containsKey (resultMapId )) {
138
- throw new ExecutorException ("Expected linked collection for resultMap '" + resultMapId
139
- + "', not found! this is a MyBatis internal error!" );
149
+ if (!linkedCollectionsByKey .containsKey (pendingCreationKey )) {
150
+ throw new ExecutorException (
151
+ "Expected linked collection for key '" + pendingCreationKey + "', not found! MyBatis internal error!" );
140
152
}
141
153
142
154
// comparing memory locations here (we rely on that fact)
143
- if (linkedCollection != linkedCollectionsByResultMapId .get (resultMapId )) {
155
+ if (linkedCollection != linkedCollectionsByKey .get (pendingCreationKey )) {
144
156
throw new ExecutorException ("Expected linked collection in creation to be the same as arg for resultMap '"
145
- + resultMapId + "', not equal! this is a MyBatis internal error!" );
157
+ + pendingCreationKey + "', not equal! MyBatis internal error!" );
146
158
}
147
159
}
148
160
}
@@ -209,11 +221,11 @@ Object create(ObjectFactory objectFactory, boolean verifyCreate) {
209
221
}
210
222
211
223
// time to finally build this collection
212
- final String resultMapId = creationMetaInfo .getResultMapId ();
213
- if (linkedCreationsByResultMapId .containsKey (resultMapId )) {
224
+ final PendingCreationKey pendingCreationKey = creationMetaInfo .getPendingCreationKey ();
225
+ if (linkedCreationsByKey .containsKey (pendingCreationKey )) {
214
226
@ SuppressWarnings ("unchecked" )
215
227
final Collection <Object > emptyCollection = (Collection <Object >) existingArg ;
216
- final List <PendingConstructorCreation > linkedCreations = linkedCreationsByResultMapId .get (resultMapId );
228
+ final List <PendingConstructorCreation > linkedCreations = linkedCreationsByKey .get (pendingCreationKey );
217
229
218
230
for (PendingConstructorCreation linkedCreation : linkedCreations ) {
219
231
emptyCollection .add (linkedCreation .create (objectFactory , verifyCreate ));
0 commit comments