#85 - Consider BigDecimal and BigInteger as simple types.
We now consider BigDecimal and BigInteger as simple types and no longer as entities. We also no longer set properties whose value is null.
This commit is contained in:
@@ -17,6 +17,8 @@ package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
import io.r2dbc.spi.Row;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -36,7 +38,7 @@ public class R2dbcSimpleTypeHolder extends SimpleTypeHolder {
|
||||
* Set of R2DBC simple types.
|
||||
*/
|
||||
public static final Set<Class<?>> R2DBC_SIMPLE_TYPES = Collections
|
||||
.unmodifiableSet(new HashSet<>(Arrays.asList(OutboundRow.class, Row.class)));
|
||||
.unmodifiableSet(new HashSet<>(Arrays.asList(OutboundRow.class, Row.class, BigInteger.class, BigDecimal.class)));
|
||||
|
||||
public static final SimpleTypeHolder HOLDER = new R2dbcSimpleTypeHolder();
|
||||
|
||||
|
||||
@@ -114,7 +114,11 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
|
||||
continue;
|
||||
}
|
||||
|
||||
propertyAccessor.setProperty(property, readFrom(row, property, ""));
|
||||
Object value = readFrom(row, property, "");
|
||||
|
||||
if (value != null) {
|
||||
propertyAccessor.setProperty(property, value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -266,8 +270,7 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
|
||||
|
||||
private void writeNullInternal(OutboundRow sink, RelationalPersistentProperty property) {
|
||||
|
||||
sink.put(property.getColumnName(),
|
||||
SettableValue.empty(getPotentiallyConvertedSimpleNullType(property.getType())));
|
||||
sink.put(property.getColumnName(), SettableValue.empty(getPotentiallyConvertedSimpleNullType(property.getType())));
|
||||
}
|
||||
|
||||
private Class<?> getPotentiallyConvertedSimpleNullType(Class<?> type) {
|
||||
@@ -419,7 +422,14 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
|
||||
String column = prefix + property.getColumnName();
|
||||
|
||||
try {
|
||||
return converter.getConversionService().convert(resultSet.get(column), parameter.getType().getType());
|
||||
|
||||
Object value = resultSet.get(column);
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return converter.getConversionService().convert(value, parameter.getType().getType());
|
||||
} catch (Exception o_O) {
|
||||
throw new MappingException(String.format("Couldn't read column %s from Row.", column), o_O);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,12 @@ import java.util.UUID;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.convert.Jsr310Converters;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.r2dbc.function.convert.R2dbcConverters.RowToNumberConverterFactory.LocalDateConverterOverride;
|
||||
import org.springframework.data.r2dbc.function.convert.R2dbcConverters.RowToNumberConverterFactory.LocalDateTimeConverterOverride;
|
||||
import org.springframework.data.r2dbc.function.convert.R2dbcConverters.RowToNumberConverterFactory.LocalTimeConverterOverride;
|
||||
import org.springframework.data.r2dbc.function.convert.R2dbcConverters.RowToNumberConverterFactory.RowToOffsetDateTimeConverter;
|
||||
import org.springframework.data.r2dbc.function.convert.R2dbcConverters.RowToNumberConverterFactory.RowToStringConverter;
|
||||
import org.springframework.data.r2dbc.function.convert.R2dbcConverters.RowToNumberConverterFactory.RowToUuidConverter;
|
||||
@@ -67,6 +73,22 @@ abstract class R2dbcConverters {
|
||||
return converters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of the registered converters to enforce JSR-310 type usage.
|
||||
* @see CustomConversions#DEFAULT_CONVERTERS
|
||||
* @see Jsr310Converters
|
||||
*/
|
||||
public static Collection<Object> getOverrideConvertersToRegister() {
|
||||
|
||||
List<Object> converters = new ArrayList<>();
|
||||
|
||||
converters.add(LocalDateConverterOverride.INSTANCE);
|
||||
converters.add(LocalDateTimeConverterOverride.INSTANCE);
|
||||
converters.add(LocalTimeConverterOverride.INSTANCE);
|
||||
|
||||
return converters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple singleton to convert {@link Row}s to their {@link Boolean} representation.
|
||||
*
|
||||
@@ -229,5 +251,53 @@ abstract class R2dbcConverters {
|
||||
return row.get(0, ZonedDateTime.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} override that forces {@link LocalDate} to stay on {@link LocalDate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@WritingConverter
|
||||
public enum LocalDateConverterOverride implements Converter<LocalDate, LocalDate> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LocalDate convert(LocalDate value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} override that forces {@link LocalDateTime} to stay on {@link LocalDateTime}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@WritingConverter
|
||||
public enum LocalDateTimeConverterOverride implements Converter<LocalDateTime, LocalDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LocalDateTime convert(LocalDateTime value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} override that forces {@link LocalTime} to stay on {@link LocalTime}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@WritingConverter
|
||||
public enum LocalTimeConverterOverride implements Converter<LocalTime, LocalTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LocalTime convert(LocalTime value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class R2dbcCustomConversions extends CustomConversions {
|
||||
* @param converters must not be {@literal null}.
|
||||
*/
|
||||
public R2dbcCustomConversions(Collection<?> converters) {
|
||||
super(STORE_CONVERSIONS, converters);
|
||||
super(STORE_CONVERSIONS, appendOverriddes(converters));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +50,14 @@ public class R2dbcCustomConversions extends CustomConversions {
|
||||
* @param converters must not be {@literal null}.
|
||||
*/
|
||||
public R2dbcCustomConversions(StoreConversions storeConversions, Collection<?> converters) {
|
||||
super(storeConversions, converters);
|
||||
super(storeConversions, appendOverriddes(converters));
|
||||
}
|
||||
|
||||
private static Collection<?> appendOverriddes(Collection<?> converters) {
|
||||
|
||||
List<Object> objects = new ArrayList<>(converters);
|
||||
objects.addAll(R2dbcConverters.getOverrideConvertersToRegister());
|
||||
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019 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.data.r2dbc.function;
|
||||
|
||||
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
||||
|
||||
/**
|
||||
* {@link PostgresDialect} specific tests for {@link ReactiveDataAccessStrategy}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgresReactiveDataAccessStrategyTests extends ReactiveDataAccessStrategyTestSupport {
|
||||
|
||||
private final ReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
|
||||
|
||||
@Override
|
||||
protected ReactiveDataAccessStrategy getStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2019 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.data.r2dbc.function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.Row;
|
||||
import io.r2dbc.spi.RowMetadata;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.r2dbc.dialect.Dialect;
|
||||
import org.springframework.data.r2dbc.domain.SettableValue;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link Dialect}-aware {@link DefaultReactiveDataAccessStrategy} tests.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract class ReactiveDataAccessStrategyTestSupport {
|
||||
|
||||
protected abstract ReactiveDataAccessStrategy getStrategy();
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteString() {
|
||||
testType(PrimitiveTypes::setString, PrimitiveTypes::getString, "foo", "string");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteCharacter() {
|
||||
testType(PrimitiveTypes::setCharacter, PrimitiveTypes::getCharacter, 'f', "character");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoolean() {
|
||||
testType(PrimitiveTypes::setBooleanValue, PrimitiveTypes::isBooleanValue, true, "boolean_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedBoolean() {
|
||||
testType(PrimitiveTypes::setBoxedBooleanValue, PrimitiveTypes::getBoxedBooleanValue, true, "boxed_boolean_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteByte() {
|
||||
testType(PrimitiveTypes::setByteValue, PrimitiveTypes::getByteValue, (byte) 123, "byte_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedByte() {
|
||||
testType(PrimitiveTypes::setBoxedByteValue, PrimitiveTypes::getBoxedByteValue, (byte) 123, "boxed_byte_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteShort() {
|
||||
testType(PrimitiveTypes::setShortValue, PrimitiveTypes::getShortValue, (short) 123, "short_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedShort() {
|
||||
testType(PrimitiveTypes::setBoxedShortValue, PrimitiveTypes::getBoxedShortValue, (short) 123, "boxed_short_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteInteger() {
|
||||
testType(PrimitiveTypes::setIntValue, PrimitiveTypes::getIntValue, 123, "int_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedInteger() {
|
||||
testType(PrimitiveTypes::setBoxedIntegerValue, PrimitiveTypes::getBoxedIntegerValue, 123, "boxed_integer_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteLong() {
|
||||
testType(PrimitiveTypes::setLongValue, PrimitiveTypes::getLongValue, 123L, "long_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedLong() {
|
||||
testType(PrimitiveTypes::setBoxedLongValue, PrimitiveTypes::getBoxedLongValue, 123L, "boxed_long_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteFloat() {
|
||||
testType(PrimitiveTypes::setFloatValue, PrimitiveTypes::getFloatValue, 0.1f, "float_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedFloat() {
|
||||
testType(PrimitiveTypes::setBoxedFloatValue, PrimitiveTypes::getBoxedFloatValue, 0.1f, "boxed_float_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteDouble() {
|
||||
testType(PrimitiveTypes::setDoubleValue, PrimitiveTypes::getDoubleValue, 0.1, "double_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBoxedDouble() {
|
||||
testType(PrimitiveTypes::setBoxedDoubleValue, PrimitiveTypes::getBoxedDoubleValue, 0.1, "boxed_double_value");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBigInteger() {
|
||||
testType(PrimitiveTypes::setBigInteger, PrimitiveTypes::getBigInteger, BigInteger.TEN, "big_integer");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteBigDecimal() {
|
||||
testType(PrimitiveTypes::setBigDecimal, PrimitiveTypes::getBigDecimal, new BigDecimal("100.123"), "big_decimal");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteLocalDate() {
|
||||
testType(PrimitiveTypes::setLocalDate, PrimitiveTypes::getLocalDate, LocalDate.now(), "local_date");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteLocalTime() {
|
||||
testType(PrimitiveTypes::setLocalTime, PrimitiveTypes::getLocalTime, LocalTime.now(), "local_time");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteLocalDateTime() {
|
||||
testType(PrimitiveTypes::setLocalDateTime, PrimitiveTypes::getLocalDateTime, LocalDateTime.now(),
|
||||
"local_date_time");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteZonedDateTime() {
|
||||
testType(PrimitiveTypes::setZonedDateTime, PrimitiveTypes::getZonedDateTime, ZonedDateTime.now(),
|
||||
"zoned_date_time");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteOffsetDateTime() {
|
||||
testType(PrimitiveTypes::setOffsetDateTime, PrimitiveTypes::getOffsetDateTime, OffsetDateTime.now(),
|
||||
"offset_date_time");
|
||||
}
|
||||
|
||||
@Test // gh-85
|
||||
public void shouldReadAndWriteUuid() {
|
||||
testType(PrimitiveTypes::setUuid, PrimitiveTypes::getUuid, UUID.randomUUID(), "uuid");
|
||||
}
|
||||
|
||||
private <T> void testType(BiConsumer<PrimitiveTypes, T> setter, Function<PrimitiveTypes, T> getter, T testValue,
|
||||
String fieldname) {
|
||||
|
||||
ReactiveDataAccessStrategy strategy = getStrategy();
|
||||
Row rowMock = mock(Row.class);
|
||||
RowMetadata metadataMock = mock(RowMetadata.class);
|
||||
|
||||
PrimitiveTypes toSave = new PrimitiveTypes();
|
||||
setter.accept(toSave, testValue);
|
||||
|
||||
assertThat(strategy.getOutboundRow(toSave)).containsEntry(fieldname, SettableValue.from(testValue));
|
||||
|
||||
when(rowMock.get(fieldname)).thenReturn(testValue);
|
||||
|
||||
PrimitiveTypes loaded = strategy.getRowMapper(PrimitiveTypes.class).apply(rowMock, metadataMock);
|
||||
|
||||
assertThat(getter.apply(loaded)).isEqualTo(testValue);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class PrimitiveTypes {
|
||||
|
||||
String string;
|
||||
char character;
|
||||
|
||||
boolean booleanValue;
|
||||
byte byteValue;
|
||||
short shortValue;
|
||||
int intValue;
|
||||
long longValue;
|
||||
double doubleValue;
|
||||
float floatValue;
|
||||
|
||||
Boolean boxedBooleanValue;
|
||||
Byte boxedByteValue;
|
||||
Short boxedShortValue;
|
||||
Integer boxedIntegerValue;
|
||||
Long boxedLongValue;
|
||||
Double boxedDoubleValue;
|
||||
Float boxedFloatValue;
|
||||
|
||||
BigInteger bigInteger;
|
||||
BigDecimal bigDecimal;
|
||||
|
||||
LocalDate localDate;
|
||||
LocalTime localTime;
|
||||
LocalDateTime localDateTime;
|
||||
OffsetDateTime offsetDateTime;
|
||||
ZonedDateTime zonedDateTime;
|
||||
|
||||
UUID uuid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019 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.data.r2dbc.function;
|
||||
|
||||
import org.springframework.data.r2dbc.dialect.SqlServerDialect;
|
||||
|
||||
/**
|
||||
* {@link SqlServerDialect} specific tests for {@link ReactiveDataAccessStrategy}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class SqlServerReactiveDataAccessStrategyTests extends ReactiveDataAccessStrategyTestSupport {
|
||||
|
||||
private final ReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(SqlServerDialect.INSTANCE);
|
||||
|
||||
@Override
|
||||
protected ReactiveDataAccessStrategy getStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user