19
19
import java .sql .ResultSetMetaData ;
20
20
import java .sql .SQLException ;
21
21
import java .sql .Statement ;
22
- import java .util .ArrayList ;
23
22
import java .util .Arrays ;
24
23
import java .util .Collection ;
25
24
import java .util .List ;
26
25
import java .util .Map ;
27
26
28
27
import org .apache .ibatis .binding .BindingException ;
28
+ import org .apache .ibatis .binding .MapperMethod .ParamMap ;
29
29
import org .apache .ibatis .executor .Executor ;
30
30
import org .apache .ibatis .executor .ExecutorException ;
31
31
import org .apache .ibatis .mapping .MappedStatement ;
32
+ import org .apache .ibatis .reflection .ArrayUtil ;
32
33
import org .apache .ibatis .reflection .MetaObject ;
33
34
import org .apache .ibatis .session .Configuration ;
35
+ import org .apache .ibatis .session .defaults .DefaultSqlSession .StrictMap ;
34
36
import org .apache .ibatis .type .JdbcType ;
35
37
import org .apache .ibatis .type .TypeHandler ;
36
38
import org .apache .ibatis .type .TypeHandlerRegistry ;
@@ -43,6 +45,7 @@ public class Jdbc3KeyGenerator implements KeyGenerator {
43
45
44
46
/**
45
47
* A shared instance.
48
+ *
46
49
* @since 3.4.3
47
50
*/
48
51
public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator ();
@@ -54,29 +57,24 @@ public void processBefore(Executor executor, MappedStatement ms, Statement stmt,
54
57
55
58
@ Override
56
59
public void processAfter (Executor executor , MappedStatement ms , Statement stmt , Object parameter ) {
57
- processBatch (ms , stmt , getParameters ( parameter ) );
60
+ processBatch (ms , stmt , parameter );
58
61
}
59
62
60
- public void processBatch (MappedStatement ms , Statement stmt , Collection <Object > parameters ) {
63
+ public void processBatch (MappedStatement ms , Statement stmt , Object parameter ) {
64
+ final String [] keyProperties = ms .getKeyProperties ();
65
+ if (keyProperties == null || keyProperties .length == 0 ) {
66
+ return ;
67
+ }
61
68
ResultSet rs = null ;
62
69
try {
63
70
rs = stmt .getGeneratedKeys ();
64
71
final Configuration configuration = ms .getConfiguration ();
65
- final TypeHandlerRegistry typeHandlerRegistry = configuration .getTypeHandlerRegistry ();
66
- final String [] keyProperties = ms .getKeyProperties ();
67
- final ResultSetMetaData rsmd = rs .getMetaData ();
68
- TypeHandler <?>[] typeHandlers = null ;
69
- if (keyProperties != null && rsmd .getColumnCount () >= keyProperties .length ) {
70
- for (Object parameter : parameters ) {
71
- // there should be one row for each statement (also one for each parameter)
72
- if (!rs .next ()) {
73
- break ;
74
- }
75
- final MetaObject metaParam = configuration .newMetaObject (parameter );
76
- if (typeHandlers == null ) {
77
- typeHandlers = getTypeHandlers (typeHandlerRegistry , metaParam , keyProperties , rsmd );
78
- }
79
- populateKeys (rs , metaParam , keyProperties , typeHandlers );
72
+ if (rs .getMetaData ().getColumnCount () >= keyProperties .length ) {
73
+ Object soleParam = getSoleParameter (parameter );
74
+ if (soleParam != null ) {
75
+ assignKeysToParam (configuration , rs , keyProperties , soleParam );
76
+ } else {
77
+ assignKeysToOneOfParams (configuration , rs , keyProperties , (Map <?, ?>) parameter );
80
78
}
81
79
}
82
80
} catch (Exception e ) {
@@ -92,25 +90,103 @@ public void processBatch(MappedStatement ms, Statement stmt, Collection<Object>
92
90
}
93
91
}
94
92
95
- private Collection <Object > getParameters (Object parameter ) {
96
- Collection <Object > parameters = null ;
97
- if (parameter instanceof Collection ) {
98
- parameters = (Collection ) parameter ;
99
- } else if (parameter instanceof Map ) {
100
- Map parameterMap = (Map ) parameter ;
101
- if (parameterMap .containsKey ("collection" )) {
102
- parameters = (Collection ) parameterMap .get ("collection" );
103
- } else if (parameterMap .containsKey ("list" )) {
104
- parameters = (List ) parameterMap .get ("list" );
105
- } else if (parameterMap .containsKey ("array" )) {
106
- parameters = Arrays .asList ((Object []) parameterMap .get ("array" ));
93
+ protected void assignKeysToOneOfParams (final Configuration configuration , ResultSet rs , final String [] keyProperties ,
94
+ Map <?, ?> paramMap ) throws SQLException {
95
+ // For backward compatibility, search parameter with special name first.
96
+ if (paramMap .containsKey ("collection" )) {
97
+ Object param = paramMap .get ("collection" );
98
+ if (param instanceof Collection ) {
99
+ assignKeysToParam (configuration , rs , keyProperties , param );
100
+ return ;
101
+ }
102
+ } else if (paramMap .containsKey ("list" )) {
103
+ Object param = paramMap .get ("list" );
104
+ if (param instanceof List ) {
105
+ assignKeysToParam (configuration , rs , keyProperties , param );
106
+ return ;
107
+ }
108
+ } else if (paramMap .containsKey ("array" )) {
109
+ Object param = paramMap .get ("array" );
110
+ if (param instanceof Object []) {
111
+ assignKeysToParam (configuration , rs , keyProperties , param );
112
+ return ;
113
+ }
114
+ }
115
+ // Assuming 'keyProperty' includes the parameter name. e.g. 'param.id'.
116
+ int firstDot = keyProperties [0 ].indexOf ('.' );
117
+ if (firstDot == -1 ) {
118
+ throw new ExecutorException (
119
+ "Could not determine which parameter to assign generated keys to. "
120
+ + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
121
+ + "Specified key properties are " + ArrayUtil .toString (keyProperties ) + " and available parameters are "
122
+ + paramMap .keySet ());
123
+ }
124
+ String paramName = keyProperties [0 ].substring (0 , firstDot );
125
+ Object param ;
126
+ if (paramMap .containsKey (paramName )) {
127
+ param = paramMap .get (paramName );
128
+ } else {
129
+ throw new ExecutorException ("Could not find parameter '" + paramName + "'. "
130
+ + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
131
+ + "Specified key properties are " + ArrayUtil .toString (keyProperties ) + " and available parameters are "
132
+ + paramMap .keySet ());
133
+ }
134
+ // Remove param name from 'keyProperty' string. e.g. 'param.id' -> 'id'
135
+ String [] modifiedKeyProperties = new String [keyProperties .length ];
136
+ for (int i = 0 ; i < keyProperties .length ; i ++) {
137
+ if (keyProperties [i ].charAt (firstDot ) == '.' && keyProperties [i ].startsWith (paramName )) {
138
+ modifiedKeyProperties [i ] = keyProperties [i ].substring (firstDot + 1 );
139
+ } else {
140
+ throw new ExecutorException ("Assigning generated keys to multiple parameters is not supported. "
141
+ + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
142
+ + "Specified key properties are " + ArrayUtil .toString (keyProperties ) + " and available parameters are "
143
+ + paramMap .keySet ());
144
+ }
145
+ }
146
+ assignKeysToParam (configuration , rs , modifiedKeyProperties , param );
147
+ }
148
+
149
+ private void assignKeysToParam (final Configuration configuration , ResultSet rs , final String [] keyProperties ,
150
+ Object param )
151
+ throws SQLException {
152
+ final TypeHandlerRegistry typeHandlerRegistry = configuration .getTypeHandlerRegistry ();
153
+ final ResultSetMetaData rsmd = rs .getMetaData ();
154
+ // Wrap the parameter in Collection to normalize the logic.
155
+ Collection <?> paramAsCollection = null ;
156
+ if (param instanceof Object []) {
157
+ paramAsCollection = Arrays .asList ((Object []) param );
158
+ } else if (!(param instanceof Collection )) {
159
+ paramAsCollection = Arrays .asList (param );
160
+ } else {
161
+ paramAsCollection = (Collection <?>) param ;
162
+ }
163
+ TypeHandler <?>[] typeHandlers = null ;
164
+ for (Object obj : paramAsCollection ) {
165
+ if (!rs .next ()) {
166
+ break ;
167
+ }
168
+ MetaObject metaParam = configuration .newMetaObject (obj );
169
+ if (typeHandlers == null ) {
170
+ typeHandlers = getTypeHandlers (typeHandlerRegistry , metaParam , keyProperties , rsmd );
107
171
}
172
+ populateKeys (rs , metaParam , keyProperties , typeHandlers );
108
173
}
109
- if (parameters == null ) {
110
- parameters = new ArrayList <Object >();
111
- parameters .add (parameter );
174
+ }
175
+
176
+ private Object getSoleParameter (Object parameter ) {
177
+ if (!(parameter instanceof ParamMap || parameter instanceof StrictMap )) {
178
+ return parameter ;
179
+ }
180
+ Object soleParam = null ;
181
+ for (Object paramValue : ((Map <?, ?>) parameter ).values ()) {
182
+ if (soleParam == null ) {
183
+ soleParam = paramValue ;
184
+ } else if (soleParam != paramValue ) {
185
+ soleParam = null ;
186
+ break ;
187
+ }
112
188
}
113
- return parameters ;
189
+ return soleParam ;
114
190
}
115
191
116
192
private TypeHandler <?>[] getTypeHandlers (TypeHandlerRegistry typeHandlerRegistry , MetaObject metaParam , String [] keyProperties , ResultSetMetaData rsmd ) throws SQLException {
0 commit comments