Skip to content

Commit 34dc733

Browse files
committed
Added better error reporting to help debug issues with mapper files
1 parent 7088e18 commit 34dc733

File tree

12 files changed

+431
-38
lines changed

12 files changed

+431
-38
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Copyright 2009-2015 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+
* http://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.executor.result;
17+
18+
import org.apache.ibatis.exceptions.PersistenceException;
19+
20+
/**
21+
* @author Ryan Lamore
22+
*/
23+
public class ResultMapException extends PersistenceException {
24+
private static final long serialVersionUID = 3270932060569707623L;
25+
26+
public ResultMapException() {
27+
}
28+
29+
public ResultMapException(String message) {
30+
super(message);
31+
}
32+
33+
public ResultMapException(String message, Throwable cause) {
34+
super(message, cause);
35+
}
36+
37+
public ResultMapException(Throwable cause) {
38+
super(cause);
39+
}
40+
}

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.ibatis.executor.parameter.ParameterHandler;
3838
import org.apache.ibatis.executor.result.DefaultResultContext;
3939
import org.apache.ibatis.executor.result.DefaultResultHandler;
40+
import org.apache.ibatis.executor.result.ResultMapException;
4041
import org.apache.ibatis.mapping.BoundSql;
4142
import org.apache.ibatis.mapping.Discriminator;
4243
import org.apache.ibatis.mapping.MappedStatement;
@@ -562,21 +563,27 @@ private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, Lis
562563
throw new ExecutorException("Do not know how to create an instance of " + resultType);
563564
}
564565

565-
private Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
566-
List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
566+
Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
567+
List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
567568
boolean foundValues = false;
568569
for (ResultMapping constructorMapping : constructorMappings) {
569570
final Class<?> parameterType = constructorMapping.getJavaType();
570571
final String column = constructorMapping.getColumn();
571572
final Object value;
572-
if (constructorMapping.getNestedQueryId() != null) {
573-
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
574-
} else if (constructorMapping.getNestedResultMapId() != null) {
575-
final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
576-
value = getRowValue(rsw, resultMap);
577-
} else {
578-
final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
579-
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
573+
try {
574+
if (constructorMapping.getNestedQueryId() != null) {
575+
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
576+
} else if (constructorMapping.getNestedResultMapId() != null) {
577+
final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
578+
value = getRowValue(rsw, resultMap);
579+
} else {
580+
final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
581+
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
582+
}
583+
} catch (ResultMapException e) {
584+
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
585+
} catch (SQLException e) {
586+
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
580587
}
581588
constructorArgTypes.add(parameterType);
582589
constructorArgs.add(value);

src/main/java/org/apache/ibatis/mapping/ParameterMapping.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,20 @@ public String getExpression() {
199199
return expression;
200200
}
201201

202+
@Override
203+
public String toString() {
204+
final StringBuilder sb = new StringBuilder("ParameterMapping{");
205+
//sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
206+
sb.append("property='").append(property).append('\'');
207+
sb.append(", mode=").append(mode);
208+
sb.append(", javaType=").append(javaType);
209+
sb.append(", jdbcType=").append(jdbcType);
210+
sb.append(", numericScale=").append(numericScale);
211+
//sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
212+
sb.append(", resultMapId='").append(resultMapId).append('\'');
213+
sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\'');
214+
sb.append(", expression='").append(expression).append('\'');
215+
sb.append('}');
216+
return sb.toString();
217+
}
202218
}

src/main/java/org/apache/ibatis/mapping/ResultMapping.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,26 @@ public int hashCode() {
280280
}
281281
}
282282

283+
@Override
284+
public String toString() {
285+
final StringBuilder sb = new StringBuilder("ResultMapping{");
286+
//sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
287+
sb.append("property='").append(property).append('\'');
288+
sb.append(", column='").append(column).append('\'');
289+
sb.append(", javaType=").append(javaType);
290+
sb.append(", jdbcType=").append(jdbcType);
291+
//sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
292+
sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\'');
293+
sb.append(", nestedQueryId='").append(nestedQueryId).append('\'');
294+
sb.append(", notNullColumns=").append(notNullColumns);
295+
sb.append(", columnPrefix='").append(columnPrefix).append('\'');
296+
sb.append(", flags=").append(flags);
297+
sb.append(", composites=").append(composites);
298+
sb.append(", resultSet='").append(resultSet).append('\'');
299+
sb.append(", foreignColumn='").append(foreignColumn).append('\'');
300+
sb.append(", lazy=").append(lazy);
301+
sb.append('}');
302+
return sb.toString();
303+
}
304+
283305
}

src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void setProperties(Properties properties) {
5555
// no props for default
5656
}
5757

58-
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
58+
<T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
5959
try {
6060
Constructor<T> constructor;
6161
if (constructorArgTypes == null || constructorArgs == null) {
@@ -72,18 +72,20 @@ private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes
7272
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
7373
} catch (Exception e) {
7474
StringBuilder argTypes = new StringBuilder();
75-
if (constructorArgTypes != null) {
75+
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
7676
for (Class<?> argType : constructorArgTypes) {
7777
argTypes.append(argType.getSimpleName());
7878
argTypes.append(",");
7979
}
80+
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
8081
}
8182
StringBuilder argValues = new StringBuilder();
82-
if (constructorArgs != null) {
83+
if (constructorArgs != null && !constructorArgs.isEmpty()) {
8384
for (Object argValue : constructorArgs) {
8485
argValues.append(String.valueOf(argValue));
8586
argValues.append(",");
8687
}
88+
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
8789
}
8890
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
8991
}

src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.ibatis.reflection.MetaObject;
2929
import org.apache.ibatis.session.Configuration;
3030
import org.apache.ibatis.type.JdbcType;
31+
import org.apache.ibatis.type.TypeException;
3132
import org.apache.ibatis.type.TypeHandler;
3233
import org.apache.ibatis.type.TypeHandlerRegistry;
3334

@@ -58,7 +59,7 @@ public Object getParameterObject() {
5859
}
5960

6061
@Override
61-
public void setParameters(PreparedStatement ps) throws SQLException {
62+
public void setParameters(PreparedStatement ps) {
6263
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
6364
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
6465
if (parameterMappings != null) {
@@ -82,7 +83,13 @@ public void setParameters(PreparedStatement ps) throws SQLException {
8283
if (value == null && jdbcType == null) {
8384
jdbcType = configuration.getJdbcTypeForNull();
8485
}
85-
typeHandler.setParameter(ps, i + 1, value, jdbcType);
86+
try {
87+
typeHandler.setParameter(ps, i + 1, value, jdbcType);
88+
} catch (TypeException e) {
89+
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
90+
} catch (SQLException e) {
91+
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
92+
}
8693
}
8794
}
8895
}

src/main/java/org/apache/ibatis/type/BaseTypeHandler.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.sql.ResultSet;
2121
import java.sql.SQLException;
2222

23+
import org.apache.ibatis.executor.result.ResultMapException;
2324
import org.apache.ibatis.session.Configuration;
2425

2526
/**
@@ -48,13 +49,24 @@ public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbc
4849
"Cause: " + e, e);
4950
}
5051
} else {
51-
setNonNullParameter(ps, i, parameter, jdbcType);
52+
try {
53+
setNonNullParameter(ps, i, parameter, jdbcType);
54+
} catch (Exception e) {
55+
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
56+
"Try setting a different JdbcType for this parameter or a different configuration property. " +
57+
"Cause: " + e, e);
58+
}
5259
}
5360
}
5461

5562
@Override
5663
public T getResult(ResultSet rs, String columnName) throws SQLException {
57-
T result = getNullableResult(rs, columnName);
64+
T result;
65+
try {
66+
result = getNullableResult(rs, columnName);
67+
} catch (Exception e) {
68+
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
69+
}
5870
if (rs.wasNull()) {
5971
return null;
6072
} else {
@@ -64,7 +76,12 @@ public T getResult(ResultSet rs, String columnName) throws SQLException {
6476

6577
@Override
6678
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
67-
T result = getNullableResult(rs, columnIndex);
79+
T result;
80+
try {
81+
result = getNullableResult(rs, columnIndex);
82+
} catch (Exception e) {
83+
throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set. Cause: " + e, e);
84+
}
6885
if (rs.wasNull()) {
6986
return null;
7087
} else {
@@ -74,7 +91,12 @@ public T getResult(ResultSet rs, int columnIndex) throws SQLException {
7491

7592
@Override
7693
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
77-
T result = getNullableResult(cs, columnIndex);
94+
T result;
95+
try {
96+
result = getNullableResult(cs, columnIndex);
97+
} catch (Exception e) {
98+
throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement. Cause: " + e, e);
99+
}
78100
if (cs.wasNull()) {
79101
return null;
80102
} else {

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

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@
1616
package org.apache.ibatis.executor.resultset;
1717

1818
import static org.junit.Assert.assertEquals;
19+
import static org.mockito.Matchers.any;
20+
import static org.mockito.Matchers.anyString;
21+
import static org.mockito.Mockito.mock;
1922
import static org.mockito.Mockito.when;
2023

21-
import java.sql.Connection;
22-
import java.sql.DatabaseMetaData;
23-
import java.sql.ResultSet;
24-
import java.sql.ResultSetMetaData;
25-
import java.sql.Statement;
26-
import java.sql.Types;
24+
import java.sql.*;
2725
import java.util.ArrayList;
26+
import java.util.Collections;
2827
import java.util.HashMap;
2928
import java.util.List;
3029

3130
import org.apache.ibatis.builder.StaticSqlSource;
3231
import org.apache.ibatis.executor.Executor;
32+
import org.apache.ibatis.executor.ExecutorException;
3333
import org.apache.ibatis.executor.parameter.ParameterHandler;
3434
import org.apache.ibatis.mapping.BoundSql;
3535
import org.apache.ibatis.mapping.MappedStatement;
@@ -39,7 +39,9 @@
3939
import org.apache.ibatis.session.Configuration;
4040
import org.apache.ibatis.session.ResultHandler;
4141
import org.apache.ibatis.session.RowBounds;
42+
import org.apache.ibatis.type.TypeHandler;
4243
import org.apache.ibatis.type.TypeHandlerRegistry;
44+
import org.junit.Assert;
4345
import org.junit.Test;
4446
import org.junit.runner.RunWith;
4547
import org.mockito.Mock;
@@ -67,18 +69,7 @@ public class DefaultResultSetHandlerTest {
6769
@Test
6870
public void shouldRetainColumnNameCase() throws Exception {
6971

70-
final Configuration config = new Configuration();
71-
final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
72-
final MappedStatement ms = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps(
73-
new ArrayList<ResultMap>() {
74-
{
75-
add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() {
76-
{
77-
add(new ResultMapping.Builder(config, "cOlUmN1", "CoLuMn1", registry.getTypeHandler(Integer.class)).build());
78-
}
79-
}).build());
80-
}
81-
}).build();
72+
final MappedStatement ms = getMappedStatement();
8273

8374
final Executor executor = null;
8475
final ParameterHandler parameterHandler = null;
@@ -106,4 +97,45 @@ public void shouldRetainColumnNameCase() throws Exception {
10697
assertEquals(Integer.valueOf(100), ((HashMap) results.get(0)).get("cOlUmN1"));
10798
}
10899

100+
@Test
101+
public void shouldThrowExceptionWithColumnName() throws Exception {
102+
final MappedStatement ms = getMappedStatement();
103+
final RowBounds rowBounds = new RowBounds(0, 100);
104+
105+
final DefaultResultSetHandler defaultResultSetHandler = new DefaultResultSetHandler(null/*executor*/, ms,
106+
null/*parameterHandler*/, null/*resultHandler*/, null/*boundSql*/, rowBounds);
107+
108+
final ResultSetWrapper rsw = mock(ResultSetWrapper.class);
109+
110+
final ResultMapping resultMapping = mock(ResultMapping.class);
111+
final TypeHandler typeHandler = mock(TypeHandler.class);
112+
when(resultMapping.getTypeHandler()).thenReturn(typeHandler);
113+
when(typeHandler.getResult(any(ResultSet.class), anyString())).thenThrow(new SQLException("exception"));
114+
List<ResultMapping> constructorMappings = Collections.singletonList(resultMapping);
115+
116+
try {
117+
defaultResultSetHandler.createParameterizedResultObject(rsw, null/*resultType*/, constructorMappings,
118+
null/*constructorArgTypes*/, null/*constructorArgs*/, null/*columnPrefix*/);
119+
Assert.fail("Should have thrown ExecutorException");
120+
} catch (Exception e) {
121+
Assert.assertTrue("Expected ExecutorException", e instanceof ExecutorException);
122+
Assert.assertTrue("", e.getMessage().contains("mapping: " + resultMapping.toString()));
123+
}
124+
}
125+
126+
MappedStatement getMappedStatement() {
127+
final Configuration config = new Configuration();
128+
final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
129+
return new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps(
130+
new ArrayList<ResultMap>() {
131+
{
132+
add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() {
133+
{
134+
add(new ResultMapping.Builder(config, "cOlUmN1", "CoLuMn1", registry.getTypeHandler(Integer.class)).build());
135+
}
136+
}).build());
137+
}
138+
}).build();
139+
}
140+
109141
}

0 commit comments

Comments
 (0)