Skip to content

Commit 41b5858

Browse files
committed
BeanPropertyRowMapper uses US locale for lower-case conversion by default
Issue: SPR-13216
1 parent c7fef87 commit 41b5858

File tree

1 file changed

+67
-54
lines changed

1 file changed

+67
-54
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java

Lines changed: 67 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -22,6 +22,7 @@
2222
import java.sql.SQLException;
2323
import java.util.HashMap;
2424
import java.util.HashSet;
25+
import java.util.Locale;
2526
import java.util.Map;
2627
import java.util.Set;
2728

@@ -136,9 +137,51 @@ public void setMappedClass(Class<T> mappedClass) {
136137
}
137138
}
138139

140+
/**
141+
* Get the class that we are mapping to.
142+
*/
143+
public final Class<T> getMappedClass() {
144+
return this.mappedClass;
145+
}
146+
147+
/**
148+
* Set whether we're strictly validating that all bean properties have been mapped
149+
* from corresponding database fields.
150+
* <p>Default is {@code false}, accepting unpopulated properties in the target bean.
151+
*/
152+
public void setCheckFullyPopulated(boolean checkFullyPopulated) {
153+
this.checkFullyPopulated = checkFullyPopulated;
154+
}
155+
156+
/**
157+
* Return whether we're strictly validating that all bean properties have been
158+
* mapped from corresponding database fields.
159+
*/
160+
public boolean isCheckFullyPopulated() {
161+
return this.checkFullyPopulated;
162+
}
163+
164+
/**
165+
* Set whether we're defaulting Java primitives in the case of mapping a null value
166+
* from corresponding database fields.
167+
* <p>Default is {@code false}, throwing an exception when nulls are mapped to Java primitives.
168+
*/
169+
public void setPrimitivesDefaultedForNullValue(boolean primitivesDefaultedForNullValue) {
170+
this.primitivesDefaultedForNullValue = primitivesDefaultedForNullValue;
171+
}
172+
173+
/**
174+
* Return whether we're defaulting Java primitives in the case of mapping a null value
175+
* from corresponding database fields.
176+
*/
177+
public boolean isPrimitivesDefaultedForNullValue() {
178+
return this.primitivesDefaultedForNullValue;
179+
}
180+
181+
139182
/**
140183
* Initialize the mapping metadata for the given class.
141-
* @param mappedClass the mapped class.
184+
* @param mappedClass the mapped class
142185
*/
143186
protected void initialize(Class<T> mappedClass) {
144187
this.mappedClass = mappedClass;
@@ -147,9 +190,9 @@ protected void initialize(Class<T> mappedClass) {
147190
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
148191
for (PropertyDescriptor pd : pds) {
149192
if (pd.getWriteMethod() != null) {
150-
this.mappedFields.put(pd.getName().toLowerCase(), pd);
193+
this.mappedFields.put(lowerCaseName(pd.getName()), pd);
151194
String underscoredName = underscoreName(pd.getName());
152-
if (!pd.getName().toLowerCase().equals(underscoredName)) {
195+
if (!lowerCaseName(pd.getName()).equals(underscoredName)) {
153196
this.mappedFields.put(underscoredName, pd);
154197
}
155198
this.mappedProperties.add(pd.getName());
@@ -160,18 +203,20 @@ protected void initialize(Class<T> mappedClass) {
160203
/**
161204
* Convert a name in camelCase to an underscored name in lower case.
162205
* Any upper case letters are converted to lower case with a preceding underscore.
163-
* @param name the string containing original name
206+
* @param name the original name
164207
* @return the converted name
208+
* @since 4.2
209+
* @see #lowerCaseName
165210
*/
166-
private String underscoreName(String name) {
211+
protected String underscoreName(String name) {
167212
if (!StringUtils.hasLength(name)) {
168213
return "";
169214
}
170215
StringBuilder result = new StringBuilder();
171-
result.append(name.substring(0, 1).toLowerCase());
216+
result.append(lowerCaseName(name.substring(0, 1)));
172217
for (int i = 1; i < name.length(); i++) {
173218
String s = name.substring(i, i + 1);
174-
String slc = s.toLowerCase();
219+
String slc = lowerCaseName(s);
175220
if (!s.equals(slc)) {
176221
result.append("_").append(slc);
177222
}
@@ -183,45 +228,14 @@ private String underscoreName(String name) {
183228
}
184229

185230
/**
186-
* Get the class that we are mapping to.
187-
*/
188-
public final Class<T> getMappedClass() {
189-
return this.mappedClass;
190-
}
191-
192-
/**
193-
* Set whether we're strictly validating that all bean properties have been
194-
* mapped from corresponding database fields.
195-
* <p>Default is {@code false}, accepting unpopulated properties in the
196-
* target bean.
197-
*/
198-
public void setCheckFullyPopulated(boolean checkFullyPopulated) {
199-
this.checkFullyPopulated = checkFullyPopulated;
200-
}
201-
202-
/**
203-
* Return whether we're strictly validating that all bean properties have been
204-
* mapped from corresponding database fields.
205-
*/
206-
public boolean isCheckFullyPopulated() {
207-
return this.checkFullyPopulated;
208-
}
209-
210-
/**
211-
* Set whether we're defaulting Java primitives in the case of mapping a null value
212-
* from corresponding database fields.
213-
* <p>Default is {@code false}, throwing an exception when nulls are mapped to Java primitives.
214-
*/
215-
public void setPrimitivesDefaultedForNullValue(boolean primitivesDefaultedForNullValue) {
216-
this.primitivesDefaultedForNullValue = primitivesDefaultedForNullValue;
217-
}
218-
219-
/**
220-
* Return whether we're defaulting Java primitives in the case of mapping a null value
221-
* from corresponding database fields.
231+
* Convert the given name to lower case.
232+
* By default, conversions will happen within the US locale.
233+
* @param name the original name
234+
* @return the converted name
235+
* @since 4.2
222236
*/
223-
public boolean isPrimitivesDefaultedForNullValue() {
224-
return primitivesDefaultedForNullValue;
237+
protected String lowerCaseName(String name) {
238+
return name.toLowerCase(Locale.US);
225239
}
226240

227241

@@ -243,7 +257,7 @@ public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
243257

244258
for (int index = 1; index <= columnCount; index++) {
245259
String column = JdbcUtils.lookupColumnName(rsmd, index);
246-
PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase());
260+
PropertyDescriptor pd = this.mappedFields.get(lowerCaseName(column.replaceAll(" ", "")));
247261
if (pd != null) {
248262
try {
249263
Object value = getColumnValue(rs, index, pd);
@@ -254,15 +268,14 @@ public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
254268
try {
255269
bw.setPropertyValue(pd.getName(), value);
256270
}
257-
catch (TypeMismatchException e) {
258-
if (value == null && primitivesDefaultedForNullValue) {
259-
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
260-
" and column '" + column + "' with value " + value +
261-
" when setting property '" + pd.getName() + "' of type " + pd.getPropertyType() +
262-
" on object: " + mappedObject);
271+
catch (TypeMismatchException ex) {
272+
if (value == null && this.primitivesDefaultedForNullValue) {
273+
logger.debug("Intercepted TypeMismatchException for row " + rowNumber + " and column '" +
274+
column + "' with null value when setting property '" + pd.getName() +
275+
"' of type " + pd.getPropertyType() + " on object: " + mappedObject);
263276
}
264277
else {
265-
throw e;
278+
throw ex;
266279
}
267280
}
268281
if (populatedProperties != null) {

0 commit comments

Comments
 (0)