#30 - Address review feedback.

Introduce ArrayColumns type to encapsulate Dialect-specific array support.
Apply array conversion for properties that do not match the native array type.
Add integration tests for Postgres array columns.

Original pull request: #31.
This commit is contained in:
Mark Paluch
2018-12-05 10:07:25 +01:00
committed by Jens Schauder
parent be5383abed
commit 9f68352ac7
9 changed files with 316 additions and 40 deletions

View File

@@ -1,8 +1,8 @@
package org.springframework.data.r2dbc.dialect;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.SoftAssertions.*;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
@@ -28,35 +28,50 @@ public class PostgresDialectUnitTests {
}
@Test // gh-30
public void shouldConsiderCollectionTypesAsSimple() {
public void shouldConsiderSimpleTypes() {
SimpleTypeHolder holder = PostgresDialect.INSTANCE.getSimpleTypeHolder();
assertThat(holder.isSimpleType(List.class)).isTrue();
assertThat(holder.isSimpleType(Collection.class)).isTrue();
assertSoftly(it -> {
it.assertThat(holder.isSimpleType(String.class)).isTrue();
it.assertThat(holder.isSimpleType(int.class)).isTrue();
it.assertThat(holder.isSimpleType(Integer.class)).isTrue();
});
}
@Test // gh-30
public void shouldConsiderStringArrayTypeAsSimple() {
public void shouldSupportArrays() {
SimpleTypeHolder holder = PostgresDialect.INSTANCE.getSimpleTypeHolder();
ArrayColumns arrayColumns = PostgresDialect.INSTANCE.getArraySupport();
assertThat(holder.isSimpleType(String[].class)).isTrue();
assertThat(arrayColumns.isSupported()).isTrue();
}
@Test // gh-30
public void shouldConsiderIntArrayTypeAsSimple() {
@Test // gh-30
public void shouldUseBoxedArrayTypesForPrimitiveTypes() {
SimpleTypeHolder holder = PostgresDialect.INSTANCE.getSimpleTypeHolder();
ArrayColumns arrayColumns = PostgresDialect.INSTANCE.getArraySupport();
assertThat(holder.isSimpleType(int[].class)).isTrue();
}
assertSoftly(it -> {
it.assertThat(arrayColumns.getArrayType(int.class)).isEqualTo(Integer.class);
it.assertThat(arrayColumns.getArrayType(double.class)).isEqualTo(Double.class);
it.assertThat(arrayColumns.getArrayType(String.class)).isEqualTo(String.class);
});
}
@Test // gh-30
public void shouldConsiderIntegerArrayTypeAsSimple() {
@Test // gh-30
public void shouldRejectNonSimpleArrayTypes() {
SimpleTypeHolder holder = PostgresDialect.INSTANCE.getSimpleTypeHolder();
ArrayColumns arrayColumns = PostgresDialect.INSTANCE.getArraySupport();
assertThat(holder.isSimpleType(Integer[].class)).isTrue();
}
assertThatThrownBy(() -> arrayColumns.getArrayType(getClass())).isInstanceOf(IllegalArgumentException.class);
}
@Test // gh-30
public void shouldRejectNestedCollections() {
ArrayColumns arrayColumns = PostgresDialect.INSTANCE.getArraySupport();
assertThatThrownBy(() -> arrayColumns.getArrayType(List.class)).isInstanceOf(IllegalArgumentException.class);
}
}

View File

@@ -33,4 +33,13 @@ public class SqlServerDialectUnitTests {
assertThat(holder.isSimpleType(UUID.class)).isTrue();
}
@Test // gh-30
public void shouldNotSupportArrays() {
ArrayColumns arrayColumns = SqlServerDialect.INSTANCE.getArraySupport();
assertThat(arrayColumns.isSupported()).isFalse();
assertThatThrownBy(() -> arrayColumns.getArrayType(String.class)).isInstanceOf(UnsupportedOperationException.class);
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2018 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.function;
import static org.assertj.core.api.Assertions.*;
import io.r2dbc.spi.ConnectionFactory;
import lombok.AllArgsConstructor;
import reactor.test.StepVerifier;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.r2dbc.testing.ExternalDatabase;
import org.springframework.data.r2dbc.testing.PostgresTestSupport;
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Integration tests for PostgreSQL-specific features such as array support.
*
* @author Mark Paluch
*/
public class PostgresIntegrationTests extends R2dbcIntegrationTestSupport {
@ClassRule public static final ExternalDatabase database = PostgresTestSupport.database();
DataSource dataSource = PostgresTestSupport.createDataSource(database);
ConnectionFactory connectionFactory = PostgresTestSupport.createConnectionFactory(database);
JdbcTemplate template = createJdbcTemplate(dataSource);
DatabaseClient client = DatabaseClient.create(connectionFactory);
@Before
public void before() {
template.execute("DROP TABLE IF EXISTS with_arrays");
template.execute("CREATE TABLE with_arrays (" //
+ "boxed_array INT[]," //
+ "primitive_array INT[]," //
+ "multidimensional_array INT[]," //
+ "collection_array INT[][])");
}
@Test // gh-30
@Ignore("https://github.com/r2dbc/r2dbc-postgresql/issues/40, r2dbc-postgresql returns Object[] instead of Integer[]")
public void shouldReadAndWritePrimitiveSingleDimensionArrays() {
EntityWithArrays withArrays = new EntityWithArrays(null, new int[] { 1, 2, 3 }, null, null);
insert(withArrays);
selectAndAssert(actual -> {
assertThat(actual.primitiveArray).containsExactly(1, 2, 3);
});
}
@Test // gh-30
public void shouldReadAndWriteBoxedSingleDimensionArrays() {
EntityWithArrays withArrays = new EntityWithArrays(new Integer[] { 1, 2, 3 }, null, null, null);
insert(withArrays);
selectAndAssert(actual -> {
assertThat(actual.boxedArray).containsExactly(1, 2, 3);
});
}
@Test // gh-30
public void shouldReadAndWriteConvertedDimensionArrays() {
EntityWithArrays withArrays = new EntityWithArrays(null, null, null, Arrays.asList(5, 6, 7));
insert(withArrays);
selectAndAssert(actual -> {
assertThat(actual.collectionArray).containsExactly(5, 6, 7);
});
}
@Test // gh-30
@Ignore("https://github.com/r2dbc/r2dbc-postgresql/issues/42, Multi-dimensional arrays not supported yet")
public void shouldReadAndWriteMultiDimensionArrays() {
EntityWithArrays withArrays = new EntityWithArrays(null, null, new int[][] { { 1, 2, 3 }, { 4, 5 } }, null);
insert(withArrays);
selectAndAssert(actual -> {
assertThat(actual.multidimensionalArray).hasSize(2);
assertThat(actual.multidimensionalArray[0]).containsExactly(1, 2, 3);
assertThat(actual.multidimensionalArray[1]).containsExactly(4, 5, 6);
});
}
private void insert(EntityWithArrays object) {
client.insert() //
.into(EntityWithArrays.class) //
.using(object) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
}
private void selectAndAssert(Consumer<? super EntityWithArrays> assertion) {
client.select() //
.from(EntityWithArrays.class).fetch() //
.first() //
.as(StepVerifier::create) //
.consumeNextWith(assertion).verifyComplete();
}
@Table("with_arrays")
@AllArgsConstructor
static class EntityWithArrays {
Integer[] boxedArray;
int[] primitiveArray;
int[][] multidimensionalArray;
List<Integer> collectionArray;
}
}

View File

@@ -8,6 +8,7 @@ import io.r2dbc.spi.RowMetadata;
import lombok.RequiredArgsConstructor;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -20,6 +21,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEnti
* Unit tests for {@link EntityRowMapper}.
*
* @author Mark Paluch
* @author Jens Schauder
*/
@RunWith(MockitoJUnitRunner.class)
public class EntityRowMapperUnitTests {
@@ -73,7 +75,7 @@ public class EntityRowMapperUnitTests {
public void shouldConvertArrayToSet() {
EntityRowMapper<EntityWithCollection> mapper = getRowMapper(EntityWithCollection.class);
when(rowMock.get("integerSet")).thenReturn((new int[] { 3, 14 }));
when(rowMock.get("integer_set")).thenReturn((new int[] { 3, 14 }));
EntityWithCollection result = mapper.apply(rowMock, metadata);
assertThat(result.integerSet).contains(3, 14);
@@ -83,7 +85,7 @@ public class EntityRowMapperUnitTests {
public void shouldConvertArrayMembers() {
EntityRowMapper<EntityWithCollection> mapper = getRowMapper(EntityWithCollection.class);
when(rowMock.get("primitiveIntegers")).thenReturn((new long[] { 3L, 14L }));
when(rowMock.get("primitive_integers")).thenReturn((new Long[] { 3L, 14L }));
EntityWithCollection result = mapper.apply(rowMock, metadata);
assertThat(result.primitiveIntegers).contains(3, 14);
@@ -93,11 +95,12 @@ public class EntityRowMapperUnitTests {
public void shouldConvertArrayToBoxedArray() {
EntityRowMapper<EntityWithCollection> mapper = getRowMapper(EntityWithCollection.class);
when(rowMock.get("boxedIntegers")).thenReturn((new int[] { 3, 11 }));
when(rowMock.get("boxed_integers")).thenReturn((new int[] { 3, 11 }));
EntityWithCollection result = mapper.apply(rowMock, metadata);
assertThat(result.boxedIntegers).contains(3, 11);
}
@SuppressWarnings("unchecked")
private <T> EntityRowMapper<T> getRowMapper(Class<T> type) {
RelationalPersistentEntity<T> entity = (RelationalPersistentEntity<T>) strategy.getMappingContext()