From c29ce3343d2116e243a6e5c1378ac3797b48309a Mon Sep 17 00:00:00 2001 From: luozongle01 Date: Fri, 27 Dec 2024 15:09:51 +0800 Subject: [PATCH 1/2] Fix IndexOutOfBoundsException --- .../resultset/DefaultResultSetHandler.java | 8 ++ ...nOrderBasedConstructorAutomappingTest.java | 129 ++++++++++++++++++ .../Mapper.java | 29 ++++ .../UserConstructorEqualsResultSet.java | 62 +++++++++ .../UserConstructorGreaterThanResultSet.java | 63 +++++++++ .../UserConstructorLessThanResultSet.java | 61 +++++++++ .../UserNoArgsConstructor.java | 59 ++++++++ .../CreateDB.sql | 27 ++++ .../Mapper.xml | 45 ++++++ .../mybatis-config.xml | 42 ++++++ 10 files changed, 525 insertions(+) create mode 100644 src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java create mode 100644 src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.java create mode 100644 src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorEqualsResultSet.java create mode 100644 src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorGreaterThanResultSet.java create mode 100644 src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorLessThanResultSet.java create mode 100644 src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserNoArgsConstructor.java create mode 100644 src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql create mode 100644 src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.xml create mode 100644 src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml diff --git a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java index 8cdc29d6377..72379fa6ac4 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -783,6 +783,14 @@ private Object applyConstructorAutomapping(ResultSetWrapper rsw, ResultMap resul private boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw, List> constructorArgTypes, List constructorArgs, Constructor constructor, boolean foundValues) throws SQLException { Class[] parameterTypes = constructor.getParameterTypes(); + + // constructor parameter is allowed to be less than or equal to the number of result set columns, but not greater than + if (parameterTypes.length > rsw.getClassNames().size()) { + throw new ExecutorException(MessageFormat.format( + "Column order based constructor auto-mapping of ''{0}'' failed. Because result set type is ''{1}''.", + constructor, rsw.getClassNames())); + } + for (int i = 0; i < parameterTypes.length; i++) { Class parameterType = parameterTypes[i]; String columnName = rsw.getColumnNames().get(i); diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java new file mode 100644 index 00000000000..30c914d97e4 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2009-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.column_order_based_constructor_automapping; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.Reader; +import java.util.List; +import org.apache.ibatis.BaseDataTest; +import org.apache.ibatis.exceptions.PersistenceException; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class ColumnOrderBasedConstructorAutomappingTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeAll + static void setUp() throws Exception { + // create an SqlSessionFactory + try (Reader reader = Resources.getResourceAsReader( + "org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml")) { + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + sqlSessionFactory.getConfiguration().setArgNameBasedConstructorAutoMapping(false); + } + + // populate in-memory database + BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), + "org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql"); + } + + @Test + void shouldHandleNoArgsConstructor() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + List userList = mapper.finaAllByNoArgsConstructor(); + + assertEquals(2, userList.size()); + UserNoArgsConstructor user1 = userList.get(0); + UserNoArgsConstructor user2 = userList.get(1); + + assertEquals(1, user1.getId()); + assertEquals("Tom", user1.getName()); + assertEquals(7, user1.getAge()); + assertNull(user1.getEmail()); + + assertEquals(2, user2.getId()); + assertEquals("Cat", user2.getName()); + assertEquals(3, user2.getAge()); + assertNull(user2.getEmail()); + } + } + + @Test + void shouldHandleConstructorEqualsResultSet() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + List userList = mapper.finaAllByConstructorEqualsResultSet(); + + assertEquals(2, userList.size()); + UserConstructorEqualsResultSet user1 = userList.get(0); + UserConstructorEqualsResultSet user2 = userList.get(1); + + assertEquals(1, user1.getId()); + assertEquals("Tom", user1.getName()); + assertEquals(7, user1.getAge()); + assertNull(user1.getEmail()); + + assertEquals(2, user2.getId()); + assertEquals("Cat", user2.getName()); + assertEquals(3, user2.getAge()); + assertNull(user2.getEmail()); + } + } + + @Test + void shouldHandleConstructorLessThanResultSet() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + List userList = mapper.finaAllByConstructorLessThanResultSet(); + + assertEquals(2, userList.size()); + UserConstructorLessThanResultSet user1 = userList.get(0); + UserConstructorLessThanResultSet user2 = userList.get(1); + + assertEquals(1, user1.getId()); + assertEquals("Tom", user1.getName()); + assertEquals(7, user1.getAge()); + assertNull(user1.getEmail()); + + assertEquals(2, user2.getId()); + assertEquals("Cat", user2.getName()); + assertEquals(3, user2.getAge()); + assertNull(user2.getEmail()); + } + } + + @Test + void shouldNotHandleConstructorGreaterThanResultSet() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + + PersistenceException persistenceException = assertThrows(PersistenceException.class, mapper::finaAllByConstructorGreaterThanResultSet); + assertNotNull(persistenceException); + assertNotNull(persistenceException.getMessage()); + assertTrue(persistenceException.getMessage().contains("Column order based constructor auto-mapping of")); + } + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.java new file mode 100644 index 00000000000..0d97a14bf8e --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.java @@ -0,0 +1,29 @@ +/* + * Copyright 2009-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.column_order_based_constructor_automapping; + +import java.util.List; + +public interface Mapper { + + List finaAllByNoArgsConstructor(); + + List finaAllByConstructorEqualsResultSet(); + + List finaAllByConstructorLessThanResultSet(); + + List finaAllByConstructorGreaterThanResultSet(); +} diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorEqualsResultSet.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorEqualsResultSet.java new file mode 100644 index 00000000000..198af44607a --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorEqualsResultSet.java @@ -0,0 +1,62 @@ +/* + * Copyright 2009-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.column_order_based_constructor_automapping; + +public class UserConstructorEqualsResultSet { + + private Integer id; + private String name; + private Integer age; + private String email; + + public UserConstructorEqualsResultSet(Integer id, String name, Integer age) { + this.id = id; + this.name = name; + this.age = age; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorGreaterThanResultSet.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorGreaterThanResultSet.java new file mode 100644 index 00000000000..cd039c1748d --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorGreaterThanResultSet.java @@ -0,0 +1,63 @@ +/* + * Copyright 2009-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.column_order_based_constructor_automapping; + +public class UserConstructorGreaterThanResultSet { + + private Integer id; + private String name; + private Integer age; + private String email; + + public UserConstructorGreaterThanResultSet(Integer id, String name, Integer age, String email) { + this.id = id; + this.name = name; + this.age = age; + this.email = email; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorLessThanResultSet.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorLessThanResultSet.java new file mode 100644 index 00000000000..39c21a65ba4 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserConstructorLessThanResultSet.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.column_order_based_constructor_automapping; + +public class UserConstructorLessThanResultSet { + + private Integer id; + private String name; + private Integer age; + private String email; + + public UserConstructorLessThanResultSet(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserNoArgsConstructor.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserNoArgsConstructor.java new file mode 100644 index 00000000000..757dab9d965 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/UserNoArgsConstructor.java @@ -0,0 +1,59 @@ +/* + * Copyright 2009-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.column_order_based_constructor_automapping; + +public class UserNoArgsConstructor { + + private Integer id; + private String name; + private Integer age; + private String email; + + public UserNoArgsConstructor() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql b/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql new file mode 100644 index 00000000000..19140d3c768 --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql @@ -0,0 +1,27 @@ +-- +-- Copyright 2009-2024 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +drop table users if exists; + +create table users ( + id int, + name varchar(50), + age int +); + +insert into users (id, name, age) values +(1, 'Tom', 7), +(2, 'Cat', 3); diff --git a/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.xml b/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.xml new file mode 100644 index 00000000000..995ca6a6a2e --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/Mapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml b/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml new file mode 100644 index 00000000000..cdc9bff4302 --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f6c28a6c0d1d7946e004db41188a410a02d4b55c Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 27 Dec 2024 20:28:50 +0900 Subject: [PATCH 2/2] Put the direct cause in the error message And let the maven plugins do the cleanup. --- .../resultset/DefaultResultSetHandler.java | 5 ++--- ...umnOrderBasedConstructorAutomappingTest.java | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java index 72379fa6ac4..c089302a254 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -784,11 +784,10 @@ private boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw List constructorArgs, Constructor constructor, boolean foundValues) throws SQLException { Class[] parameterTypes = constructor.getParameterTypes(); - // constructor parameter is allowed to be less than or equal to the number of result set columns, but not greater than if (parameterTypes.length > rsw.getClassNames().size()) { throw new ExecutorException(MessageFormat.format( - "Column order based constructor auto-mapping of ''{0}'' failed. Because result set type is ''{1}''.", - constructor, rsw.getClassNames())); + "Constructor auto-mapping of ''{0}'' failed. The constructor takes ''{1}'' arguments, but there are only ''{2}'' columns in the result set.", + constructor, parameterTypes.length, rsw.getClassNames().size())); } for (int i = 0; i < parameterTypes.length; i++) { diff --git a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java index 30c914d97e4..f8fb4317965 100644 --- a/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java +++ b/src/test/java/org/apache/ibatis/submitted/column_order_based_constructor_automapping/ColumnOrderBasedConstructorAutomappingTest.java @@ -20,8 +20,11 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.Reader; +import java.text.MessageFormat; import java.util.List; + import org.apache.ibatis.BaseDataTest; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.io.Resources; @@ -39,14 +42,14 @@ class ColumnOrderBasedConstructorAutomappingTest { static void setUp() throws Exception { // create an SqlSessionFactory try (Reader reader = Resources.getResourceAsReader( - "org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml")) { + "org/apache/ibatis/submitted/column_order_based_constructor_automapping/mybatis-config.xml")) { sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); sqlSessionFactory.getConfiguration().setArgNameBasedConstructorAutoMapping(false); } // populate in-memory database BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), - "org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql"); + "org/apache/ibatis/submitted/column_order_based_constructor_automapping/CreateDB.sql"); } @Test @@ -120,10 +123,14 @@ void shouldNotHandleConstructorGreaterThanResultSet() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { Mapper mapper = sqlSession.getMapper(Mapper.class); - PersistenceException persistenceException = assertThrows(PersistenceException.class, mapper::finaAllByConstructorGreaterThanResultSet); + PersistenceException persistenceException = assertThrows(PersistenceException.class, + mapper::finaAllByConstructorGreaterThanResultSet); assertNotNull(persistenceException); - assertNotNull(persistenceException.getMessage()); - assertTrue(persistenceException.getMessage().contains("Column order based constructor auto-mapping of")); + String message = persistenceException.getMessage(); + assertNotNull(message); + assertTrue(message.contains(MessageFormat.format( + "Constructor auto-mapping of ''{0}'' failed. The constructor takes ''{1}'' arguments, but there are only ''{2}'' columns in the result set.", + UserConstructorGreaterThanResultSet.class.getConstructors()[0], 4, 3))); } } }