From 34dc7332ea63c9cade5d07cb508b8121da4b8799 Mon Sep 17 00:00:00 2001 From: Ryan Lamore <rlamore@salesforce.com> Date: Fri, 27 Mar 2015 12:59:42 -0700 Subject: [PATCH] Added better error reporting to help debug issues with mapper files --- .../executor/result/ResultMapException.java | 40 +++++++++ .../resultset/DefaultResultSetHandler.java | 27 +++--- .../ibatis/mapping/ParameterMapping.java | 16 ++++ .../apache/ibatis/mapping/ResultMapping.java | 22 +++++ .../factory/DefaultObjectFactory.java | 8 +- .../defaults/DefaultParameterHandler.java | 11 ++- .../apache/ibatis/type/BaseTypeHandler.java | 30 ++++++- .../DefaultResultSetHandlerTest.java | 68 +++++++++++---- .../factory/DefaultObjectFactoryTest.java | 55 ++++++++++++ .../ibatis/reflection/factory/TestClass.java | 31 +++++++ .../defaults/DefaultParameterHandlerTest.java | 87 +++++++++++++++++++ .../ibatis/type/UnknownTypeHandlerTest.java | 74 +++++++++++++++- 12 files changed, 431 insertions(+), 38 deletions(-) create mode 100644 src/main/java/org/apache/ibatis/executor/result/ResultMapException.java create mode 100644 src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java create mode 100644 src/test/java/org/apache/ibatis/reflection/factory/TestClass.java create mode 100644 src/test/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandlerTest.java diff --git a/src/main/java/org/apache/ibatis/executor/result/ResultMapException.java b/src/main/java/org/apache/ibatis/executor/result/ResultMapException.java new file mode 100644 index 00000000000..d2c2bc0a9c2 --- /dev/null +++ b/src/main/java/org/apache/ibatis/executor/result/ResultMapException.java @@ -0,0 +1,40 @@ +/** + * Copyright 2009-2015 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.executor.result; + +import org.apache.ibatis.exceptions.PersistenceException; + +/** + * @author Ryan Lamore + */ +public class ResultMapException extends PersistenceException { + private static final long serialVersionUID = 3270932060569707623L; + + public ResultMapException() { + } + + public ResultMapException(String message) { + super(message); + } + + public ResultMapException(String message, Throwable cause) { + super(message, cause); + } + + public ResultMapException(Throwable cause) { + super(cause); + } +} 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 2556bc295c9..8343dc6333a 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -37,6 +37,7 @@ import org.apache.ibatis.executor.loader.ResultLoaderMap; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.result.DefaultResultContext; import org.apache.ibatis.executor.result.DefaultResultHandler; +import org.apache.ibatis.executor.result.ResultMapException; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.Discriminator; import org.apache.ibatis.mapping.MappedStatement; @@ -562,21 +563,27 @@ public class DefaultResultSetHandler implements ResultSetHandler { throw new ExecutorException("Do not know how to create an instance of " + resultType); } - private Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, - List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException { + Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, + List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) { boolean foundValues = false; for (ResultMapping constructorMapping : constructorMappings) { final Class<?> parameterType = constructorMapping.getJavaType(); final String column = constructorMapping.getColumn(); final Object value; - if (constructorMapping.getNestedQueryId() != null) { - value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix); - } else if (constructorMapping.getNestedResultMapId() != null) { - final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId()); - value = getRowValue(rsw, resultMap); - } else { - final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler(); - value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix)); + try { + if (constructorMapping.getNestedQueryId() != null) { + value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix); + } else if (constructorMapping.getNestedResultMapId() != null) { + final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId()); + value = getRowValue(rsw, resultMap); + } else { + final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler(); + value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix)); + } + } catch (ResultMapException e) { + throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e); + } catch (SQLException e) { + throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e); } constructorArgTypes.add(parameterType); constructorArgs.add(value); diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java index 13a6cd033d0..3d69a8912db 100644 --- a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java @@ -199,4 +199,20 @@ public class ParameterMapping { return expression; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ParameterMapping{"); + //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString() + sb.append("property='").append(property).append('\''); + sb.append(", mode=").append(mode); + sb.append(", javaType=").append(javaType); + sb.append(", jdbcType=").append(jdbcType); + sb.append(", numericScale=").append(numericScale); + //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString() + sb.append(", resultMapId='").append(resultMapId).append('\''); + sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\''); + sb.append(", expression='").append(expression).append('\''); + sb.append('}'); + return sb.toString(); + } } diff --git a/src/main/java/org/apache/ibatis/mapping/ResultMapping.java b/src/main/java/org/apache/ibatis/mapping/ResultMapping.java index cc6dc70b409..b1cc567c4ed 100644 --- a/src/main/java/org/apache/ibatis/mapping/ResultMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ResultMapping.java @@ -280,4 +280,26 @@ public class ResultMapping { } } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ResultMapping{"); + //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString() + sb.append("property='").append(property).append('\''); + sb.append(", column='").append(column).append('\''); + sb.append(", javaType=").append(javaType); + sb.append(", jdbcType=").append(jdbcType); + //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString() + sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\''); + sb.append(", nestedQueryId='").append(nestedQueryId).append('\''); + sb.append(", notNullColumns=").append(notNullColumns); + sb.append(", columnPrefix='").append(columnPrefix).append('\''); + sb.append(", flags=").append(flags); + sb.append(", composites=").append(composites); + sb.append(", resultSet='").append(resultSet).append('\''); + sb.append(", foreignColumn='").append(foreignColumn).append('\''); + sb.append(", lazy=").append(lazy); + sb.append('}'); + return sb.toString(); + } + } diff --git a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java index 277ec6275a4..cb7cdf9c8be 100644 --- a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java +++ b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java @@ -55,7 +55,7 @@ public class DefaultObjectFactory implements ObjectFactory, Serializable { // no props for default } - private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { + <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null) { @@ -72,18 +72,20 @@ public class DefaultObjectFactory implements ObjectFactory, Serializable { return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (Exception e) { StringBuilder argTypes = new StringBuilder(); - if (constructorArgTypes != null) { + if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) { for (Class<?> argType : constructorArgTypes) { argTypes.append(argType.getSimpleName()); argTypes.append(","); } + argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing , } StringBuilder argValues = new StringBuilder(); - if (constructorArgs != null) { + if (constructorArgs != null && !constructorArgs.isEmpty()) { for (Object argValue : constructorArgs) { argValues.append(String.valueOf(argValue)); argValues.append(","); } + argValues.deleteCharAt(argValues.length() - 1); // remove trailing , } throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); } diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java index 426f40c87ec..d1c720c92cd 100644 --- a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java +++ b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java @@ -28,6 +28,7 @@ import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.TypeException; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; @@ -58,7 +59,7 @@ public class DefaultParameterHandler implements ParameterHandler { } @Override - public void setParameters(PreparedStatement ps) throws SQLException { + public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { @@ -82,7 +83,13 @@ public class DefaultParameterHandler implements ParameterHandler { if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } - typeHandler.setParameter(ps, i + 1, value, jdbcType); + try { + typeHandler.setParameter(ps, i + 1, value, jdbcType); + } catch (TypeException e) { + throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); + } catch (SQLException e) { + throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); + } } } } diff --git a/src/main/java/org/apache/ibatis/type/BaseTypeHandler.java b/src/main/java/org/apache/ibatis/type/BaseTypeHandler.java index 7bc857324a1..43d9499943e 100644 --- a/src/main/java/org/apache/ibatis/type/BaseTypeHandler.java +++ b/src/main/java/org/apache/ibatis/type/BaseTypeHandler.java @@ -20,6 +20,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import org.apache.ibatis.executor.result.ResultMapException; import org.apache.ibatis.session.Configuration; /** @@ -48,13 +49,24 @@ public abstract class BaseTypeHandler<T> extends TypeReference<T> implements Typ "Cause: " + e, e); } } else { - setNonNullParameter(ps, i, parameter, jdbcType); + try { + setNonNullParameter(ps, i, parameter, jdbcType); + } catch (Exception e) { + throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + + "Try setting a different JdbcType for this parameter or a different configuration property. " + + "Cause: " + e, e); + } } } @Override public T getResult(ResultSet rs, String columnName) throws SQLException { - T result = getNullableResult(rs, columnName); + T result; + try { + result = getNullableResult(rs, columnName); + } catch (Exception e) { + throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); + } if (rs.wasNull()) { return null; } else { @@ -64,7 +76,12 @@ public abstract class BaseTypeHandler<T> extends TypeReference<T> implements Typ @Override public T getResult(ResultSet rs, int columnIndex) throws SQLException { - T result = getNullableResult(rs, columnIndex); + T result; + try { + result = getNullableResult(rs, columnIndex); + } catch (Exception e) { + throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set. Cause: " + e, e); + } if (rs.wasNull()) { return null; } else { @@ -74,7 +91,12 @@ public abstract class BaseTypeHandler<T> extends TypeReference<T> implements Typ @Override public T getResult(CallableStatement cs, int columnIndex) throws SQLException { - T result = getNullableResult(cs, columnIndex); + T result; + try { + result = getNullableResult(cs, columnIndex); + } catch (Exception e) { + throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement. Cause: " + e, e); + } if (cs.wasNull()) { return null; } else { diff --git a/src/test/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandlerTest.java b/src/test/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandlerTest.java index f9340f214ae..7c59c40cdb8 100644 --- a/src/test/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandlerTest.java +++ b/src/test/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandlerTest.java @@ -16,20 +16,20 @@ package org.apache.ibatis.executor.resultset; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.Statement; -import java.sql.Types; +import java.sql.*; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import org.apache.ibatis.builder.StaticSqlSource; import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.ExecutorException; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; @@ -39,7 +39,9 @@ import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; +import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -67,18 +69,7 @@ public class DefaultResultSetHandlerTest { @Test public void shouldRetainColumnNameCase() throws Exception { - final Configuration config = new Configuration(); - final TypeHandlerRegistry registry = config.getTypeHandlerRegistry(); - final MappedStatement ms = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps( - new ArrayList<ResultMap>() { - { - add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() { - { - add(new ResultMapping.Builder(config, "cOlUmN1", "CoLuMn1", registry.getTypeHandler(Integer.class)).build()); - } - }).build()); - } - }).build(); + final MappedStatement ms = getMappedStatement(); final Executor executor = null; final ParameterHandler parameterHandler = null; @@ -106,4 +97,45 @@ public class DefaultResultSetHandlerTest { assertEquals(Integer.valueOf(100), ((HashMap) results.get(0)).get("cOlUmN1")); } + @Test + public void shouldThrowExceptionWithColumnName() throws Exception { + final MappedStatement ms = getMappedStatement(); + final RowBounds rowBounds = new RowBounds(0, 100); + + final DefaultResultSetHandler defaultResultSetHandler = new DefaultResultSetHandler(null/*executor*/, ms, + null/*parameterHandler*/, null/*resultHandler*/, null/*boundSql*/, rowBounds); + + final ResultSetWrapper rsw = mock(ResultSetWrapper.class); + + final ResultMapping resultMapping = mock(ResultMapping.class); + final TypeHandler typeHandler = mock(TypeHandler.class); + when(resultMapping.getTypeHandler()).thenReturn(typeHandler); + when(typeHandler.getResult(any(ResultSet.class), anyString())).thenThrow(new SQLException("exception")); + List<ResultMapping> constructorMappings = Collections.singletonList(resultMapping); + + try { + defaultResultSetHandler.createParameterizedResultObject(rsw, null/*resultType*/, constructorMappings, + null/*constructorArgTypes*/, null/*constructorArgs*/, null/*columnPrefix*/); + Assert.fail("Should have thrown ExecutorException"); + } catch (Exception e) { + Assert.assertTrue("Expected ExecutorException", e instanceof ExecutorException); + Assert.assertTrue("", e.getMessage().contains("mapping: " + resultMapping.toString())); + } + } + + MappedStatement getMappedStatement() { + final Configuration config = new Configuration(); + final TypeHandlerRegistry registry = config.getTypeHandlerRegistry(); + return new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps( + new ArrayList<ResultMap>() { + { + add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() { + { + add(new ResultMapping.Builder(config, "cOlUmN1", "CoLuMn1", registry.getTypeHandler(Integer.class)).build()); + } + }).build()); + } + }).build(); + } + } diff --git a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java new file mode 100644 index 00000000000..1c5388dcd82 --- /dev/null +++ b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java @@ -0,0 +1,55 @@ +/** + * Copyright 2009-2015 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.reflection.factory; + +import java.util.Arrays; +import java.util.Collections; + +import org.apache.ibatis.reflection.ReflectionException; +import org.junit.Assert; +import org.junit.Test; + +/** + * DefaultObjectFactoryTest + * + * @author Ryan Lamore + */ +public class DefaultObjectFactoryTest { + + @Test + public void instantiateClass() throws Exception { + DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); + TestClass testClass = defaultObjectFactory.instantiateClass(TestClass.class, + Arrays.<Class<?>>asList(String.class, Integer.class), Arrays.<Object>asList("foo", 0)); + + Assert.assertEquals("myInteger didn't match expected", (Integer) 0, testClass.myInteger); + Assert.assertEquals("myString didn't match expected", "foo", testClass.myString); + } + + @Test + public void instantiateClassThrowsProperErrorMsg() { + DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); + try { + defaultObjectFactory.instantiateClass(TestClass.class, Collections.<Class<?>>singletonList(String.class), Collections.<Object>singletonList("foo")); + Assert.fail("Should have thrown ReflectionException"); + } catch (Exception e) { + Assert.assertTrue("Should be ReflectionException", e instanceof ReflectionException); + Assert.assertTrue("Should not have trailing commas in types list", e.getMessage().contains("(String)")); + Assert.assertTrue("Should not have trailing commas in values list", e.getMessage().contains("(foo)")); + } + } + +} diff --git a/src/test/java/org/apache/ibatis/reflection/factory/TestClass.java b/src/test/java/org/apache/ibatis/reflection/factory/TestClass.java new file mode 100644 index 00000000000..3af8666d8cf --- /dev/null +++ b/src/test/java/org/apache/ibatis/reflection/factory/TestClass.java @@ -0,0 +1,31 @@ +/** + * Copyright 2009-2015 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.reflection.factory; + +/** + * TestClass + * + * @author Ryan Lamore + */ +public class TestClass { + String myString; + Integer myInteger; + + public TestClass(String myString, Integer myInteger) { + this.myString = myString; + this.myInteger = myInteger; + } +} diff --git a/src/test/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandlerTest.java b/src/test/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandlerTest.java new file mode 100644 index 00000000000..ab21b445fdc --- /dev/null +++ b/src/test/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandlerTest.java @@ -0,0 +1,87 @@ +/** + * Copyright 2009-2015 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.scripting.defaults; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import org.apache.ibatis.builder.StaticSqlSource; +import org.apache.ibatis.mapping.*; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.TypeException; +import org.apache.ibatis.type.TypeHandler; +import org.apache.ibatis.type.TypeHandlerRegistry; +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * DefaultParameterHandlerTest + * + * @author Ryan Lamore + */ +public class DefaultParameterHandlerTest { + + @Test + public void setParametersThrowsProperException() throws SQLException { + final MappedStatement mappedStatement = getMappedStatement(); + final Object parameterObject = null; + final BoundSql boundSql = mock(BoundSql.class); + + TypeHandler<String> typeHandler = mock(TypeHandler.class); + doThrow(new SQLException("foo")).when(typeHandler).setParameter(any(PreparedStatement.class), anyInt(), anyString(), any(JdbcType.class)); + ParameterMapping parameterMapping = new ParameterMapping.Builder(mappedStatement.getConfiguration(), "prop", typeHandler).build(); + List<ParameterMapping> parameterMappings = Collections.singletonList(parameterMapping); + when(boundSql.getParameterMappings()).thenReturn(parameterMappings); + + DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); + + PreparedStatement ps = mock(PreparedStatement.class); + try { + defaultParameterHandler.setParameters(ps); + Assert.fail("Should have thrown TypeException"); + } catch (Exception e) { + Assert.assertTrue("expected TypeException", e instanceof TypeException); + Assert.assertTrue("", e.getMessage().contains("mapping: ParameterMapping")); + } + + } + + MappedStatement getMappedStatement() { + final Configuration config = new Configuration(); + final TypeHandlerRegistry registry = config.getTypeHandlerRegistry(); + return new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps( + new ArrayList<ResultMap>() { + { + add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() { + { + add(new ResultMapping.Builder(config, "cOlUmN1", "CoLuMn1", registry.getTypeHandler(Integer.class)).build()); + } + }).build()); + } + }).build(); + } + +} diff --git a/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java b/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java index f3daac22e6f..333d5009dbd 100644 --- a/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java +++ b/src/test/java/org/apache/ibatis/type/UnknownTypeHandlerTest.java @@ -15,15 +15,21 @@ */ package org.apache.ibatis.type; +import java.sql.SQLException; + import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.spy; +import org.apache.ibatis.executor.result.ResultMapException; +import org.junit.Assert; import org.junit.Test; public class UnknownTypeHandlerTest extends BaseTypeHandlerTest { - private static final TypeHandler<Object> TYPE_HANDLER = new UnknownTypeHandler(new TypeHandlerRegistry()); + private static final TypeHandler<Object> TYPE_HANDLER = spy(new UnknownTypeHandler(new TypeHandlerRegistry())); @Test public void shouldSetParameter() throws Exception { @@ -50,4 +56,70 @@ public class UnknownTypeHandlerTest extends BaseTypeHandlerTest { assertEquals("Hello", TYPE_HANDLER.getResult(cs, 1)); } + @Test + public void setParameterWithNullParameter() throws Exception { + TYPE_HANDLER.setParameter(ps, 0, null, JdbcType.INTEGER); + verify(ps).setNull(0, JdbcType.INTEGER.TYPE_CODE); + } + + @Test + public void setParameterWithNullParameterThrowsException() throws SQLException { + doThrow(new SQLException("invalid column")).when(ps).setNull(1, JdbcType.INTEGER.TYPE_CODE); + try { + TYPE_HANDLER.setParameter(ps, 1, null, JdbcType.INTEGER); + Assert.fail("Should have thrown a TypeException"); + } catch (Exception e) { + Assert.assertTrue("Expected TypedException", e instanceof TypeException); + Assert.assertTrue("Parameter index is in exception", e.getMessage().contains("parameter #1")); + } + } + + @Test + public void setParameterWithNonNullParameterThrowsException() throws SQLException { + doThrow(new SQLException("invalid column")).when((UnknownTypeHandler)TYPE_HANDLER).setNonNullParameter(ps, 1, 99, JdbcType.INTEGER); + try { + TYPE_HANDLER.setParameter(ps, 1, 99, JdbcType.INTEGER); + Assert.fail("Should have thrown a TypeException"); + } catch (Exception e) { + Assert.assertTrue("Expected TypedException", e instanceof TypeException); + Assert.assertTrue("Parameter index is in exception", e.getMessage().contains("parameter #1")); + } + } + + @Test + public void getResultWithResultSetAndColumnNameThrowsException() throws SQLException { + doThrow(new SQLException("invalid column")).when((UnknownTypeHandler)TYPE_HANDLER).getNullableResult(rs, "foo"); + try { + TYPE_HANDLER.getResult(rs, "foo"); + Assert.fail("Should have thrown a ResultMapException"); + } catch (Exception e) { + Assert.assertTrue("Expected ResultMapException", e instanceof ResultMapException); + Assert.assertTrue("column name is not in exception", e.getMessage().contains("column 'foo'")); + } + } + + @Test + public void getResultWithResultSetAndColumnIndexThrowsException() throws SQLException { + doThrow(new SQLException("invalid column")).when((UnknownTypeHandler)TYPE_HANDLER).getNullableResult(rs, 1); + try { + TYPE_HANDLER.getResult(rs, 1); + Assert.fail("Should have thrown a ResultMapException"); + } catch (Exception e) { + Assert.assertTrue("Expected ResultMapException", e instanceof ResultMapException); + Assert.assertTrue("column index is not in exception", e.getMessage().contains("column #1")); + } + } + + @Test + public void getResultWithCallableStatementAndColumnIndexThrowsException() throws SQLException { + doThrow(new SQLException("invalid column")).when((UnknownTypeHandler)TYPE_HANDLER).getNullableResult(cs, 1); + try { + TYPE_HANDLER.getResult(cs, 1); + Assert.fail("Should have thrown a ResultMapException"); + } catch (Exception e) { + Assert.assertTrue("Expected ResultMapException", e instanceof ResultMapException); + Assert.assertTrue("column index is not in exception", e.getMessage().contains("column #1")); + } + } + } \ No newline at end of file -- GitLab