From 261bc2ad6acf974933267bbfcc2586c286cccad0 Mon Sep 17 00:00:00 2001 From: Marten Deinum Date: Mon, 17 Jan 2022 07:54:07 +0100 Subject: [PATCH] Fix regression in BeanPropertyRowMapper.underscoreName(String) Commit 6316a35 introduced a regression for property names starting with multiple uppercase letters (such as setEMail(...)). This commit fixes that regression and includes an additional test to cover this case. See gh-27929 Closes gh-27941 --- .../jdbc/core/BeanPropertyRowMapper.java | 5 +- .../jdbc/core/AbstractRowMapperTests.java | 16 +++- .../jdbc/core/BeanPropertyRowMapperTests.java | 14 +++- .../jdbc/core/test/EmailPerson.java | 78 +++++++++++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 spring-jdbc/src/test/java/org/springframework/jdbc/core/test/EmailPerson.java diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java index 446ef64c5e4..5399e9c4741 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -273,7 +273,8 @@ public class BeanPropertyRowMapper implements RowMapper { } StringBuilder result = new StringBuilder(); - for (int i = 0; i < name.length(); i++) { + result.append(Character.toLowerCase(name.charAt(0))); + for (int i = 1; i < name.length(); i++) { char c = name.charAt(i); if (Character.isUpperCase(c)) { result.append('_').append(Character.toLowerCase(c)); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java index 601bbdfd7a1..bee0bade090 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -30,6 +30,7 @@ import org.springframework.beans.PropertyAccessorFactory; import org.springframework.jdbc.core.test.ConcretePerson; import org.springframework.jdbc.core.test.ConstructorPerson; import org.springframework.jdbc.core.test.DatePerson; +import org.springframework.jdbc.core.test.EmailPerson; import org.springframework.jdbc.core.test.Person; import org.springframework.jdbc.core.test.SpacePerson; import org.springframework.jdbc.datasource.SingleConnectionDataSource; @@ -97,6 +98,14 @@ public abstract class AbstractRowMapperTests { assertThat(bw.getPropertyValue("balance")).isEqualTo(new BigDecimal("1234.56")); } + protected void verifyPerson(EmailPerson person) { + assertThat(person.getName()).isEqualTo("Bubba"); + assertThat(person.getAge()).isEqualTo(22L); + assertThat(person.getBirth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); + assertThat(person.getBalance()).isEqualTo(new BigDecimal("1234.56")); + assertThat(person.getEMail()).isEqualTo("hello@world.info"); + } + protected enum MockType {ONE, TWO, THREE}; @@ -136,19 +145,22 @@ public abstract class AbstractRowMapperTests { given(resultSet.getDate(3)).willReturn(new java.sql.Date(1221222L)); given(resultSet.getBigDecimal(4)).willReturn(new BigDecimal("1234.56")); given(resultSet.getObject(4)).willReturn(new BigDecimal("1234.56")); + given(resultSet.getString(5)).willReturn("hello@world.info"); given(resultSet.wasNull()).willReturn(type == MockType.TWO); - given(resultSetMetaData.getColumnCount()).willReturn(4); + given(resultSetMetaData.getColumnCount()).willReturn(5); given(resultSetMetaData.getColumnLabel(1)).willReturn( type == MockType.THREE ? "Last Name" : "name"); given(resultSetMetaData.getColumnLabel(2)).willReturn("age"); given(resultSetMetaData.getColumnLabel(3)).willReturn("birth_date"); given(resultSetMetaData.getColumnLabel(4)).willReturn("balance"); + given(resultSetMetaData.getColumnLabel(5)).willReturn("e_mail"); given(resultSet.findColumn("name")).willReturn(1); given(resultSet.findColumn("age")).willReturn(2); given(resultSet.findColumn("birth_date")).willReturn(3); given(resultSet.findColumn("balance")).willReturn(4); + given(resultSet.findColumn("e_mail")).willReturn(5); jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(new SingleConnectionDataSource(connection, false)); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java index 6e1f84a632d..c61bab0a297 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -24,6 +24,7 @@ import org.springframework.beans.TypeMismatchException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.jdbc.core.test.ConcretePerson; import org.springframework.jdbc.core.test.DatePerson; +import org.springframework.jdbc.core.test.EmailPerson; import org.springframework.jdbc.core.test.ExtendedPerson; import org.springframework.jdbc.core.test.Person; import org.springframework.jdbc.core.test.SpacePerson; @@ -134,4 +135,15 @@ public class BeanPropertyRowMapperTests extends AbstractRowMapperTests { mock.verifyClosed(); } + @Test + public void testQueryWithUnderscoreAndPersonWithMultipleAdjacentUppercaseLettersInPropertyName() throws Exception { + Mock mock = new Mock(); + List result = mock.getJdbcTemplate().query( + "select name, age, birth_date, balance, e_mail from people", + new BeanPropertyRowMapper<>(EmailPerson.class)); + assertThat(result.size()).isEqualTo(1); + verifyPerson(result.get(0)); + mock.verifyClosed(); + } + } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/EmailPerson.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/EmailPerson.java new file mode 100644 index 00000000000..f9c6996f7ab --- /dev/null +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/EmailPerson.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2022 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 + * + * https://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.springframework.jdbc.core.test; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author Thomas Risberg + * @author Marten Deinum + */ +public class EmailPerson { + + private String name; + + private long age; + + private Date birth_date; + + private BigDecimal balance; + + private String eMail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getAge() { + return age; + } + + public void setAge(long age) { + this.age = age; + } + + public Date getBirth_date() { + return birth_date; + } + + public void setBirth_date(Date birth_date) { + this.birth_date = birth_date; + } + + public BigDecimal getBalance() { + return balance; + } + + public void setBalance(BigDecimal balance) { + this.balance = balance; + } + + public void setEMail(String email) { + this.eMail=email; + } + + public String getEMail() { + return this.eMail; + } + +}