Skip to content

Add converters for simple type projections #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
* 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
*
* http://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.repository.query;

import io.r2dbc.spi.Row;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToOffsetDateTimeConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToStringConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToUuidConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToZonedDateTimeConverter;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

/**
* Base class for row data converter.
*
* @author Hebert Coelho
*/
abstract class RowDataConverter {
private RowDataConverter() {
}

/**
* @return A list of the registered converters
*/
static Collection<Object> getConvertersToRegister() {
List<Object> converters = new ArrayList<>();

converters.add(RowToBooleanConverter.INSTANCE);
converters.add(RowToLocalDateConverter.INSTANCE);
converters.add(RowToLocalDateTimeConverter.INSTANCE);
converters.add(RowToLocalTimeConverter.INSTANCE);
converters.add(RowToOffsetDateTimeConverter.INSTANCE);
converters.add(RowToStringConverter.INSTANCE);
converters.add(RowToUuidConverter.INSTANCE);
converters.add(RowToZonedDateTimeConverter.INSTANCE);

return converters;
}

/**
* Simple singleton to convert {@link Row}s to their {@link Boolean} representation.
*
* @author Hebert Coelho
*/
public enum RowToBooleanConverter implements Converter<Row, Boolean> {
INSTANCE;

@Override
public Boolean convert(Row row) {
return row.get(0, Boolean.class);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link LocalDate} representation.
*
* @author Hebert Coelho
*/
public enum RowToLocalDateConverter implements Converter<Row, LocalDate> {
INSTANCE;

@Override
public LocalDate convert(Row row) {
return row.get(0, LocalDate.class);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link LocalDateTime} representation.
*
* @author Hebert Coelho
*/
public enum RowToLocalDateTimeConverter implements Converter<Row, LocalDateTime> {
INSTANCE;

@Override
public LocalDateTime convert(Row row) {
return row.get(0, LocalDateTime.class);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link LocalTime} representation.
*
* @author Hebert Coelho
*/
public enum RowToLocalTimeConverter implements Converter<Row, LocalTime> {
INSTANCE;

@Override
public LocalTime convert(Row row) {
return row.get(0, LocalTime.class);
}
}

/**
* Singleton converter factory to convert the first column of a {@link Row} to a {@link Number}.
* <p>
* Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class
* delegates to {@link NumberUtils#convertNumberToTargetClass(Number, Class)} to perform the conversion.
*
* @see Byte
* @see Short
* @see Integer
* @see Long
* @see java.math.BigInteger
* @see Float
* @see Double
* @see java.math.BigDecimal
*
* @author Hebert Coelho
*/
public enum RowToNumberConverterFactory implements ConverterFactory<Row, Number> {
INSTANCE;

@Override
public <T extends Number> Converter<Row, T> getConverter(Class<T> targetType) {
Assert.notNull(targetType, "Target type must not be null");
return new RowToNumber<>(targetType);
}

private static final class RowToNumber<T extends Number> implements Converter<Row, T> {
private final Class<T> targetType;

RowToNumber(Class<T> targetType) {
this.targetType = targetType;
}

@Override
public T convert(Row source) {

Object object = source.get(0, targetType);

return (object != null ? NumberUtils.convertNumberToTargetClass((Number) object, this.targetType) : null);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link OffsetDateTime} representation.
*
* @author Hebert Coelho
*/
public enum RowToOffsetDateTimeConverter implements Converter<Row, OffsetDateTime> {
INSTANCE;

@Override
public OffsetDateTime convert(Row row) {
return row.get(0, OffsetDateTime.class);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link String} representation.
*
* @author Hebert Coelho
*/
public enum RowToStringConverter implements Converter<Row, String> {
INSTANCE;

@Override
public String convert(Row row) {
return row.get(0, String.class);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link UUID} representation.
*
* @author Hebert Coelho
*/
public enum RowToUuidConverter implements Converter<Row, UUID> {
INSTANCE;

@Override
public UUID convert(Row row) {
return row.get(0, UUID.class);
}
}

/**
* Simple singleton to convert {@link Row}s to their {@link ZonedDateTime} representation.
*
* @author Hebert Coelho
*/
public enum RowToZonedDateTimeConverter implements Converter<Row, ZonedDateTime> {
INSTANCE;

@Override
public ZonedDateTime convert(Row row) {
return row.get(0, ZonedDateTime.class);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.springframework.data.r2dbc.repository.query;

import io.r2dbc.spi.Row;
import org.junit.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToBooleanConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToLocalDateConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToLocalDateTimeConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToLocalTimeConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToOffsetDateTimeConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToStringConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToUuidConverter;
import org.springframework.data.r2dbc.repository.query.RowDataConverter.RowToNumberConverterFactory.RowToZonedDateTimeConverter;

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 static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class RowDataConverterTests {
private static final int TOTAL_REGISTERED_CONVERTERS = 8;

@Test
public void isReturningAllCreatedConverts() {
assertThat(RowDataConverter.getConvertersToRegister().size())
.isEqualTo(TOTAL_REGISTERED_CONVERTERS);
}

@Test
public void isConvertingBoolean() {
Row row = mock(Row.class);
when(row.get(0, Boolean.class)).thenReturn(true);

assertTrue(RowToBooleanConverter.INSTANCE.convert(row));
}

@Test
public void isConvertingLocalDate() {
LocalDate now = LocalDate.now();
Row row = mock(Row.class);
when(row.get(0, LocalDate.class)).thenReturn(now);

assertThat(RowToLocalDateConverter.INSTANCE.convert(row)).isEqualTo(now);
}

@Test
public void isConvertingLocalDateTime() {
LocalDateTime now = LocalDateTime.now();
Row row = mock(Row.class);
when(row.get(0, LocalDateTime.class)).thenReturn(now);

assertThat(RowToLocalDateTimeConverter.INSTANCE.convert(row)).isEqualTo(now);
}

@Test
public void isConvertingLocalTime() {
LocalTime now = LocalTime.now();
Row row = mock(Row.class);
when(row.get(0, LocalTime.class)).thenReturn(now);

assertThat(RowToLocalTimeConverter.INSTANCE.convert(row)).isEqualTo(now);
}

@Test
public void isConvertingOffsetDateTime() {
OffsetDateTime now = OffsetDateTime.now();
Row row = mock(Row.class);
when(row.get(0, OffsetDateTime.class)).thenReturn(now);

assertThat(RowToOffsetDateTimeConverter.INSTANCE.convert(row)).isEqualTo(now);
}

@Test
public void isConvertingString() {
String value = "aValue";
Row row = mock(Row.class);
when(row.get(0, String.class)).thenReturn(value);

assertThat(RowToStringConverter.INSTANCE.convert(row)).isEqualTo(value);
}

@Test
public void isConvertingUUID() {
UUID value = UUID.randomUUID();
Row row = mock(Row.class);
when(row.get(0, UUID.class)).thenReturn(value);

assertThat(RowToUuidConverter.INSTANCE.convert(row)).isEqualTo(value);
}

@Test
public void isConvertingZonedDateTime() {
ZonedDateTime now = ZonedDateTime.now();
Row row = mock(Row.class);
when(row.get(0, ZonedDateTime.class)).thenReturn(now);

assertThat(RowToZonedDateTimeConverter.INSTANCE.convert(row)).isEqualTo(now);
}

@Test
public void isConvertingNumber() {
Row row = mock(Row.class);
when(row.get(0, Integer.class)).thenReturn(33);

final Converter<Row, Integer> converter = RowToNumberConverterFactory.INSTANCE.getConverter(Integer.class);

assertThat(converter.convert(row)).isEqualTo(33);
}

@Test
public void isRaisingExceptionForInvalidNumber() {
assertThatIllegalArgumentException().isThrownBy(
() -> RowToNumberConverterFactory.INSTANCE.getConverter(null)
);
}
}