Polishing
This commit is contained in:
parent
66a1be2d86
commit
b9e972c248
|
@ -274,7 +274,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the given name to lower case.
|
* Convert the given name to lower case.
|
||||||
* By default, conversions will happen within the US locale.
|
* <p>By default, conversions will happen within the US locale.
|
||||||
* @param name the original name
|
* @param name the original name
|
||||||
* @return the converted name
|
* @return the converted name
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
|
@ -285,7 +285,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a name in camelCase to an underscored name in lower case.
|
* 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.
|
* <p>Any upper case letters are converted to lower case with a preceding underscore.
|
||||||
* @param name the original name
|
* @param name the original name
|
||||||
* @return the converted name
|
* @return the converted name
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
|
@ -390,7 +390,7 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the given BeanWrapper to be used for row mapping.
|
* Initialize the given BeanWrapper to be used for row mapping.
|
||||||
* To be called for each row.
|
* <p>To be called for each row.
|
||||||
* <p>The default implementation applies the configured {@link ConversionService},
|
* <p>The default implementation applies the configured {@link ConversionService},
|
||||||
* if any. Can be overridden in subclasses.
|
* if any. Can be overridden in subclasses.
|
||||||
* @param bw the BeanWrapper to initialize
|
* @param bw the BeanWrapper to initialize
|
||||||
|
|
|
@ -48,47 +48,46 @@ import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping {@code Function} implementation that converts an R2DBC {@code Readable}
|
* Mapping {@code Function} implementation that converts an R2DBC {@link Readable}
|
||||||
* (a {@code Row} or {@code OutParameters}) into a new instance of the specified mapped
|
* (a {@link Row} or {@link OutParameters}) into a new instance of the specified mapped
|
||||||
* target class. The mapped target class must be a top-level class or {@code static}
|
* target class. The mapped target class must be a top-level class or {@code static}
|
||||||
* nested class, and it must have a default or no-arg constructor.
|
* nested class, and it must have a default or no-arg constructor.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>{@code Readable} component values are mapped based on matching the column
|
||||||
* Readable component values are mapped based on matching the name (as obtained from R2DBC
|
* name (as obtained from R2DBC meta-data) to public setters in the target class
|
||||||
* meta-data) to public setters in the target class for the corresponding properties. The
|
* for the corresponding properties. The names are matched either directly or by
|
||||||
* names are matched either directly or by transforming a name separating the parts with
|
* transforming a name separating the parts with underscores to the same name using
|
||||||
* underscores to the same name using "camel" case.
|
* "camel" case.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Mapping is provided for properties in the target class for many common types —
|
||||||
* Mapping is provided for properties in the target class for many common types —
|
* for example: String, boolean, Boolean, byte, Byte, short, Short, int, Integer,
|
||||||
* for example: String, boolean, Boolean, byte, Byte, short, Short, int, Integer, long,
|
* long, Long, float, Float, double, Double, BigDecimal, {@code java.util.Date}, etc.
|
||||||
* Long, float, Float, double, Double, BigDecimal, {@code java.util.Date}, etc.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>To facilitate mapping between columns and properties that don't have matching
|
||||||
* To facilitate mapping between columns and properties that don't have matching names,
|
* names, try using column aliases in the SQL statement like
|
||||||
* try using column aliases in the SQL statement like
|
* {@code "select fname as first_name from customer"}, where {@code first_name}
|
||||||
* {@code "select fname as first_name from customer"}, where {@code first_name} can be
|
* can be mapped to a {@code setFirstName(String)} method in the target class.
|
||||||
* mapped to a {@code setFirstName(String)} method in the target class.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>For a {@code NULL} value read from the database, an attempt will be made to
|
||||||
* For a {@code NULL} value read from the database, an attempt will be made to call the
|
* call the corresponding setter method with {@code null}, but in the case of
|
||||||
* corresponding setter method with {@code null}, but in the case of Java primitives this
|
* Java primitives this will result in a {@link TypeMismatchException} by default.
|
||||||
* will result in a {@link TypeMismatchException} by default. To ignore {@code NULL}
|
* To ignore {@code NULL} database values for all primitive properties in the
|
||||||
* database values for all primitive properties in the target class, set the
|
* target class, set the {@code primitivesDefaultedForNullValue} flag to
|
||||||
* {@code primitivesDefaultedForNullValue} flag to {@code true}. See
|
* {@code true}. See {@link #setPrimitivesDefaultedForNullValue(boolean)} for
|
||||||
* {@link #setPrimitivesDefaultedForNullValue(boolean)} for details.
|
* details.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>If you need to map to a target class which has a <em>data class</em> constructor
|
||||||
* If you need to map to a target class which has a <em>data class</em> constructor
|
* — for example, a Java {@code record} or a Kotlin {@code data} class —
|
||||||
* — for example, a Java {@code record} or a Kotlin {@code data} class — use
|
* use {@link DataClassRowMapper} instead.
|
||||||
* {@link DataClassRowMapper} instead.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Please note that this class is designed to provide convenience rather than
|
||||||
* Please note that this class is designed to provide convenience rather than high
|
* high performance. For best performance, consider using a custom mapping function
|
||||||
* performance. For best performance, consider using a custom mapping function
|
|
||||||
* implementation.
|
* implementation.
|
||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
|
* @author Thomas Risberg
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
* @param <T> the result type
|
* @param <T> the result type
|
||||||
* @see DataClassRowMapper
|
* @see DataClassRowMapper
|
||||||
|
@ -254,7 +253,7 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the given name to lower case.
|
* Convert the given name to lower case.
|
||||||
* By default, conversions will happen within the US locale.
|
* <p>By default, conversions will happen within the US locale.
|
||||||
* @param name the original name
|
* @param name the original name
|
||||||
* @return the converted name
|
* @return the converted name
|
||||||
*/
|
*/
|
||||||
|
@ -264,7 +263,7 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a name in camelCase to an underscored name in lower case.
|
* 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.
|
* <p>Any upper case letters are converted to lower case with a preceding underscore.
|
||||||
* @param name the original name
|
* @param name the original name
|
||||||
* @return the converted name
|
* @return the converted name
|
||||||
* @see #lowerCaseName
|
* @see #lowerCaseName
|
||||||
|
@ -289,13 +288,11 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the values for the current {@code Readable} :
|
* Extract the values for the current {@link Readable}: all columns in case
|
||||||
* all columns in case of a {@code Row} or all parameters in
|
* of a {@link Row} or all parameters in case of an {@link OutParameters}.
|
||||||
* case of an {@code OutParameters}.
|
* <p>Utilizes public setters and derives meta-data from the concrete type.
|
||||||
* <p>Utilizes public setters and derives meta-data from the
|
* @throws IllegalArgumentException in case the concrete type is neither
|
||||||
* concrete type.
|
* {@code Row} nor {@code OutParameters}
|
||||||
* @throws UnsupportedOperationException in case the concrete type
|
|
||||||
* is neither {@code Row} nor {@code OutParameters}
|
|
||||||
* @see RowMetadata
|
* @see RowMetadata
|
||||||
* @see OutParametersMetadata
|
* @see OutParametersMetadata
|
||||||
*/
|
*/
|
||||||
|
@ -326,7 +323,7 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
PropertyDescriptor pd = (this.mappedProperties != null ? this.mappedProperties.get(property) : null);
|
PropertyDescriptor pd = (this.mappedProperties != null ? this.mappedProperties.get(property) : null);
|
||||||
if (pd != null) {
|
if (pd != null) {
|
||||||
Object value = getItemValue(readable, itemIndex, pd);
|
Object value = getItemValue(readable, itemIndex, pd);
|
||||||
//Implementation note: the JDBC mapper can log the column mapping details each time row 0 is encountered
|
// Implementation note: the JDBC mapper can log the column mapping details each time row 0 is encountered
|
||||||
// but unfortunately this is not possible in R2DBC as row number is not provided. The BiFunction#apply
|
// but unfortunately this is not possible in R2DBC as row number is not provided. The BiFunction#apply
|
||||||
// cannot be stateful as it could be applied to a different row set, e.g. when resubscribing.
|
// cannot be stateful as it could be applied to a different row set, e.g. when resubscribing.
|
||||||
try {
|
try {
|
||||||
|
@ -363,9 +360,8 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an instance of the mapped class for the current {@code Readable}.
|
* Construct an instance of the mapped class for the current {@code Readable}.
|
||||||
* <p>
|
* <p>The default implementation simply instantiates the mapped class. Can be
|
||||||
* The default implementation simply instantiates the mapped class. Can be overridden
|
* overridden in subclasses.
|
||||||
* in subclasses.
|
|
||||||
* @param readable the {@code Readable} being mapped (a {@code Row} or {@code OutParameters})
|
* @param readable the {@code Readable} being mapped (a {@code Row} or {@code OutParameters})
|
||||||
* @param itemMetadatas the list of item {@code ReadableMetadata} (either
|
* @param itemMetadatas the list of item {@code ReadableMetadata} (either
|
||||||
* {@code ColumnMetadata} or {@code OutParameterMetadata})
|
* {@code ColumnMetadata} or {@code OutParameterMetadata})
|
||||||
|
@ -380,7 +376,7 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
/**
|
/**
|
||||||
* Initialize the given BeanWrapper to be used for row mapping or outParameters
|
* Initialize the given BeanWrapper to be used for row mapping or outParameters
|
||||||
* mapping.
|
* mapping.
|
||||||
* To be called for each Readable.
|
* <p>To be called for each Readable.
|
||||||
* <p>The default implementation applies the configured {@link ConversionService},
|
* <p>The default implementation applies the configured {@link ConversionService},
|
||||||
* if any. Can be overridden in subclasses.
|
* if any. Can be overridden in subclasses.
|
||||||
* @param bw the BeanWrapper to initialize
|
* @param bw the BeanWrapper to initialize
|
||||||
|
@ -395,7 +391,7 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a R2DBC object value for the specified item index (a column or an
|
* Retrieve an R2DBC object value for the specified item index (a column or an
|
||||||
* out-parameter).
|
* out-parameter).
|
||||||
* <p>The default implementation delegates to
|
* <p>The default implementation delegates to
|
||||||
* {@link #getItemValue(Readable, int, Class)}.
|
* {@link #getItemValue(Readable, int, Class)}.
|
||||||
|
@ -411,12 +407,12 @@ public class BeanPropertyRowMapper<T> implements Function<Readable, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a R2DBC object value for the specified item index (a column or
|
* Retrieve an R2DBC object value for the specified item index (a column or
|
||||||
* an out-parameter).
|
* an out-parameter).
|
||||||
* <p>The default implementation calls {@link Readable#get(int, Class)} then
|
* <p>The default implementation calls {@link Readable#get(int, Class)} then
|
||||||
* falls back to {@link Readable#get(int)} in case of an exception.
|
* falls back to {@link Readable#get(int)} in case of an exception.
|
||||||
* Subclasses may override this to check specific value types upfront,
|
* Subclasses may override this to check specific value types upfront,
|
||||||
* or to post-process values return from {@code get}.
|
* or to post-process values returned from {@code get}.
|
||||||
* @param readable is the {@code Row} or {@code OutParameters} holding the data
|
* @param readable is the {@code Row} or {@code OutParameters} holding the data
|
||||||
* @param itemIndex is the column index or out-parameter index
|
* @param itemIndex is the column index or out-parameter index
|
||||||
* @param paramType the target parameter type
|
* @param paramType the target parameter type
|
||||||
|
|
|
@ -32,35 +32,34 @@ import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping {@code Function} implementation that converts an R2DBC {@code Readable}
|
* Mapping {@code Function} implementation that converts an R2DBC {@link Readable}
|
||||||
* (a {@code Row} or {@code OutParameters}) into a new instance of the specified mapped
|
* (a {@link io.r2dbc.spi.Row Row} or {@link io.r2dbc.spi.OutParameters OutParameters})
|
||||||
* target class. The mapped target class must be a top-level class or {@code static}
|
* into a new instance of the specified mapped target class. The mapped target class
|
||||||
* nested class, and it may expose either a <em>data class</em> constructor with named
|
* must be a top-level class or {@code static} nested class, and it may expose either
|
||||||
* parameters corresponding to column names or classic bean property setter methods
|
* a <em>data class</em> constructor with named parameters corresponding to column
|
||||||
* with property names corresponding to column names (or even a combination of both).
|
* names or classic bean property setter methods with property names corresponding
|
||||||
|
* to column names (or even a combination of both).
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>The term "data class" applies to Java <em>records</em>, Kotlin <em>data
|
||||||
* The term "data class" applies to Java <em>records</em>, Kotlin <em>data classes</em>,
|
* classes</em>, and any class which has a constructor with named parameters
|
||||||
* and any class which has a constructor with named parameters that are intended to be
|
* that are intended to be mapped to corresponding column names.
|
||||||
* mapped to corresponding column names.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>When combining a data class constructor with setter methods, any property
|
||||||
* When combining a data class constructor with setter methods, any property mapped
|
* mapped successfully via a constructor argument will not be mapped additionally
|
||||||
* successfully via a constructor argument will not be mapped additionally via a
|
* via a corresponding setter method. This means that constructor arguments take
|
||||||
* corresponding setter method. This means that constructor arguments take precedence over
|
* precedence over property setter methods.
|
||||||
* property setter methods.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Note that this class extends {@link BeanPropertyRowMapper} and can
|
||||||
* Note that this class extends {@link BeanPropertyRowMapper} and can therefore serve as a
|
* therefore serve as a common choice for any mapped target class, flexibly
|
||||||
* common choice for any mapped target class, flexibly adapting to constructor style
|
* adapting to constructor style versus setter methods in the mapped class.
|
||||||
* versus setter methods in the mapped class.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Please note that this class is designed to provide convenience rather than
|
||||||
* Please note that this class is designed to provide convenience rather than high
|
* high performance. For best performance, consider using a custom readable mapping
|
||||||
* performance. For best performance, consider using a custom readable mapping
|
|
||||||
* {@code Function} implementation.
|
* {@code Function} implementation.
|
||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
* @param <T> the result type
|
* @param <T> the result type
|
||||||
*/
|
*/
|
||||||
|
@ -135,7 +134,7 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
|
||||||
private int findIndex(Readable readable, List<? extends ReadableMetadata> itemMetadatas, String name) {
|
private int findIndex(Readable readable, List<? extends ReadableMetadata> itemMetadatas, String name) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (ReadableMetadata itemMetadata : itemMetadatas) {
|
for (ReadableMetadata itemMetadata : itemMetadatas) {
|
||||||
//we use equalsIgnoreCase, similarly to RowMetadata#contains(String)
|
// we use equalsIgnoreCase, similar to RowMetadata#contains(String)
|
||||||
if (itemMetadata.getName().equalsIgnoreCase(name)) {
|
if (itemMetadata.getName().equalsIgnoreCase(name)) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,25 +34,30 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
|
||||||
class BeanPropertyRowMapperTests {
|
/**
|
||||||
|
* Tests for R2DBC-based {@link BeanPropertyRowMapper}.
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
class R2dbcBeanPropertyRowMapperTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mappingUnknownReadableRejected() {
|
void mappingUnknownReadableRejected() {
|
||||||
final BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> mapper.apply(Mockito.mock(Readable.class)))
|
assertThatIllegalArgumentException().isThrownBy(() -> mapper.apply(Mockito.mock(Readable.class)))
|
||||||
.withMessageStartingWith("Can only map Readable Row or OutParameters, got io.r2dbc.spi.Readable$MockitoMock$");
|
.withMessageStartingWith("Can only map Readable Row or OutParameters, got io.r2dbc.spi.Readable$MockitoMock$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mappingOutParametersAccepted() {
|
void mappingOutParametersAccepted() {
|
||||||
final BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
||||||
assertThatNoException().isThrownBy(() -> mapper.apply(MockOutParameters.empty()));
|
assertThatNoException().isThrownBy(() -> mapper.apply(MockOutParameters.empty()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mappingRowSimpleObject() {
|
void mappingRowSimpleObject() {
|
||||||
MockRow mockRow = SIMPLE_PERSON_ROW;
|
MockRow mockRow = SIMPLE_PERSON_ROW;
|
||||||
final BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
||||||
|
|
||||||
Person result = mapper.apply(mockRow);
|
Person result = mapper.apply(mockRow);
|
||||||
|
|
||||||
|
@ -64,7 +69,7 @@ class BeanPropertyRowMapperTests {
|
||||||
@Test
|
@Test
|
||||||
void mappingRowMissingAttributeAccepted() {
|
void mappingRowMissingAttributeAccepted() {
|
||||||
MockRow mockRow = SIMPLE_PERSON_ROW;
|
MockRow mockRow = SIMPLE_PERSON_ROW;
|
||||||
final BeanPropertyRowMapper<ExtendedPerson> mapper = new BeanPropertyRowMapper<>(ExtendedPerson.class);
|
BeanPropertyRowMapper<ExtendedPerson> mapper = new BeanPropertyRowMapper<>(ExtendedPerson.class);
|
||||||
|
|
||||||
ExtendedPerson result = mapper.apply(mockRow);
|
ExtendedPerson result = mapper.apply(mockRow);
|
||||||
|
|
||||||
|
@ -77,7 +82,7 @@ class BeanPropertyRowMapperTests {
|
||||||
@Test
|
@Test
|
||||||
void mappingRowWithDifferentName() {
|
void mappingRowWithDifferentName() {
|
||||||
MockRow mockRow = EMAIL_PERSON_ROW;
|
MockRow mockRow = EMAIL_PERSON_ROW;
|
||||||
final BeanPropertyRowMapper<EmailPerson> mapper = new BeanPropertyRowMapper<>(EmailPerson.class);
|
BeanPropertyRowMapper<EmailPerson> mapper = new BeanPropertyRowMapper<>(EmailPerson.class);
|
||||||
|
|
||||||
EmailPerson result = mapper.apply(mockRow);
|
EmailPerson result = mapper.apply(mockRow);
|
||||||
|
|
||||||
|
@ -89,21 +94,22 @@ class BeanPropertyRowMapperTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mappingRowMissingAttributeRejected() {
|
void mappingRowMissingAttributeRejected() {
|
||||||
|
Class<ExtendedPerson> mappedClass = ExtendedPerson.class;
|
||||||
MockRow mockRow = SIMPLE_PERSON_ROW;
|
MockRow mockRow = SIMPLE_PERSON_ROW;
|
||||||
final BeanPropertyRowMapper<ExtendedPerson> mapper = new BeanPropertyRowMapper<>(ExtendedPerson.class, true);
|
BeanPropertyRowMapper<ExtendedPerson> mapper = new BeanPropertyRowMapper<>(mappedClass, true);
|
||||||
|
|
||||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
||||||
.isThrownBy(() -> mapper.apply(mockRow))
|
.isThrownBy(() -> mapper.apply(mockRow))
|
||||||
.withMessage("Given readable does not contain all items necessary to populate object of class org.springframework."
|
.withMessage("Given readable does not contain all items necessary to populate object of %s"
|
||||||
+ "r2dbc.core.BeanPropertyRowMapperTests$ExtendedPerson: [firstName, lastName, address, age]");
|
+ ": [firstName, lastName, address, age]", mappedClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO cannot trigger a mapping of a read-only property, as mappedProperties don't include properties without a setter.
|
// TODO cannot trigger a mapping of a read-only property, as mappedProperties don't include properties without a setter.
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rowTypeAndMappingTypeMisaligned() {
|
void rowTypeAndMappingTypeMisaligned() {
|
||||||
MockRow mockRow = EXTENDED_PERSON_ROW;
|
MockRow mockRow = EXTENDED_PERSON_ROW;
|
||||||
final BeanPropertyRowMapper<TypeMismatchExtendedPerson> mapper = new BeanPropertyRowMapper<>(TypeMismatchExtendedPerson.class);
|
BeanPropertyRowMapper<TypeMismatchExtendedPerson> mapper = new BeanPropertyRowMapper<>(TypeMismatchExtendedPerson.class);
|
||||||
|
|
||||||
assertThatExceptionOfType(TypeMismatchException.class)
|
assertThatExceptionOfType(TypeMismatchException.class)
|
||||||
.isThrownBy(() -> mapper.apply(mockRow))
|
.isThrownBy(() -> mapper.apply(mockRow))
|
||||||
|
@ -124,7 +130,7 @@ class BeanPropertyRowMapperTests {
|
||||||
.identified(2, int.class, null)
|
.identified(2, int.class, null)
|
||||||
.identified(3, String.class, "123 Sesame Street")
|
.identified(3, String.class, "123 Sesame Street")
|
||||||
.build();
|
.build();
|
||||||
final BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
BeanPropertyRowMapper<Person> mapper = new BeanPropertyRowMapper<>(Person.class);
|
||||||
mapper.setPrimitivesDefaultedForNullValue(true);
|
mapper.setPrimitivesDefaultedForNullValue(true);
|
||||||
|
|
||||||
Person result = mapper.apply(mockRow);
|
Person result = mapper.apply(mockRow);
|
||||||
|
@ -147,6 +153,7 @@ class BeanPropertyRowMapperTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class Person {
|
private static class Person {
|
||||||
|
|
||||||
String firstName;
|
String firstName;
|
||||||
|
@ -181,6 +188,7 @@ class BeanPropertyRowMapperTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class ExtendedPerson extends Person {
|
private static class ExtendedPerson extends Person {
|
||||||
|
|
||||||
String address;
|
String address;
|
||||||
|
@ -204,6 +212,7 @@ class BeanPropertyRowMapperTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class EmailPerson extends Person {
|
private static class EmailPerson extends Person {
|
||||||
|
|
||||||
String email;
|
String email;
|
|
@ -27,12 +27,17 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class DataClassRowMapperTests {
|
/**
|
||||||
|
* Test for R2DBC-based {@link DataClassRowMapper}.
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
class R2dbcDataClassRowMapperTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void staticQueryWithDataClass() {
|
void staticQueryWithDataClass() {
|
||||||
MockRow mockRow = MOCK_ROW; // uses name, age, birth_date
|
MockRow mockRow = MOCK_ROW; // uses name, age, birth_date
|
||||||
final DataClassRowMapper<ConstructorPerson> mapper = new DataClassRowMapper<>(ConstructorPerson.class);
|
DataClassRowMapper<ConstructorPerson> mapper = new DataClassRowMapper<>(ConstructorPerson.class);
|
||||||
|
|
||||||
ConstructorPerson person = mapper.apply(mockRow);
|
ConstructorPerson person = mapper.apply(mockRow);
|
||||||
|
|
||||||
|
@ -44,8 +49,8 @@ class DataClassRowMapperTests {
|
||||||
@Test
|
@Test
|
||||||
void staticQueryWithDataClassAndGenerics() {
|
void staticQueryWithDataClassAndGenerics() {
|
||||||
MockRow mockRow = buildMockRow("birth_date", true); // uses name, age, birth_date, balance (as list)
|
MockRow mockRow = buildMockRow("birth_date", true); // uses name, age, birth_date, balance (as list)
|
||||||
//TODO validate actual R2DBC Row implementations would return something for balance if asking a List
|
// TODO validate actual R2DBC Row implementations would return something for balance if requesting a List
|
||||||
final DataClassRowMapper<ConstructorPersonWithGenerics> mapper = new DataClassRowMapper<>(ConstructorPersonWithGenerics.class);
|
DataClassRowMapper<ConstructorPersonWithGenerics> mapper = new DataClassRowMapper<>(ConstructorPersonWithGenerics.class);
|
||||||
ConstructorPersonWithGenerics person = mapper.apply(mockRow);
|
ConstructorPersonWithGenerics person = mapper.apply(mockRow);
|
||||||
|
|
||||||
assertThat(person.name()).isEqualTo("Bubba");
|
assertThat(person.name()).isEqualTo("Bubba");
|
||||||
|
@ -57,7 +62,7 @@ class DataClassRowMapperTests {
|
||||||
@Test
|
@Test
|
||||||
void staticQueryWithDataRecord() {
|
void staticQueryWithDataRecord() {
|
||||||
MockRow mockRow = MOCK_ROW; // uses name, age, birth_date, balance
|
MockRow mockRow = MOCK_ROW; // uses name, age, birth_date, balance
|
||||||
final DataClassRowMapper<RecordPerson> mapper = new DataClassRowMapper<>(RecordPerson.class);
|
DataClassRowMapper<RecordPerson> mapper = new DataClassRowMapper<>(RecordPerson.class);
|
||||||
RecordPerson person = mapper.apply(mockRow);
|
RecordPerson person = mapper.apply(mockRow);
|
||||||
|
|
||||||
assertThat(person.name()).isEqualTo("Bubba");
|
assertThat(person.name()).isEqualTo("Bubba");
|
||||||
|
@ -69,7 +74,7 @@ class DataClassRowMapperTests {
|
||||||
@Test
|
@Test
|
||||||
void staticQueryWithDataClassAndSetters() {
|
void staticQueryWithDataClassAndSetters() {
|
||||||
MockRow mockRow = buildMockRow("birthdate", false); // uses name, age, birthdate (no underscore), balance
|
MockRow mockRow = buildMockRow("birthdate", false); // uses name, age, birthdate (no underscore), balance
|
||||||
final DataClassRowMapper<ConstructorPersonWithSetters> mapper = new DataClassRowMapper<>(ConstructorPersonWithSetters.class);
|
DataClassRowMapper<ConstructorPersonWithSetters> mapper = new DataClassRowMapper<>(ConstructorPersonWithSetters.class);
|
||||||
ConstructorPersonWithSetters person = mapper.apply(mockRow);
|
ConstructorPersonWithSetters person = mapper.apply(mockRow);
|
||||||
|
|
||||||
assertThat(person.name()).isEqualTo("BUBBA");
|
assertThat(person.name()).isEqualTo("BUBBA");
|
||||||
|
@ -177,10 +182,10 @@ class DataClassRowMapperTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static MockRow MOCK_ROW = buildMockRow("birth_date", false);
|
static final MockRow MOCK_ROW = buildMockRow("birth_date", false);
|
||||||
|
|
||||||
private static MockRow buildMockRow(String birthDateColumnName, boolean balanceObjectIdentifier) {
|
private static MockRow buildMockRow(String birthDateColumnName, boolean balanceObjectIdentifier) {
|
||||||
final MockRow.Builder builder = MockRow.builder();
|
MockRow.Builder builder = MockRow.builder();
|
||||||
builder.metadata(MockRowMetadata.builder()
|
builder.metadata(MockRowMetadata.builder()
|
||||||
.columnMetadata(MockColumnMetadata.builder().name("name").javaType(String.class).build())
|
.columnMetadata(MockColumnMetadata.builder().name("name").javaType(String.class).build())
|
||||||
.columnMetadata(MockColumnMetadata.builder().name("age").javaType(long.class).build())
|
.columnMetadata(MockColumnMetadata.builder().name("age").javaType(long.class).build())
|
|
@ -766,7 +766,7 @@ class RestTemplateTests {
|
||||||
given(request.getHeaders()).willReturn(requestHeaders);
|
given(request.getHeaders()).willReturn(requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings({ "deprecation", "removal" })
|
||||||
private void mockResponseStatus(HttpStatus responseStatus) throws Exception {
|
private void mockResponseStatus(HttpStatus responseStatus) throws Exception {
|
||||||
given(request.execute()).willReturn(response);
|
given(request.execute()).willReturn(response);
|
||||||
given(errorHandler.hasError(response)).willReturn(responseStatus.isError());
|
given(errorHandler.hasError(response)).willReturn(responseStatus.isError());
|
||||||
|
|
Loading…
Reference in New Issue