Skip to content

Commit

Permalink
DataClassRowMapper suppresses setter method calls for constructor-bou…
Browse files Browse the repository at this point in the history
…nd properties

Closes gh-26569
  • Loading branch information
jhoeller committed Jul 9, 2021
1 parent c45c46d commit 4fe3ca1
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 15 deletions.
Expand Up @@ -225,16 +225,40 @@ protected void initialize(Class<T> mappedClass) {

for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(mappedClass)) {
if (pd.getWriteMethod() != null) {
this.mappedFields.put(lowerCaseName(pd.getName()), pd);
String underscoredName = underscoreName(pd.getName());
if (!lowerCaseName(pd.getName()).equals(underscoredName)) {
this.mappedFields.put(underscoredName, pd);
String lowerCaseName = lowerCaseName(pd.getName());
this.mappedFields.put(lowerCaseName, pd);
String underscoreName = underscoreName(pd.getName());
if (!lowerCaseName.equals(underscoreName)) {
this.mappedFields.put(underscoreName, pd);
}
this.mappedProperties.add(pd.getName());
}
}
}

/**
* Remove the specified property from the mapped fields.
* @param propertyName the property name (as used by property descriptors)
* @since 5.3.9
*/
protected void suppressProperty(String propertyName) {
if (this.mappedFields != null) {
this.mappedFields.remove(lowerCaseName(propertyName));
this.mappedFields.remove(underscoreName(propertyName));
}
}

/**
* Convert the given name to lower case.
* By default, conversions will happen within the US locale.
* @param name the original name
* @return the converted name
* @since 4.2
*/
protected String lowerCaseName(String name) {
return name.toLowerCase(Locale.US);
}

/**
* Convert a name in camelCase to an underscored name in lower case.
* Any upper case letters are converted to lower case with a preceding underscore.
Expand All @@ -261,17 +285,6 @@ protected String underscoreName(String name) {
return result.toString();
}

/**
* Convert the given name to lower case.
* By default, conversions will happen within the US locale.
* @param name the original name
* @return the converted name
* @since 4.2
*/
protected String lowerCaseName(String name) {
return name.toLowerCase(Locale.US);
}


/**
* Extract the values for all columns in the current row.
Expand Down
Expand Up @@ -80,6 +80,9 @@ protected void initialize(Class<T> mappedClass) {
int paramCount = this.mappedConstructor.getParameterCount();
if (paramCount > 0) {
this.constructorParameterNames = BeanUtils.getParameterNames(this.mappedConstructor);
for (String name : this.constructorParameterNames) {
suppressProperty(name);
}
this.constructorParameterTypes = new TypeDescriptor[paramCount];
for (int i = 0; i < paramCount; i++) {
this.constructorParameterTypes[i] = new TypeDescriptor(new MethodParameter(this.mappedConstructor, i));
Expand Down
Expand Up @@ -25,6 +25,7 @@

import org.springframework.jdbc.core.test.ConstructorPerson;
import org.springframework.jdbc.core.test.ConstructorPersonWithGenerics;
import org.springframework.jdbc.core.test.ConstructorPersonWithSetters;

import static org.assertj.core.api.Assertions.assertThat;

Expand Down Expand Up @@ -62,4 +63,20 @@ public void testStaticQueryWithDataClassAndGenerics() throws Exception {
mock.verifyClosed();
}

@Test
public void testStaticQueryWithDataClassAndSetters() throws Exception {
Mock mock = new Mock();
List<ConstructorPersonWithSetters> result = mock.getJdbcTemplate().query(
"select name, age, birth_date, balance from people",
new DataClassRowMapper<>(ConstructorPersonWithSetters.class));
assertThat(result.size()).isEqualTo(1);
ConstructorPersonWithSetters person = result.get(0);
assertThat(person.name()).isEqualTo("BUBBA");
assertThat(person.age()).isEqualTo(22L);
assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.balance()).isEqualTo(new BigDecimal("1234.56"));

mock.verifyClosed();
}

}
@@ -0,0 +1,76 @@
/*
* Copyright 2002-2021 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 Juergen Hoeller
*/
public class ConstructorPersonWithSetters {

private String name;

private long age;

private Date birth_date;

private BigDecimal balance;


public ConstructorPersonWithSetters(String name, long age, Date birth_date, BigDecimal balance) {
this.name = name.toUpperCase();
this.age = age;
this.birth_date = birth_date;
this.balance = balance;
}


public void setName(String name) {
this.name = name;
}

public void setAge(long age) {
this.age = age;
}

public void setBirth_date(Date birth_date) {
this.birth_date = birth_date;
}

public void setBalance(BigDecimal balance) {
this.balance = balance;
}

public String name() {
return this.name;
}

public long age() {
return this.age;
}

public Date birth_date() {
return this.birth_date;
}

public BigDecimal balance() {
return this.balance;
}

}
@@ -0,0 +1,57 @@
/*
* Copyright 2002-2021 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

import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.jdbc.core.test.ConstructorPerson
import java.math.BigDecimal
import java.util.*

class KotlinDataClassRowMapperTests : AbstractRowMapperTests() {

@Test
fun testStaticQueryWithDataClass() {
val mock = Mock()
val result = mock.jdbcTemplate.query(
"select name, age, birth_date, balance from people",
DataClassRowMapper(ConstructorPerson::class.java)
)
Assertions.assertThat(result.size).isEqualTo(1)
verifyPerson(result[0])
mock.verifyClosed()
}

@Test
fun testInitPropertiesAreNotOverridden() {
val mock = Mock()
val result = mock.jdbcTemplate.query(
"select name, age, birth_date, balance from people",
DataClassRowMapper(KotlinPerson::class.java)
)
Assertions.assertThat(result.size).isEqualTo(1)
Assertions.assertThat(result[0].name).isEqualTo("Bubba appended by init")
}


data class KotlinPerson(var name: String, val age: Long, val birth_date: Date, val balance: BigDecimal) {
init {
name += " appended by init"
}
}

}

0 comments on commit 4fe3ca1

Please sign in to comment.