Consider full type descriptors in DataClassRowMapper.

Closes #1129
This commit is contained in:
Mark Paluch
2021-05-12 11:21:48 +02:00
parent 1f3095e635
commit e8ad9bf36b
3 changed files with 34 additions and 9 deletions

View File

@@ -389,7 +389,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
*/
@Nullable
protected Object getColumnValue(Row row, int index, Class<?> paramType) {
return row.get(index, paramType);
return row.getObject(index);
}
/**

View File

@@ -19,7 +19,9 @@ import java.lang.reflect.Constructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -43,7 +45,7 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
private @Nullable String[] constructorParameterNames;
private @Nullable Class<?>[] constructorParameterTypes;
private @Nullable TypeDescriptor[] constructorParameterTypes;
/**
* Create a new {@code DataClassRowMapper}.
@@ -60,9 +62,13 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
super.initialize(mappedClass);
this.mappedConstructor = BeanUtils.getResolvableConstructor(mappedClass);
if (this.mappedConstructor.getParameterCount() > 0) {
int paramCount = this.mappedConstructor.getParameterCount();
if (paramCount > 0) {
this.constructorParameterNames = BeanUtils.getParameterNames(this.mappedConstructor);
this.constructorParameterTypes = this.mappedConstructor.getParameterTypes();
this.constructorParameterTypes = new TypeDescriptor[paramCount];
for (int i = 0; i < paramCount; i++) {
this.constructorParameterTypes[i] = new TypeDescriptor(new MethodParameter(this.mappedConstructor, i));
}
}
}
@@ -76,8 +82,9 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
args = new Object[this.constructorParameterNames.length];
for (int i = 0; i < args.length; i++) {
String name = underscoreName(this.constructorParameterNames[i]);
Class<?> type = this.constructorParameterTypes[i];
args[i] = tc.convertIfNecessary(getColumnValue(row, row.getColumnDefinitions().firstIndexOf(name), type), type);
TypeDescriptor td = this.constructorParameterTypes[i];
Object value = getColumnValue(row, row.getColumnDefinitions().firstIndexOf(name), td.getType());
args[i] = tc.convertIfNecessary(value, td.getType(), td);
}
} else {
args = new Object[0];

View File

@@ -40,8 +40,8 @@ class DataClassRowMapperUnitTests {
val definitions = forColumns("firstname", "age")
every { row.columnDefinitions } returns definitions
every { row.get(0, String::class.java) } returns "Walter"
every { row.get(1, Int::class.javaPrimitiveType) } returns 42
every { row.getObject(0) } returns "Walter"
every { row.getObject(1) } returns 42
val rowMapper = DataClassRowMapper<Person>()
val person = rowMapper.mapRow(row, 0)
@@ -50,7 +50,24 @@ class DataClassRowMapperUnitTests {
assertThat(person.age).isEqualTo(42)
}
@Test // #1129
fun createBeanWithGenericsFromRow() {
val definitions = forColumns("firstname", "age")
every { row.columnDefinitions } returns definitions
every { row.getObject(0) } returns "Walter"
every { row.getObject(1) } returns 42
val rowMapper = DataClassRowMapper<PersonWithAgeList>()
val person = rowMapper.mapRow(row, 0)
assertThat(person.firstname).isEqualTo("Walter")
assertThat(person.age).contains(42)
}
data class Person(val firstname: String, val age: Int)
data class PersonWithAgeList(val firstname: String, val age: List<Int>)
private fun forColumns(vararg columns: String): ColumnDefinitions {
@@ -59,7 +76,8 @@ class DataClassRowMapperUnitTests {
for (column in columns) {
val columnDefinition = Mockito.mock(ColumnDefinition::class.java)
val columnIndex = index++
Mockito.`when`(columnDefinition.name).thenReturn(CqlIdentifier.fromInternal(column))
Mockito.`when`(columnDefinition.name)
.thenReturn(CqlIdentifier.fromInternal(column))
Mockito.`when`(definitions[columnIndex]).thenReturn(columnDefinition)
Mockito.`when`(definitions.firstIndexOf(column)).thenReturn(columnIndex)
}