Skip to content

Commit f7e6d98

Browse files
authored
Merge pull request #3327 from luozongle01/fixIndexOutOfBoundsException20241226
Throw helpful error instead of IndexOutOfBoundsException when there are not enough columns for constructor auto-mapping
2 parents 3e54e62 + f6c28a6 commit f7e6d98

File tree

10 files changed

+531
-0
lines changed

10 files changed

+531
-0
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,13 @@ private Object applyConstructorAutomapping(ResultSetWrapper rsw, ResultMap resul
783783
private boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw, List<Class<?>> constructorArgTypes,
784784
List<Object> constructorArgs, Constructor<?> constructor, boolean foundValues) throws SQLException {
785785
Class<?>[] parameterTypes = constructor.getParameterTypes();
786+
787+
if (parameterTypes.length > rsw.getClassNames().size()) {
788+
throw new ExecutorException(MessageFormat.format(
789+
"Constructor auto-mapping of ''{0}'' failed. The constructor takes ''{1}'' arguments, but there are only ''{2}'' columns in the result set.",
790+
constructor, parameterTypes.length, rsw.getClassNames().size()));
791+
}
792+
786793
for (int i = 0; i < parameterTypes.length; i++) {
787794
Class<?> parameterType = parameterTypes[i];
788795
String columnName = rsw.getColumnNames().get(i);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright 2009-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.column_order_based_constructor_automapping;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertNotNull;
20+
import static org.junit.jupiter.api.Assertions.assertNull;
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
24+
import java.io.Reader;
25+
import java.text.MessageFormat;
26+
import java.util.List;
27+
28+
import org.apache.ibatis.BaseDataTest;
29+
import org.apache.ibatis.exceptions.PersistenceException;
30+
import org.apache.ibatis.io.Resources;
31+
import org.apache.ibatis.session.SqlSession;
32+
import org.apache.ibatis.session.SqlSessionFactory;
33+
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
34+
import org.junit.jupiter.api.BeforeAll;
35+
import org.junit.jupiter.api.Test;
36+
37+
class ColumnOrderBasedConstructorAutomappingTest {
38+
39+
private static SqlSessionFactory sqlSessionFactory;
40+
41+
@BeforeAll
42+
static void setUp() throws Exception {
43+
// create an SqlSessionFactory
44+
try (Reader reader = Resources.getResourceAsReader(
45+
"org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml")) {
46+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
47+
sqlSessionFactory.getConfiguration().setArgNameBasedConstructorAutoMapping(false);
48+
}
49+
50+
// populate in-memory database
51+
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
52+
"org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql");
53+
}
54+
55+
@Test
56+
void shouldHandleNoArgsConstructor() {
57+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
58+
Mapper mapper = sqlSession.getMapper(Mapper.class);
59+
List<UserNoArgsConstructor> userList = mapper.finaAllByNoArgsConstructor();
60+
61+
assertEquals(2, userList.size());
62+
UserNoArgsConstructor user1 = userList.get(0);
63+
UserNoArgsConstructor user2 = userList.get(1);
64+
65+
assertEquals(1, user1.getId());
66+
assertEquals("Tom", user1.getName());
67+
assertEquals(7, user1.getAge());
68+
assertNull(user1.getEmail());
69+
70+
assertEquals(2, user2.getId());
71+
assertEquals("Cat", user2.getName());
72+
assertEquals(3, user2.getAge());
73+
assertNull(user2.getEmail());
74+
}
75+
}
76+
77+
@Test
78+
void shouldHandleConstructorEqualsResultSet() {
79+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
80+
Mapper mapper = sqlSession.getMapper(Mapper.class);
81+
List<UserConstructorEqualsResultSet> userList = mapper.finaAllByConstructorEqualsResultSet();
82+
83+
assertEquals(2, userList.size());
84+
UserConstructorEqualsResultSet user1 = userList.get(0);
85+
UserConstructorEqualsResultSet user2 = userList.get(1);
86+
87+
assertEquals(1, user1.getId());
88+
assertEquals("Tom", user1.getName());
89+
assertEquals(7, user1.getAge());
90+
assertNull(user1.getEmail());
91+
92+
assertEquals(2, user2.getId());
93+
assertEquals("Cat", user2.getName());
94+
assertEquals(3, user2.getAge());
95+
assertNull(user2.getEmail());
96+
}
97+
}
98+
99+
@Test
100+
void shouldHandleConstructorLessThanResultSet() {
101+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
102+
Mapper mapper = sqlSession.getMapper(Mapper.class);
103+
List<UserConstructorLessThanResultSet> userList = mapper.finaAllByConstructorLessThanResultSet();
104+
105+
assertEquals(2, userList.size());
106+
UserConstructorLessThanResultSet user1 = userList.get(0);
107+
UserConstructorLessThanResultSet user2 = userList.get(1);
108+
109+
assertEquals(1, user1.getId());
110+
assertEquals("Tom", user1.getName());
111+
assertEquals(7, user1.getAge());
112+
assertNull(user1.getEmail());
113+
114+
assertEquals(2, user2.getId());
115+
assertEquals("Cat", user2.getName());
116+
assertEquals(3, user2.getAge());
117+
assertNull(user2.getEmail());
118+
}
119+
}
120+
121+
@Test
122+
void shouldNotHandleConstructorGreaterThanResultSet() {
123+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
124+
Mapper mapper = sqlSession.getMapper(Mapper.class);
125+
126+
PersistenceException persistenceException = assertThrows(PersistenceException.class,
127+
mapper::finaAllByConstructorGreaterThanResultSet);
128+
assertNotNull(persistenceException);
129+
String message = persistenceException.getMessage();
130+
assertNotNull(message);
131+
assertTrue(message.contains(MessageFormat.format(
132+
"Constructor auto-mapping of ''{0}'' failed. The constructor takes ''{1}'' arguments, but there are only ''{2}'' columns in the result set.",
133+
UserConstructorGreaterThanResultSet.class.getConstructors()[0], 4, 3)));
134+
}
135+
}
136+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2009-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.column_order_based_constructor_automapping;
17+
18+
import java.util.List;
19+
20+
public interface Mapper {
21+
22+
List<UserNoArgsConstructor> finaAllByNoArgsConstructor();
23+
24+
List<UserConstructorEqualsResultSet> finaAllByConstructorEqualsResultSet();
25+
26+
List<UserConstructorLessThanResultSet> finaAllByConstructorLessThanResultSet();
27+
28+
List<UserConstructorGreaterThanResultSet> finaAllByConstructorGreaterThanResultSet();
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2009-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.column_order_based_constructor_automapping;
17+
18+
public class UserConstructorEqualsResultSet {
19+
20+
private Integer id;
21+
private String name;
22+
private Integer age;
23+
private String email;
24+
25+
public UserConstructorEqualsResultSet(Integer id, String name, Integer age) {
26+
this.id = id;
27+
this.name = name;
28+
this.age = age;
29+
}
30+
31+
public Integer getId() {
32+
return id;
33+
}
34+
35+
public void setId(Integer id) {
36+
this.id = id;
37+
}
38+
39+
public String getName() {
40+
return name;
41+
}
42+
43+
public void setName(String name) {
44+
this.name = name;
45+
}
46+
47+
public Integer getAge() {
48+
return age;
49+
}
50+
51+
public void setAge(Integer age) {
52+
this.age = age;
53+
}
54+
55+
public String getEmail() {
56+
return email;
57+
}
58+
59+
public void setEmail(String email) {
60+
this.email = email;
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2009-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.column_order_based_constructor_automapping;
17+
18+
public class UserConstructorGreaterThanResultSet {
19+
20+
private Integer id;
21+
private String name;
22+
private Integer age;
23+
private String email;
24+
25+
public UserConstructorGreaterThanResultSet(Integer id, String name, Integer age, String email) {
26+
this.id = id;
27+
this.name = name;
28+
this.age = age;
29+
this.email = email;
30+
}
31+
32+
public Integer getId() {
33+
return id;
34+
}
35+
36+
public void setId(Integer id) {
37+
this.id = id;
38+
}
39+
40+
public String getName() {
41+
return name;
42+
}
43+
44+
public void setName(String name) {
45+
this.name = name;
46+
}
47+
48+
public Integer getAge() {
49+
return age;
50+
}
51+
52+
public void setAge(Integer age) {
53+
this.age = age;
54+
}
55+
56+
public String getEmail() {
57+
return email;
58+
}
59+
60+
public void setEmail(String email) {
61+
this.email = email;
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2009-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.column_order_based_constructor_automapping;
17+
18+
public class UserConstructorLessThanResultSet {
19+
20+
private Integer id;
21+
private String name;
22+
private Integer age;
23+
private String email;
24+
25+
public UserConstructorLessThanResultSet(Integer id, String name) {
26+
this.id = id;
27+
this.name = name;
28+
}
29+
30+
public Integer getId() {
31+
return id;
32+
}
33+
34+
public void setId(Integer id) {
35+
this.id = id;
36+
}
37+
38+
public String getName() {
39+
return name;
40+
}
41+
42+
public void setName(String name) {
43+
this.name = name;
44+
}
45+
46+
public Integer getAge() {
47+
return age;
48+
}
49+
50+
public void setAge(Integer age) {
51+
this.age = age;
52+
}
53+
54+
public String getEmail() {
55+
return email;
56+
}
57+
58+
public void setEmail(String email) {
59+
this.email = email;
60+
}
61+
}

0 commit comments

Comments
 (0)