Skip to content

Commit 838b00f

Browse files
committed
Fix IndexOutOfBoundsException
1 parent 884da7a commit 838b00f

File tree

2 files changed

+178
-3
lines changed

2 files changed

+178
-3
lines changed

src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,10 +729,11 @@ private Object createByConstructorSignature(ResultSetWrapper rsw, ResultMap resu
729729
"No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames())));
730730
}
731731

732-
private Optional<Constructor<?>> findConstructorForAutomapping(final Class<?> resultType, ResultSetWrapper rsw) {
732+
private Optional<? extends Constructor<?>> findConstructorForAutomapping(final Class<?> resultType,
733+
ResultSetWrapper rsw) {
733734
Constructor<?>[] constructors = resultType.getDeclaredConstructors();
734735
if (constructors.length == 1) {
735-
return Optional.of(constructors[0]);
736+
return Optional.of(constructors[0]).filter(x -> findUsableConstructorByArgTypes(x, rsw.getJdbcTypes()));
736737
}
737738
Optional<Constructor<?>> annotated = Arrays.stream(constructors)
738739
.filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).reduce((x, y) -> {

src/test/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandlerTest.java

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2023 the original author or authors.
2+
* Copyright 2009-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616
package org.apache.ibatis.executor.resultset;
1717

1818
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertNull;
1920
import static org.mockito.ArgumentMatchers.any;
2021
import static org.mockito.Mockito.mock;
2122
import static org.mockito.Mockito.when;
@@ -130,6 +131,74 @@ void shouldThrowExceptionWithColumnName() throws Exception {
130131
}
131132
}
132133

134+
@Test
135+
void shouldNormallyWhenHasNoArgsConstructor() throws Exception {
136+
Class<ResultClassNoArgsConstructor> resultTypeClazz = ResultClassNoArgsConstructor.class;
137+
final DefaultResultSetHandler fastResultSetHandler = getDefaultResultSetHandler(resultTypeClazz);
138+
final List<Object> results = fastResultSetHandler.handleResultSets(stmt);
139+
assertEquals(1, results.size());
140+
141+
ResultClassNoArgsConstructor result = resultTypeClazz.cast(results.get(0));
142+
assertEquals(10, result.id);
143+
assertEquals("tom", result.name);
144+
assertNull(result.gender);
145+
}
146+
147+
@Test
148+
void shouldNormallyWhenHasNoArgsAndAllArgsConstructor() throws Exception {
149+
Class<ResultClassNoArgsAndAllArgsConstructor> resultTypeClazz = ResultClassNoArgsAndAllArgsConstructor.class;
150+
final DefaultResultSetHandler fastResultSetHandler = getDefaultResultSetHandler(resultTypeClazz);
151+
final List<Object> results = fastResultSetHandler.handleResultSets(stmt);
152+
assertEquals(1, results.size());
153+
154+
ResultClassNoArgsAndAllArgsConstructor result = resultTypeClazz.cast(results.get(0));
155+
assertEquals(10, result.id);
156+
assertEquals("tom", result.name);
157+
assertNull(result.gender);
158+
}
159+
160+
@Test
161+
void shouldThrowExceptionWithConstructorParameterMismatch() throws Exception {
162+
163+
final Configuration config = new Configuration();
164+
MappedStatement ms = new MappedStatement.Builder(config, "testSelect",
165+
new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps(new ArrayList<>() {
166+
private static final long serialVersionUID = 1L;
167+
{
168+
add(new ResultMap.Builder(config, "testMap", ResultClassOnlyHasAllArgsConstructor.class,
169+
Collections.emptyList()).build());
170+
}
171+
}).build();
172+
173+
final Executor executor = null;
174+
final ParameterHandler parameterHandler = null;
175+
final ResultHandler resultHandler = null;
176+
final BoundSql boundSql = null;
177+
final RowBounds rowBounds = new RowBounds(0, 100);
178+
final DefaultResultSetHandler fastResultSetHandler = new DefaultResultSetHandler(executor, ms, parameterHandler,
179+
resultHandler, boundSql, rowBounds);
180+
181+
when(stmt.getResultSet()).thenReturn(rs);
182+
when(rs.getMetaData()).thenReturn(rsmd);
183+
when(rs.getType()).thenReturn(ResultSet.TYPE_FORWARD_ONLY);
184+
when(rs.next()).thenReturn(true).thenReturn(false);
185+
when(rsmd.getColumnCount()).thenReturn(2);
186+
when(rsmd.getColumnLabel(1)).thenReturn("id");
187+
when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
188+
when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
189+
when(rsmd.getColumnLabel(2)).thenReturn("name");
190+
when(rsmd.getColumnType(2)).thenReturn(Types.VARCHAR);
191+
when(rsmd.getColumnClassName(2)).thenReturn(String.class.getCanonicalName());
192+
try {
193+
fastResultSetHandler.handleResultSets(stmt);
194+
} catch (Exception e) {
195+
Assertions.assertTrue(e instanceof ExecutorException, "Expected ExecutorException");
196+
Assertions.assertTrue(e.getMessage().contains("No constructor found in"));
197+
ResultSetWrapper resultSetWrapper = new ResultSetWrapper(rs, config);
198+
Assertions.assertTrue(e.getMessage().contains("matching " + resultSetWrapper.getClassNames()));
199+
}
200+
}
201+
133202
MappedStatement getMappedStatement() {
134203
final Configuration config = new Configuration();
135204
final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
@@ -148,4 +217,109 @@ MappedStatement getMappedStatement() {
148217
}).build();
149218
}
150219

220+
DefaultResultSetHandler getDefaultResultSetHandler(Class<?> clazz) throws SQLException {
221+
final Configuration config = new Configuration();
222+
MappedStatement ms = new MappedStatement.Builder(config, "testSelect",
223+
new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps(new ArrayList<>() {
224+
private static final long serialVersionUID = 1L;
225+
{
226+
add(new ResultMap.Builder(config, "testMap", clazz, Collections.emptyList()).build());
227+
}
228+
}).build();
229+
230+
final Executor executor = null;
231+
final ParameterHandler parameterHandler = null;
232+
final ResultHandler resultHandler = null;
233+
final BoundSql boundSql = null;
234+
final RowBounds rowBounds = new RowBounds(0, 100);
235+
final DefaultResultSetHandler fastResultSetHandler = new DefaultResultSetHandler(executor, ms, parameterHandler,
236+
resultHandler, boundSql, rowBounds);
237+
238+
when(stmt.getResultSet()).thenReturn(rs);
239+
when(rs.getMetaData()).thenReturn(rsmd);
240+
when(rs.getType()).thenReturn(ResultSet.TYPE_FORWARD_ONLY);
241+
when(rs.next()).thenReturn(true).thenReturn(false);
242+
when(rs.getInt("id")).thenReturn(10);
243+
when(rs.getString("name")).thenReturn("tom");
244+
when(rsmd.getColumnCount()).thenReturn(2);
245+
when(rsmd.getColumnLabel(1)).thenReturn("id");
246+
when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
247+
when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
248+
when(rsmd.getColumnLabel(2)).thenReturn("name");
249+
when(rsmd.getColumnType(2)).thenReturn(Types.VARCHAR);
250+
when(rsmd.getColumnClassName(2)).thenReturn(String.class.getCanonicalName());
251+
when(stmt.getConnection()).thenReturn(conn);
252+
when(conn.getMetaData()).thenReturn(dbmd);
253+
when(dbmd.supportsMultipleResultSets()).thenReturn(false); // for simplicity.
254+
255+
return fastResultSetHandler;
256+
}
257+
258+
static class ResultClassNoArgsConstructor {
259+
private int id;
260+
private String name;
261+
private String gender;
262+
263+
public void setId(int id) {
264+
this.id = id;
265+
}
266+
267+
public void setName(String name) {
268+
this.name = name;
269+
}
270+
271+
public void setGender(String gender) {
272+
this.gender = gender;
273+
}
274+
}
275+
276+
static class ResultClassNoArgsAndAllArgsConstructor {
277+
private int id;
278+
private String name;
279+
private String gender;
280+
281+
public ResultClassNoArgsAndAllArgsConstructor() {
282+
}
283+
284+
public ResultClassNoArgsAndAllArgsConstructor(int id, String name) {
285+
this.id = id;
286+
this.name = name;
287+
}
288+
289+
public void setId(int id) {
290+
this.id = id;
291+
}
292+
293+
public void setName(String name) {
294+
this.name = name;
295+
}
296+
297+
public void setGender(String gender) {
298+
this.gender = gender;
299+
}
300+
}
301+
302+
static class ResultClassOnlyHasAllArgsConstructor {
303+
private int id;
304+
private String name;
305+
private String gender;
306+
307+
public ResultClassOnlyHasAllArgsConstructor(int id, String name, String gender) {
308+
this.id = id;
309+
this.name = name;
310+
this.gender = gender;
311+
}
312+
313+
public void setId(int id) {
314+
this.id = id;
315+
}
316+
317+
public void setName(String name) {
318+
this.name = name;
319+
}
320+
321+
public void setGender(String gender) {
322+
this.gender = gender;
323+
}
324+
}
151325
}

0 commit comments

Comments
 (0)