Skip to content

Commit a11a592

Browse files
committed
AbstractSqlParameterSource enumerates parameter values in toString()
Closes gh-2080
1 parent 4a5b9d3 commit a11a592

File tree

8 files changed

+170
-20
lines changed

8 files changed

+170
-20
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/AbstractSqlParameterSource.java

+53-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,16 +18,26 @@
1818

1919
import java.util.HashMap;
2020
import java.util.Map;
21+
import java.util.StringJoiner;
2122

23+
import org.springframework.jdbc.core.SqlParameterValue;
24+
import org.springframework.jdbc.support.JdbcUtils;
2225
import org.springframework.lang.Nullable;
2326
import org.springframework.util.Assert;
2427

2528
/**
2629
* Abstract base class for {@link SqlParameterSource} implementations.
27-
* Provides registration of SQL types per parameter.
30+
* Provides registration of SQL types per parameter and a friendly
31+
* {@link #toString() toString} representation enumerating all parameters for
32+
* a {@code SqlParameterSource} implementing {@link #getParameterNames()}.
33+
* Concrete subclasses must implement {@link #hasValue} and {@link #getValue}.
2834
*
2935
* @author Juergen Hoeller
36+
* @author Jens Schauder
3037
* @since 2.0
38+
* @see #hasValue(String)
39+
* @see #getValue(String)
40+
* @see #getParameterNames()
3141
*/
3242
public abstract class AbstractSqlParameterSource implements SqlParameterSource {
3343

@@ -81,4 +91,45 @@ public String getTypeName(String paramName) {
8191
return this.typeNames.get(paramName);
8292
}
8393

94+
95+
/**
96+
* Enumerate the parameter names and values with their corresponding SQL type if available,
97+
* or just return the simple {@code SqlParameterSource} implementation class name otherwise.
98+
* @since 5.2
99+
* @see #getParameterNames()
100+
*/
101+
@Override
102+
public String toString() {
103+
String[] parameterNames = getParameterNames();
104+
if (parameterNames != null) {
105+
StringJoiner result = new StringJoiner(", ", getClass().getSimpleName() + " {", "}");
106+
for (String parameterName : parameterNames) {
107+
Object value = getValue(parameterName);
108+
if (value instanceof SqlParameterValue) {
109+
value = ((SqlParameterValue) value).getValue();
110+
}
111+
String typeName = getTypeName(parameterName);
112+
if (typeName == null) {
113+
int sqlType = getSqlType(parameterName);
114+
if (sqlType != TYPE_UNKNOWN) {
115+
typeName = JdbcUtils.resolveTypeName(sqlType);
116+
if (typeName == null) {
117+
typeName = String.valueOf(sqlType);
118+
}
119+
}
120+
}
121+
StringBuilder entry = new StringBuilder();
122+
entry.append(parameterName).append('=').append(value);
123+
if (typeName != null) {
124+
entry.append(" (type:").append(typeName).append(')');
125+
}
126+
result.add(entry);
127+
}
128+
return result.toString();
129+
}
130+
else {
131+
return getClass().getSimpleName();
132+
}
133+
}
134+
84135
}

spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/BeanPropertySqlParameterSource.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.springframework.beans.PropertyAccessor;
2626
import org.springframework.beans.PropertyAccessorFactory;
2727
import org.springframework.jdbc.core.StatementCreatorUtils;
28+
import org.springframework.lang.NonNull;
2829
import org.springframework.lang.Nullable;
2930
import org.springframework.util.StringUtils;
3031

@@ -89,7 +90,7 @@ public int getSqlType(String paramName) {
8990
}
9091

9192
@Override
92-
@Nullable
93+
@NonNull
9394
public String[] getParameterNames() {
9495
return getReadablePropertyNames();
9596
}

spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/MapSqlParameterSource.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.util.Map;
2222

2323
import org.springframework.jdbc.core.SqlParameterValue;
24+
import org.springframework.lang.NonNull;
2425
import org.springframework.lang.Nullable;
2526
import org.springframework.util.Assert;
2627
import org.springframework.util.StringUtils;
@@ -165,7 +166,7 @@ public Object getValue(String paramName) {
165166
}
166167

167168
@Override
168-
@Nullable
169+
@NonNull
169170
public String[] getParameterNames() {
170171
return StringUtils.toStringArray(this.values.keySet());
171172
}

spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/SqlParameterSource.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -89,7 +89,7 @@ default String getTypeName(String paramName) {
8989
}
9090

9191
/**
92-
* Extract all available parameter names if possible.
92+
* Enumerate all available parameter names if possible.
9393
* <p>This is an optional operation, primarily for use with
9494
* {@link org.springframework.jdbc.core.simple.SimpleJdbcInsert}
9595
* and {@link org.springframework.jdbc.core.simple.SimpleJdbcCall}.

spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java

+28
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.jdbc.support;
1818

19+
import java.lang.reflect.Field;
1920
import java.lang.reflect.InvocationTargetException;
2021
import java.math.BigDecimal;
2122
import java.sql.Blob;
@@ -28,6 +29,8 @@
2829
import java.sql.SQLFeatureNotSupportedException;
2930
import java.sql.Statement;
3031
import java.sql.Types;
32+
import java.util.HashMap;
33+
import java.util.Map;
3134
import javax.sql.DataSource;
3235

3336
import org.apache.commons.logging.Log;
@@ -56,6 +59,19 @@ public abstract class JdbcUtils {
5659

5760
private static final Log logger = LogFactory.getLog(JdbcUtils.class);
5861

62+
private static final Map<Integer, String> typeNames = new HashMap<>();
63+
64+
static {
65+
try {
66+
for (Field field : Types.class.getFields()) {
67+
typeNames.put((Integer) field.get(null), field.getName());
68+
}
69+
}
70+
catch (Exception ex) {
71+
throw new IllegalStateException("Failed to resolve JDBC Types constants", ex);
72+
}
73+
}
74+
5975

6076
/**
6177
* Close the given JDBC Connection and ignore any thrown exception.
@@ -442,6 +458,18 @@ public static boolean isNumeric(int sqlType) {
442458
Types.TINYINT == sqlType);
443459
}
444460

461+
/**
462+
* Resolve the standard type name for the given SQL type, if possible.
463+
* @param sqlType the SQL type to resolve
464+
* @return the corresponding constant name in {@link java.sql.Types}
465+
* (e.g. "VARCHAR"/"NUMERIC"), or {@code null} if not resolvable
466+
* @since 5.2
467+
*/
468+
@Nullable
469+
public static String resolveTypeName(int sqlType) {
470+
return typeNames.get(sqlType);
471+
}
472+
445473
/**
446474
* Determine the column name to use. The column name is determined based on a
447475
* lookup using ResultSetMetaData.

spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/BeanPropertySqlParameterSourceTests.java

+42-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.sql.Types;
2020
import java.util.Arrays;
2121

22+
import org.hamcrest.Matchers;
2223
import org.junit.Test;
2324

2425
import org.springframework.tests.sample.beans.TestBean;
@@ -29,16 +30,17 @@
2930
* @author Rick Evans
3031
* @author Juergen Hoeller
3132
* @author Arjen Poutsma
33+
* @author Juergen Hoeller
3234
*/
3335
public class BeanPropertySqlParameterSourceTests {
3436

3537
@Test(expected = IllegalArgumentException.class)
36-
public void withNullBeanPassedToCtor() throws Exception {
38+
public void withNullBeanPassedToCtor() {
3739
new BeanPropertySqlParameterSource(null);
3840
}
3941

4042
@Test(expected = IllegalArgumentException.class)
41-
public void getValueWhereTheUnderlyingBeanHasNoSuchProperty() throws Exception {
43+
public void getValueWhereTheUnderlyingBeanHasNoSuchProperty() {
4244
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new TestBean());
4345
source.getValue("thisPropertyDoesNotExist");
4446
}
@@ -65,23 +67,57 @@ public void successfulPropertyAccessWithOverriddenSqlType() {
6567
}
6668

6769
@Test
68-
public void hasValueWhereTheUnderlyingBeanHasNoSuchProperty() throws Exception {
70+
public void hasValueWhereTheUnderlyingBeanHasNoSuchProperty() {
6971
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new TestBean());
7072
assertFalse(source.hasValue("thisPropertyDoesNotExist"));
7173
}
7274

7375
@Test(expected = IllegalArgumentException.class)
74-
public void getValueWhereTheUnderlyingBeanPropertyIsNotReadable() throws Exception {
76+
public void getValueWhereTheUnderlyingBeanPropertyIsNotReadable() {
7577
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new NoReadableProperties());
7678
source.getValue("noOp");
7779
}
7880

7981
@Test
80-
public void hasValueWhereTheUnderlyingBeanPropertyIsNotReadable() throws Exception {
82+
public void hasValueWhereTheUnderlyingBeanPropertyIsNotReadable() {
8183
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new NoReadableProperties());
8284
assertFalse(source.hasValue("noOp"));
8385
}
8486

87+
@Test
88+
public void toStringShowsParameterDetails() {
89+
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new TestBean("tb", 99));
90+
assertThat(source.toString(), Matchers.allOf(
91+
Matchers.startsWith("BeanPropertySqlParameterSource {"),
92+
Matchers.endsWith("}"),
93+
Matchers.containsString("name=tb (type:VARCHAR)"),
94+
Matchers.containsString("age=99 (type:INTEGER)")
95+
));
96+
}
97+
98+
@Test
99+
public void toStringShowsCustomSqlType() {
100+
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new TestBean("tb", 99));
101+
source.registerSqlType("name", Integer.MAX_VALUE);
102+
assertThat(source.toString(), Matchers.allOf(
103+
Matchers.startsWith("BeanPropertySqlParameterSource {"),
104+
Matchers.endsWith("}"),
105+
Matchers.containsString("name=tb (type:" + Integer.MAX_VALUE + ")"),
106+
Matchers.containsString("age=99 (type:INTEGER)")
107+
));
108+
}
109+
110+
@Test
111+
public void toStringDoesNotShowTypeUnknown() {
112+
BeanPropertySqlParameterSource source = new BeanPropertySqlParameterSource(new TestBean("tb", 99));
113+
assertThat(source.toString(), Matchers.allOf(
114+
Matchers.startsWith("BeanPropertySqlParameterSource {"),
115+
Matchers.endsWith("}"),
116+
Matchers.containsString("beanFactory=null"),
117+
Matchers.not(Matchers.containsString("beanFactory=null (type:"))
118+
));
119+
}
120+
85121

86122
@SuppressWarnings("unused")
87123
private static final class NoReadableProperties {

spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/MapSqlParameterSourceTests.java

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2006 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,36 +16,58 @@
1616

1717
package org.springframework.jdbc.core.namedparam;
1818

19+
import java.sql.Types;
20+
1921
import org.junit.Test;
2022

2123
import org.springframework.jdbc.core.SqlParameterValue;
24+
import org.springframework.jdbc.support.JdbcUtils;
2225

2326
import static org.junit.Assert.*;
2427

2528
/**
2629
* @author Rick Evans
2730
* @author Arjen Poutsma
31+
* @author Juergen Hoeller
2832
*/
2933
public class MapSqlParameterSourceTests {
3034

3135
@Test
32-
public void nullParameterValuesPassedToCtorIsOk() throws Exception {
36+
public void nullParameterValuesPassedToCtorIsOk() {
3337
new MapSqlParameterSource(null);
3438
}
3539

3640
@Test(expected = IllegalArgumentException.class)
37-
public void getValueChokesIfParameterIsNotPresent() throws Exception {
41+
public void getValueChokesIfParameterIsNotPresent() {
3842
MapSqlParameterSource source = new MapSqlParameterSource();
3943
source.getValue("pechorin was right!");
4044
}
4145

4246
@Test
43-
public void sqlParameterValueRegistersSqlType() throws Exception {
44-
MapSqlParameterSource msps = new MapSqlParameterSource("FOO", new SqlParameterValue(2, "Foo"));
47+
public void sqlParameterValueRegistersSqlType() {
48+
MapSqlParameterSource msps = new MapSqlParameterSource("FOO", new SqlParameterValue(Types.NUMERIC, "Foo"));
4549
assertEquals("Correct SQL Type not registered", 2, msps.getSqlType("FOO"));
4650
MapSqlParameterSource msps2 = new MapSqlParameterSource();
4751
msps2.addValues(msps.getValues());
4852
assertEquals("Correct SQL Type not registered", 2, msps2.getSqlType("FOO"));
4953
}
5054

55+
@Test
56+
public void toStringShowsParameterDetails() {
57+
MapSqlParameterSource source = new MapSqlParameterSource("FOO", new SqlParameterValue(Types.NUMERIC, "Foo"));
58+
assertEquals("MapSqlParameterSource {FOO=Foo (type:NUMERIC)}", source.toString());
59+
}
60+
61+
@Test
62+
public void toStringShowsCustomSqlType() {
63+
MapSqlParameterSource source = new MapSqlParameterSource("FOO", new SqlParameterValue(Integer.MAX_VALUE, "Foo"));
64+
assertEquals("MapSqlParameterSource {FOO=Foo (type:" + Integer.MAX_VALUE + ")}", source.toString());
65+
}
66+
67+
@Test
68+
public void toStringDoesNotShowTypeUnknown() {
69+
MapSqlParameterSource source = new MapSqlParameterSource("FOO", new SqlParameterValue(JdbcUtils.TYPE_UNKNOWN, "Foo"));
70+
assertEquals("MapSqlParameterSource {FOO=Foo}", source.toString());
71+
}
72+
5173
}

0 commit comments

Comments
 (0)