#423 - Add r2dbc-postgresql Geotypes to simple types.
R2DBC Postgres Geo-types are now considered simple types that are passed-thru to the driver without further mapping. Types such as io.r2dbc.postgresql.codec.Circle or io.r2dbc.postgresql.codec.Box can be used directly in domain models and as bind parameters.
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -35,7 +35,7 @@
|
||||
<r2dbc-spi-test.version>0.8.0.RELEASE</r2dbc-spi-test.version>
|
||||
<mssql-jdbc.version>7.1.2.jre8-preview</mssql-jdbc.version>
|
||||
<mariadb-jdbc.version>2.5.4</mariadb-jdbc.version>
|
||||
<r2dbc-releasetrain.version>Arabba-SR6</r2dbc-releasetrain.version>
|
||||
<r2dbc-releasetrain.version>Arabba-BUILD-SNAPSHOT</r2dbc-releasetrain.version>
|
||||
<reactive-streams.version>1.0.3</reactive-streams.version>
|
||||
<netty>4.1.47.Final</netty>
|
||||
</properties>
|
||||
@@ -221,7 +221,7 @@
|
||||
<dependency>
|
||||
<groupId>io.r2dbc</groupId>
|
||||
<artifactId>r2dbc-postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -3,15 +3,28 @@ package org.springframework.data.r2dbc.dialect;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.relational.core.dialect.ArrayColumns;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -25,15 +38,25 @@ public class PostgresDialect extends org.springframework.data.relational.core.di
|
||||
|
||||
private static final Set<Class<?>> SIMPLE_TYPES;
|
||||
|
||||
private static final boolean GEO_TYPES_PRESENT = ClassUtils.isPresent("io.r2dbc.postgresql.codec.Polygon",
|
||||
PostgresDialect.class.getClassLoader());
|
||||
|
||||
static {
|
||||
|
||||
Set<Class<?>> simpleTypes = new HashSet<>(Arrays.asList(UUID.class, URL.class, URI.class, InetAddress.class));
|
||||
|
||||
if (ClassUtils.isPresent("io.r2dbc.postgresql.codec.Json", PostgresDialect.class.getClassLoader())) {
|
||||
// conditional Postgres JSON support.
|
||||
ifClassPresent("io.r2dbc.postgresql.codec.Json", simpleTypes::add);
|
||||
|
||||
simpleTypes
|
||||
.add(ClassUtils.resolveClassName("io.r2dbc.postgresql.codec.Json", PostgresDialect.class.getClassLoader()));
|
||||
}
|
||||
// conditional Postgres Geo support.
|
||||
Stream.of("io.r2dbc.postgresql.codec.Box", //
|
||||
"io.r2dbc.postgresql.codec.Circle", //
|
||||
"io.r2dbc.postgresql.codec.Line", //
|
||||
"io.r2dbc.postgresql.codec.Lseg", //
|
||||
"io.r2dbc.postgresql.codec.Point", //
|
||||
"io.r2dbc.postgresql.codec.Path", //
|
||||
"io.r2dbc.postgresql.codec.Polygon") //
|
||||
.forEach(s -> ifClassPresent(s, simpleTypes::add));
|
||||
|
||||
SIMPLE_TYPES = simpleTypes;
|
||||
}
|
||||
@@ -76,6 +99,23 @@ public class PostgresDialect extends org.springframework.data.relational.core.di
|
||||
return this.arrayColumns.get();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.dialect.Dialect#getConverters()
|
||||
*/
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
|
||||
if (GEO_TYPES_PRESENT) {
|
||||
return Arrays.asList(FromPostgresPointConverter.INSTANCE, ToPostgresPointConverter.INSTANCE, //
|
||||
FromPostgresCircleConverter.INSTANCE, ToPostgresCircleConverter.INSTANCE, //
|
||||
FromPostgresBoxConverter.INSTANCE, ToPostgresBoxConverter.INSTANCE, //
|
||||
FromPostgresPolygonConverter.INSTANCE, ToPostgresPolygonConverter.INSTANCE);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static class R2dbcArrayColumns implements ArrayColumns {
|
||||
|
||||
private final ArrayColumns delegate;
|
||||
@@ -107,4 +147,126 @@ public class PostgresDialect extends org.springframework.data.relational.core.di
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the class is present on the class path, invoke the specified consumer {@code action} with the class object,
|
||||
* otherwise do nothing.
|
||||
*
|
||||
* @param action block to be executed if a value is present.
|
||||
*/
|
||||
private static void ifClassPresent(String className, Consumer<Class<?>> action) {
|
||||
|
||||
if (ClassUtils.isPresent(className, PostgresDialect.class.getClassLoader())) {
|
||||
action.accept(ClassUtils.resolveClassName(className, PostgresDialect.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
private enum FromPostgresBoxConverter implements Converter<io.r2dbc.postgresql.codec.Box, Box> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Box convert(io.r2dbc.postgresql.codec.Box source) {
|
||||
return new Box(FromPostgresPointConverter.INSTANCE.convert(source.getA()),
|
||||
FromPostgresPointConverter.INSTANCE.convert(source.getB()));
|
||||
}
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
private enum ToPostgresBoxConverter implements Converter<Box, io.r2dbc.postgresql.codec.Box> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public io.r2dbc.postgresql.codec.Box convert(Box source) {
|
||||
return io.r2dbc.postgresql.codec.Box.of(ToPostgresPointConverter.INSTANCE.convert(source.getFirst()),
|
||||
ToPostgresPointConverter.INSTANCE.convert(source.getSecond()));
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
private enum FromPostgresCircleConverter implements Converter<io.r2dbc.postgresql.codec.Circle, Circle> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Circle convert(io.r2dbc.postgresql.codec.Circle source) {
|
||||
return new Circle(source.getCenter().getX(), source.getCenter().getY(), source.getRadius());
|
||||
}
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
private enum ToPostgresCircleConverter implements Converter<Circle, io.r2dbc.postgresql.codec.Circle> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public io.r2dbc.postgresql.codec.Circle convert(Circle source) {
|
||||
return io.r2dbc.postgresql.codec.Circle.of(source.getCenter().getX(), source.getCenter().getY(),
|
||||
source.getRadius().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
private enum FromPostgresPolygonConverter implements Converter<io.r2dbc.postgresql.codec.Polygon, Polygon> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Polygon convert(io.r2dbc.postgresql.codec.Polygon source) {
|
||||
|
||||
List<io.r2dbc.postgresql.codec.Point> sourcePoints = source.getPoints();
|
||||
List<Point> targetPoints = new ArrayList<>(sourcePoints.size());
|
||||
|
||||
for (io.r2dbc.postgresql.codec.Point sourcePoint : sourcePoints) {
|
||||
targetPoints.add(FromPostgresPointConverter.INSTANCE.convert(sourcePoint));
|
||||
}
|
||||
|
||||
return new Polygon(targetPoints);
|
||||
}
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
private enum ToPostgresPolygonConverter implements Converter<Polygon, io.r2dbc.postgresql.codec.Polygon> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public io.r2dbc.postgresql.codec.Polygon convert(Polygon source) {
|
||||
|
||||
List<Point> sourcePoints = source.getPoints();
|
||||
List<io.r2dbc.postgresql.codec.Point> targetPoints = new ArrayList<>(sourcePoints.size());
|
||||
|
||||
for (Point sourcePoint : sourcePoints) {
|
||||
targetPoints.add(ToPostgresPointConverter.INSTANCE.convert(sourcePoint));
|
||||
}
|
||||
|
||||
return io.r2dbc.postgresql.codec.Polygon.of(targetPoints);
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
private enum FromPostgresPointConverter implements Converter<io.r2dbc.postgresql.codec.Point, Point> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Point convert(io.r2dbc.postgresql.codec.Point source) {
|
||||
return new Point(source.getX(), source.getY());
|
||||
}
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
private enum ToPostgresPointConverter implements Converter<Point, io.r2dbc.postgresql.codec.Point> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public io.r2dbc.postgresql.codec.Point convert(Point source) {
|
||||
return io.r2dbc.postgresql.codec.Point.of(source.getX(), source.getY());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,14 @@ import static org.springframework.data.relational.core.query.Criteria.*;
|
||||
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
|
||||
import io.r2dbc.postgresql.codec.Box;
|
||||
import io.r2dbc.postgresql.codec.Circle;
|
||||
import io.r2dbc.postgresql.codec.EnumCodec;
|
||||
import io.r2dbc.postgresql.codec.Line;
|
||||
import io.r2dbc.postgresql.codec.Lseg;
|
||||
import io.r2dbc.postgresql.codec.Path;
|
||||
import io.r2dbc.postgresql.codec.Point;
|
||||
import io.r2dbc.postgresql.codec.Polygon;
|
||||
import io.r2dbc.postgresql.extension.CodecRegistrar;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -36,7 +43,6 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@@ -133,7 +139,6 @@ public class PostgresIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
}
|
||||
|
||||
@Test // gh-411
|
||||
@Ignore("Depends on https://github.com/pgjdbc/r2dbc-postgresql/issues/301")
|
||||
public void shouldWriteAndReadEnumValuesUsingDriverInternals() {
|
||||
|
||||
CodecRegistrar codecRegistrar = EnumCodec.builder().withEnum("state_enum", State.class).build();
|
||||
@@ -183,6 +188,63 @@ public class PostgresIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
}
|
||||
|
||||
@Test // gh-423
|
||||
public void shouldReadAndWriteGeoTypes() {
|
||||
|
||||
GeoType geoType = new GeoType();
|
||||
geoType.thePoint = Point.of(1, 2);
|
||||
geoType.theBox = Box.of(Point.of(3, 4), Point.of(1, 2));
|
||||
geoType.theCircle = Circle.of(1, 2, 3);
|
||||
geoType.theLine = Line.of(1, 2, 3, 4);
|
||||
geoType.theLseg = Lseg.of(Point.of(1, 2), Point.of(3, 4));
|
||||
geoType.thePath = Path.open(Point.of(1, 2), Point.of(3, 4));
|
||||
geoType.thePolygon = Polygon.of(Point.of(1, 2), Point.of(3, 4), Point.of(5, 6), Point.of(1, 2));
|
||||
geoType.springDataBox = new org.springframework.data.geo.Box(new org.springframework.data.geo.Point(3, 4),
|
||||
new org.springframework.data.geo.Point(1, 2));
|
||||
geoType.springDataCircle = new org.springframework.data.geo.Circle(1, 2, 3);
|
||||
geoType.springDataPoint = new org.springframework.data.geo.Point(1, 2);
|
||||
geoType.springDataPolygon = new org.springframework.data.geo.Polygon(new org.springframework.data.geo.Point(1, 2),
|
||||
new org.springframework.data.geo.Point(3, 4), new org.springframework.data.geo.Point(5, 6),
|
||||
new org.springframework.data.geo.Point(1, 2));
|
||||
|
||||
template.execute("DROP TABLE IF EXISTS geo_type");
|
||||
template.execute("CREATE TABLE geo_type (" //
|
||||
+ "id serial PRIMARY KEY," //
|
||||
+ "the_point POINT," //
|
||||
+ "the_box BOX," //
|
||||
+ "the_circle CIRCLE," //
|
||||
+ "the_line LINE," //
|
||||
+ "the_lseg LSEG," //
|
||||
+ "the_path PATH," //
|
||||
+ "the_polygon POLYGON," //
|
||||
+ "spring_data_box BOX," //
|
||||
+ "spring_data_circle CIRCLE," //
|
||||
+ "spring_data_point POINT," //
|
||||
+ "spring_data_polygon POLYGON" //
|
||||
+ ")");
|
||||
|
||||
R2dbcEntityTemplate template = new R2dbcEntityTemplate(client,
|
||||
new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE));
|
||||
|
||||
GeoType saved = template.insert(geoType).block();
|
||||
GeoType loaded = template.select(Query.empty(), GeoType.class) //
|
||||
.blockLast();
|
||||
|
||||
assertThat(saved.id).isEqualTo(loaded.id);
|
||||
assertThat(saved.thePoint).isEqualTo(loaded.thePoint);
|
||||
assertThat(saved.theBox).isEqualTo(loaded.theBox);
|
||||
assertThat(saved.theCircle).isEqualTo(loaded.theCircle);
|
||||
assertThat(saved.theLine).isEqualTo(loaded.theLine);
|
||||
assertThat(saved.theLseg).isEqualTo(loaded.theLseg);
|
||||
assertThat(saved.thePath).isEqualTo(loaded.thePath);
|
||||
assertThat(saved.thePolygon).isEqualTo(loaded.thePolygon);
|
||||
assertThat(saved.springDataBox).isEqualTo(loaded.springDataBox);
|
||||
assertThat(saved.springDataCircle).isEqualTo(loaded.springDataCircle);
|
||||
assertThat(saved.springDataPoint).isEqualTo(loaded.springDataPoint);
|
||||
assertThat(saved.springDataPolygon).isEqualTo(loaded.springDataPolygon);
|
||||
assertThat(saved).isEqualTo(loaded);
|
||||
}
|
||||
|
||||
private void insert(EntityWithArrays object) {
|
||||
|
||||
client.insert() //
|
||||
@@ -219,4 +281,23 @@ public class PostgresIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
int[][] multidimensionalArray;
|
||||
List<Integer> collectionArray;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class GeoType {
|
||||
|
||||
@Id Integer id;
|
||||
|
||||
Point thePoint;
|
||||
Box theBox;
|
||||
Circle theCircle;
|
||||
Line theLine;
|
||||
Lseg theLseg;
|
||||
Path thePath;
|
||||
Polygon thePolygon;
|
||||
|
||||
org.springframework.data.geo.Box springDataBox;
|
||||
org.springframework.data.geo.Circle springDataCircle;
|
||||
org.springframework.data.geo.Point springDataPoint;
|
||||
org.springframework.data.geo.Polygon springDataPolygon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class PostgresTestSupport {
|
||||
.database("postgres") //
|
||||
.username("postgres") //
|
||||
.password("") //
|
||||
.jdbcUrl("jdbc:postgresql://localhost/postgres") //
|
||||
.jdbcUrl("jdbc:postgresql://localhost:5432/postgres") //
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user