From 451440bfc142e986b0ba241c51066a5280883376 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Fri, 25 Oct 2019 01:01:08 +0900 Subject: [PATCH] Change to refers useColumnLabel at UnknownTypeHandler Fixes gh-1551 --- .../apache/ibatis/session/Configuration.java | 2 +- .../ibatis/type/TypeHandlerRegistry.java | 18 +++- .../ibatis/type/UnknownTypeHandler.java | 36 ++++++-- .../nestedresulthandler_gh1551/CreateDB.sql | 44 ++++++++++ .../NestedResultHandlerGh1551Test.java | 82 +++++++++++++++++++ .../ProductInfo.java | 48 +++++++++++ .../ProductMapper.java | 24 ++++++ .../ProductMapper.xml | 54 ++++++++++++ .../ProductResp.java | 69 ++++++++++++++++ .../ProductSku.java | 57 +++++++++++++ .../mybatis-config.xml | 48 +++++++++++ .../ibatis/type/UnknownTypeHandlerTest.java | 9 +- 12 files changed, 478 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/CreateDB.sql create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/NestedResultHandlerGh1551Test.java create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductInfo.java create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.java create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.xml create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductResp.java create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductSku.java create mode 100644 src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/mybatis-config.xml diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index ac92206c593..3bd9b7a5b2a 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -146,7 +146,7 @@ public class Configuration { protected final MapperRegistry mapperRegistry = new MapperRegistry(this); protected final InterceptorChain interceptorChain = new InterceptorChain(); - protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(); + protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this); protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry(); diff --git a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java index ba917d30beb..73d8783d064 100644 --- a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java +++ b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java @@ -46,6 +46,7 @@ import org.apache.ibatis.binding.MapperMethod.ParamMap; import org.apache.ibatis.io.ResolverUtil; import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.Configuration; /** * @author Clinton Begin @@ -55,14 +56,29 @@ public final class TypeHandlerRegistry { private final Map> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); private final Map>> typeHandlerMap = new ConcurrentHashMap<>(); - private final TypeHandler unknownTypeHandler = new UnknownTypeHandler(this); + private final TypeHandler unknownTypeHandler; private final Map, TypeHandler> allTypeHandlersMap = new HashMap<>(); private static final Map> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); private Class defaultEnumTypeHandler = EnumTypeHandler.class; + /** + * The default constructor. + */ public TypeHandlerRegistry() { + this(new Configuration()); + } + + /** + * The constructor that pass the MyBatis configuration. + * + * @param configuration a MyBatis configuration + * @since 3.5.4 + */ + public TypeHandlerRegistry(Configuration configuration) { + this.unknownTypeHandler = new UnknownTypeHandler(configuration); + register(Boolean.class, new BooleanTypeHandler()); register(boolean.class, new BooleanTypeHandler()); register(JdbcType.BOOLEAN, new BooleanTypeHandler()); diff --git a/src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java b/src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java index a930e80fa7c..303ddaa0220 100644 --- a/src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java +++ b/src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java @@ -22,8 +22,10 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.Configuration; /** * @author Clinton Begin @@ -31,11 +33,30 @@ public class UnknownTypeHandler extends BaseTypeHandler { private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler(); + private final Configuration configuration; + private final Supplier typeHandlerRegistrySupplier; - private TypeHandlerRegistry typeHandlerRegistry; + /** + * The constructor that pass a MyBatis configuration. + * + * @param configuration a MyBatis configuration + * @since 3.5.4 + */ + public UnknownTypeHandler(Configuration configuration) { + this.configuration = configuration; + this.typeHandlerRegistrySupplier = configuration::getTypeHandlerRegistry; + } + /** + * The constructor that pass the type handler registry. + * + * @param typeHandlerRegistry a type handler registry + * @deprecated Since 3.5.4, please use the {@link #UnknownTypeHandler(Configuration)}. + */ + @Deprecated public UnknownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) { - this.typeHandlerRegistry = typeHandlerRegistry; + this.configuration = new Configuration(); + this.typeHandlerRegistrySupplier = () -> typeHandlerRegistry; } @Override @@ -73,7 +94,7 @@ private TypeHandler resolveTypeHandler(Object parameter, JdbcType jdbcType) { if (parameter == null) { handler = OBJECT_TYPE_HANDLER; } else { - handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType); + handler = typeHandlerRegistrySupplier.get().getTypeHandler(parameter.getClass(), jdbcType); // check if handler is null (issue #270) if (handler == null || handler instanceof UnknownTypeHandler) { handler = OBJECT_TYPE_HANDLER; @@ -88,8 +109,9 @@ private TypeHandler resolveTypeHandler(ResultSet rs, String column) { columnIndexLookup = new HashMap<>(); ResultSetMetaData rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); + boolean useColumnLabel = configuration.isUseColumnLabel(); for (int i = 1; i <= count; i++) { - String name = rsmd.getColumnName(i); + String name = useColumnLabel ? rsmd.getColumnLabel(i) : rsmd.getColumnName(i); columnIndexLookup.put(name,i); } Integer columnIndex = columnIndexLookup.get(column); @@ -111,11 +133,11 @@ private TypeHandler resolveTypeHandler(ResultSetMetaData rsmd, Integer column JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex); Class javaType = safeGetClassForColumn(rsmd, columnIndex); if (javaType != null && jdbcType != null) { - handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType); + handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType, jdbcType); } else if (javaType != null) { - handler = typeHandlerRegistry.getTypeHandler(javaType); + handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType); } else if (jdbcType != null) { - handler = typeHandlerRegistry.getTypeHandler(jdbcType); + handler = typeHandlerRegistrySupplier.get().getTypeHandler(jdbcType); } return handler; } diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/CreateDB.sql new file mode 100644 index 00000000000..3154f931a73 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/CreateDB.sql @@ -0,0 +1,44 @@ +-- +-- Copyright 2009-2019 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 +-- +-- http://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 product_sku if exists; +drop table product_info if exists; +drop table product if exists; + +create table product ( + id varchar(32) not null, + code varchar(80) not null, + name varchar(240) not null +); + +create table product_sku ( + id varchar(32) not null, + product_id varchar(32) not null, + code varchar(80) not null, + color varchar(40), + size varchar(40) +); + +create table product_info ( + id int not null, + product_id varchar(32) not null, + other_info varchar(240) +); + +insert into product(id, code, name) values('10000000000000000000000000000001', 'P001', 'Product 001'); +insert into product_sku(id ,product_id, code, color, size) values('20000000000000000000000000000001', '10000000000000000000000000000001', 'S001', 'red', '80'); +insert into product_sku(id ,product_id, code, color, size) values('20000000000000000000000000000002', '10000000000000000000000000000001', 'S001', 'blue', '10'); +insert into product_info(id, product_id, other_info) values(1, '10000000000000000000000000000001', 'Sale 50% Off'); diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/NestedResultHandlerGh1551Test.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/NestedResultHandlerGh1551Test.java new file mode 100644 index 00000000000..6676f503b01 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/NestedResultHandlerGh1551Test.java @@ -0,0 +1,82 @@ +/** + * Copyright 2009-2019 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 + * + * http://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.nestedresulthandler_gh1551; + +import java.io.Reader; + +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.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class NestedResultHandlerGh1551Test { + private static SqlSessionFactory sqlSessionFactory; + + @BeforeAll + static void setUp() throws Exception { + // create a SqlSessionFactory + try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/nestedresulthandler_gh1551/mybatis-config.xml")) { + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + } + + // populate in-memory database + BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), + "org/apache/ibatis/submitted/nestedresulthandler_gh1551/CreateDB.sql"); + } + + @Test + void useColumnLabelIsTrue() { + sqlSessionFactory.getConfiguration().setUseColumnLabel(true); + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + ProductMapper mapper = sqlSession.getMapper(ProductMapper.class); + + ProductResp productResp = mapper.selectAllInfo("P001").get(0); + + Assertions.assertEquals("10000000000000000000000000000001", productResp.getId()); + Assertions.assertEquals("P001", productResp.getCode()); + Assertions.assertEquals("Product 001", productResp.getName()); + + Assertions.assertEquals(1, productResp.getProductInfo().getId()); + Assertions.assertEquals("10000000000000000000000000000001", productResp.getProductInfo().getProductId()); + Assertions.assertEquals("Sale 50% Off", productResp.getProductInfo().getOtherInfo()); + + Assertions.assertEquals("20000000000000000000000000000001", productResp.getSkus().get(0).getId()); + Assertions.assertEquals("10000000000000000000000000000001", productResp.getSkus().get(0).getProductId()); + Assertions.assertEquals("red", productResp.getSkus().get(0).getColor()); + Assertions.assertEquals("80", productResp.getSkus().get(0).getSize()); + Assertions.assertEquals("20000000000000000000000000000002", productResp.getSkus().get(1).getId()); + Assertions.assertEquals("10000000000000000000000000000001", productResp.getSkus().get(1).getProductId()); + Assertions.assertEquals("blue", productResp.getSkus().get(1).getColor()); + Assertions.assertEquals("10", productResp.getSkus().get(1).getSize()); + } + } + + @Test + void useColumnLabelIsFalse() { + sqlSessionFactory.getConfiguration().setUseColumnLabel(false); + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + ProductMapper mapper = sqlSession.getMapper(ProductMapper.class); + PersistenceException exception = Assertions.assertThrows(PersistenceException.class, () -> mapper.selectAllInfo("P001")); + Assertions.assertTrue(exception.getMessage().contains("Error attempting to get column 'ID' from result set. Cause: java.sql.SQLSyntaxErrorException: incompatible data type in conversion: from SQL type VARCHAR to java.lang.Integer, value: 10000000000000000000000000000001")); + } + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductInfo.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductInfo.java new file mode 100644 index 00000000000..e8043cfdd56 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductInfo.java @@ -0,0 +1,48 @@ +/** + * Copyright 2009-2019 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 + * + * http://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.nestedresulthandler_gh1551; + +public class ProductInfo { + + private Long id; + private String productId; + private String otherInfo; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public String getOtherInfo() { + return otherInfo; + } + + public void setOtherInfo(String otherInfo) { + this.otherInfo = otherInfo; + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.java new file mode 100644 index 00000000000..8857b464c73 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.java @@ -0,0 +1,24 @@ +/** + * Copyright 2009-2019 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 + * + * http://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.nestedresulthandler_gh1551; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; + +public interface ProductMapper { + List selectAllInfo(@Param("code") String code); +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.xml b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.xml new file mode 100644 index 00000000000..ae5af67d98c --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductMapper.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductResp.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductResp.java new file mode 100644 index 00000000000..c9e4986ef79 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductResp.java @@ -0,0 +1,69 @@ +/** + * Copyright 2009-2019 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 + * + * http://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.nestedresulthandler_gh1551; + +import java.util.List; + +public class ProductResp { + + private String id; + private String code; + private String name; + private List skus; + private ProductInfo productInfo; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getSkus() { + return skus; + } + + public void setSkus(List skus) { + this.skus = skus; + } + + public ProductInfo getProductInfo() { + return productInfo; + } + + public void setProductInfo(ProductInfo productInfo) { + this.productInfo = productInfo; + } + +} + diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductSku.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductSku.java new file mode 100644 index 00000000000..b5fe2b5c45b --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/ProductSku.java @@ -0,0 +1,57 @@ +/** + * Copyright 2009-2019 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 + * + * http://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.nestedresulthandler_gh1551; + +public class ProductSku { + + private String id; + private String color; + private String size; + private String productId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/mybatis-config.xml b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/mybatis-config.xml new file mode 100644 index 00000000000..289ac4b85da --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler_gh1551/mybatis-config.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java b/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java index 2cae1a1696d..13049c6772b 100644 --- a/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java +++ b/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java @@ -22,12 +22,13 @@ import static org.mockito.Mockito.*; import org.apache.ibatis.executor.result.ResultMapException; +import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class UnknownTypeHandlerTest extends BaseTypeHandlerTest { - private static final TypeHandler TYPE_HANDLER = spy(new UnknownTypeHandler(new TypeHandlerRegistry())); + private static final TypeHandler TYPE_HANDLER = spy(new UnknownTypeHandler(new Configuration())); @Override @Test @@ -41,7 +42,7 @@ public void shouldSetParameter() throws Exception { public void shouldGetResultFromResultSetByName() throws Exception { when(rs.getMetaData()).thenReturn(rsmd); when(rsmd.getColumnCount()).thenReturn(1); - when(rsmd.getColumnName(1)).thenReturn("column"); + when(rsmd.getColumnLabel(1)).thenReturn("column"); when(rsmd.getColumnClassName(1)).thenReturn(String.class.getName()); when(rsmd.getColumnType(1)).thenReturn(JdbcType.VARCHAR.TYPE_CODE); when(rs.getString("column")).thenReturn("Hello"); @@ -102,7 +103,7 @@ void setParameterWithNullParameterThrowsException() throws SQLException { @Test void setParameterWithNonNullParameterThrowsException() throws SQLException { - doThrow(new SQLException("invalid column")).when((UnknownTypeHandler)TYPE_HANDLER).setNonNullParameter(ps, 1, 99, JdbcType.INTEGER); + doThrow(new SQLException("invalid column")).when((UnknownTypeHandler) TYPE_HANDLER).setNonNullParameter(ps, 1, 99, JdbcType.INTEGER); try { TYPE_HANDLER.setParameter(ps, 1, 99, JdbcType.INTEGER); Assertions.fail("Should have thrown a TypeException"); @@ -148,4 +149,4 @@ void getResultWithCallableStatementAndColumnIndexThrowsException() throws SQLExc } } -} \ No newline at end of file +}