From 5bae2d81967889065d8bff5e8503a417a4dc804c Mon Sep 17 00:00:00 2001 From: Karel Maesen Date: Sat, 4 Sep 2021 13:07:23 +0200 Subject: [PATCH] HHH-14808 Basic SpatialPredicates --- hibernate-spatial/gradle.properties | 0 .../java/org/hibernate/spatial/Spatial.java | 1 - .../postgis/PGGeometryTypeDescriptor.java | 18 +- .../predicate/GeolatteSpatialPredicates.java | 49 +++--- .../predicate/JTSSpatialPredicates.java | 49 +++--- .../integration/{functions => }/Model.java | 34 +++- .../integration/SpatialTestDataProvider.java | 3 + .../functions/CommonFunctionTests.java | 15 +- .../functions/FunctionTestTemplate.java | 1 + .../GeolatteSpatialPredicatesTest.java | 154 ++++++++++++++++++ .../spatial/testing/HSReflectionUtil.java | 33 ++++ .../spatial/testing/IsSupportedBySpatial.java | 7 + .../testing/datareader/TestSupport.java | 6 + .../testing/dialects/PredicateRegexes.java | 61 +++++++ .../cockroachdb/CockroachDBTestSupport.java | 8 + .../dialects/postgis/PostgisTestSupport.java | 6 + .../src/test/resources/hibernate.properties | 1 - 17 files changed, 372 insertions(+), 74 deletions(-) create mode 100644 hibernate-spatial/gradle.properties rename hibernate-spatial/src/test/java/org/hibernate/spatial/integration/{functions => }/Model.java (52%) create mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/GeolatteSpatialPredicatesTest.java create mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/testing/HSReflectionUtil.java create mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/PredicateRegexes.java diff --git a/hibernate-spatial/gradle.properties b/hibernate-spatial/gradle.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/Spatial.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/Spatial.java index 4e59bc96c1..f9fddeac3d 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/Spatial.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/Spatial.java @@ -13,7 +13,6 @@ package org.hibernate.spatial; * @author Karel Maesen, Geovise BVBA * creation-date: 4/18/13 */ -@Deprecated public interface Spatial { } diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java index 33a7d981b5..85f331badc 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java @@ -13,12 +13,14 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.BasicBinder; import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.geolatte.geom.ByteBuffer; @@ -29,6 +31,7 @@ import org.geolatte.geom.codec.WkbDecoder; import org.geolatte.geom.codec.WkbEncoder; import org.geolatte.geom.codec.Wkt; import org.geolatte.geom.codec.WktDecoder; +import org.geolatte.geom.jts.JTS; import org.postgresql.util.PGobject; /** @@ -46,6 +49,18 @@ public class PGGeometryTypeDescriptor implements JdbcTypeDescriptor { // Type descriptor instance using EWKB v2 (postgis versions >= 2.2.2, see: https://trac.osgeo.org/postgis/ticket/3181) public static final PGGeometryTypeDescriptor INSTANCE_WKB_2 = new PGGeometryTypeDescriptor( Wkb.Dialect.POSTGIS_EWKB_2 ); + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaTypeDescriptor javaTypeDescriptor) { + if ( javaTypeDescriptor instanceof GeolatteGeometryJavaTypeDescriptor ) { + return (value, dialect, wrapperOptions) -> "ST_GeomFromEWKT('" + value + "')"; + } + return (value, dialect, wrapperOptions) -> "ST_GeomFromEWKT('" + jts2Gl( value ) + "')"; + } + + private Geometry jts2Gl(T value) { + return JTS.from( (org.locationtech.jts.geom.Geometry) value ); + } + private PGGeometryTypeDescriptor(Wkb.Dialect dialect) { wkbDialect = dialect; } @@ -78,14 +93,13 @@ public class PGGeometryTypeDescriptor implements JdbcTypeDescriptor { } - @Override public int getJdbcTypeCode() { return Types.OTHER; } @Override - public int getDefaultSqlTypeCode(){ + public int getDefaultSqlTypeCode() { return 5432; } diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/GeolatteSpatialPredicates.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/GeolatteSpatialPredicates.java index f59d7269a7..7b23c28cfc 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/GeolatteSpatialPredicates.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/GeolatteSpatialPredicates.java @@ -10,11 +10,20 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Predicate; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.spatial.SpatialFunction; -import org.geolatte.geom.Envelope; import org.geolatte.geom.Geometry; +import static org.hibernate.spatial.CommonSpatialFunction.ST_CONTAINS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_CROSSES; +import static org.hibernate.spatial.CommonSpatialFunction.ST_DISJOINT; +import static org.hibernate.spatial.CommonSpatialFunction.ST_EQUALS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_INTERSECTS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_OVERLAPS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_TOUCHES; +import static org.hibernate.spatial.CommonSpatialFunction.ST_WITHIN; + /** * {@link JTSSpatialPredicates}, but for geolatte-geom. * @@ -41,7 +50,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.equals.toString(), boolean.class, + criteriaBuilder.function( ST_EQUALS.name(), boolean.class, geometry1, geometry2 ) ); @@ -82,9 +91,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.within.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_WITHIN.name(), boolean.class, geometry1, geometry2 ) ); } @@ -102,8 +109,7 @@ public class GeolatteSpatialPredicates { public static Predicate within( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return within( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) + return within( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } @@ -123,7 +129,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.contains.toString(), boolean.class, + criteriaBuilder.function( ST_CONTAINS.name(), boolean.class, geometry1, geometry2 ) ); @@ -164,7 +170,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.crosses.toString(), boolean.class, + criteriaBuilder.function( ST_CROSSES.name(), boolean.class, geometry1, geometry2 ) ); @@ -205,9 +211,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.disjoint.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_DISJOINT.name(), boolean.class, geometry1, geometry2 ) ); } @@ -246,9 +250,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.intersects.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_INTERSECTS.name(), boolean.class, geometry1, geometry2 ) ); } @@ -266,8 +268,7 @@ public class GeolatteSpatialPredicates { public static Predicate intersects( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return intersects( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) + return intersects( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } @@ -287,9 +288,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.overlaps.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_OVERLAPS.name(), boolean.class, geometry1, geometry2 ) ); } @@ -307,8 +306,7 @@ public class GeolatteSpatialPredicates { public static Predicate overlaps( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return overlaps( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) + return overlaps( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } @@ -328,9 +326,7 @@ public class GeolatteSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.touches.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_TOUCHES.name(), boolean.class, geometry1, geometry2 ) ); } @@ -348,8 +344,7 @@ public class GeolatteSpatialPredicates { public static Predicate touches( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return touches( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) + return touches( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/JTSSpatialPredicates.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/JTSSpatialPredicates.java index 057a1a6752..0f94a38825 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/JTSSpatialPredicates.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/predicate/JTSSpatialPredicates.java @@ -10,12 +10,20 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Predicate; -import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.spatial.SpatialFunction; -import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; +import static org.hibernate.spatial.CommonSpatialFunction.ST_CONTAINS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_CROSSES; +import static org.hibernate.spatial.CommonSpatialFunction.ST_DISJOINT; +import static org.hibernate.spatial.CommonSpatialFunction.ST_EQUALS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_INTERSECTS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_OVERLAPS; +import static org.hibernate.spatial.CommonSpatialFunction.ST_TOUCHES; +import static org.hibernate.spatial.CommonSpatialFunction.ST_WITHIN; + /** * A factory for spatial JPA Criteria API {@link Predicate}s. * @@ -40,7 +48,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.equals.toString(), boolean.class, geometry1, geometry2 ) + criteriaBuilder.function( ST_EQUALS.name(), boolean.class, geometry1, geometry2 ) ); } @@ -75,7 +83,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.within.toString(), boolean.class, geometry1, geometry2 ) + criteriaBuilder.function( ST_WITHIN.name(), boolean.class, geometry1, geometry2 ) ); } @@ -110,9 +118,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.contains.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_CONTAINS.name(), boolean.class, geometry1, geometry2 ) ); } @@ -149,7 +155,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.crosses.toString(), boolean.class, + criteriaBuilder.function( ST_CROSSES.name(), boolean.class, geometry1, geometry2 ) ); @@ -188,9 +194,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.disjoint.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_DISJOINT.name(), boolean.class, geometry1, geometry2 ) ); } @@ -227,9 +231,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.intersects.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_INTERSECTS.name(), boolean.class, geometry1, geometry2 ) ); } @@ -247,8 +249,7 @@ public class JTSSpatialPredicates { public static Predicate intersects( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return intersects( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) + return intersects( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } @@ -266,8 +267,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.overlaps.toString(), boolean.class, - geometry1, geometry2 + criteriaBuilder.function( ST_OVERLAPS.name(), boolean.class, geometry1, geometry2 ) ); } @@ -286,8 +286,7 @@ public class JTSSpatialPredicates { public static Predicate overlaps( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return overlaps( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) + return overlaps( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } @@ -305,9 +304,7 @@ public class JTSSpatialPredicates { Expression geometry2) { return booleanExpressionToPredicate( criteriaBuilder, - criteriaBuilder.function( SpatialFunction.touches.toString(), boolean.class, - geometry1, geometry2 - ) + criteriaBuilder.function( ST_TOUCHES.name(), boolean.class, geometry1, geometry2 ) ); } @@ -325,11 +322,11 @@ public class JTSSpatialPredicates { public static Predicate touches( CriteriaBuilder criteriaBuilder, Expression geometry1, Geometry geometry2) { - return touches( criteriaBuilder, geometry1, - criteriaBuilder.literal( geometry2 ) - ); + return touches( criteriaBuilder, geometry1, criteriaBuilder.literal( geometry2 ) ); } + + // /** // * Create a predicate for testing the arguments for bounding box overlap constraint. // * diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/Model.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/Model.java similarity index 52% rename from hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/Model.java rename to hibernate-spatial/src/test/java/org/hibernate/spatial/integration/Model.java index 6df714a2f5..0f19fc00de 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/Model.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/Model.java @@ -5,7 +5,14 @@ * See the lgpl.txt file in the root directory or . */ -package org.hibernate.spatial.integration.functions; +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.integration; import java.util.function.Function; @@ -21,36 +28,45 @@ import org.geolatte.geom.jts.JTS; * T */ @SuppressWarnings({ "unchecked", "rawtypes" }) -enum Model { +public enum Model { JTSMODEL( JtsGeomEntity.class, - JTS::to + JTS::to, + org.locationtech.jts.geom.Geometry.class ), GLMODEL( GeomEntity.class, - geom -> geom + geom -> geom, + Geometry.class ); /** * Test Entity class */ - final Class entityClass; + public final Class entityClass; /** - * How to translate from Geolatte Geometry class to the object class - * expected by the entity geom property + * How to translate from Geolatte Geometry to the geometry type + * expected by the entity in this model */ - final Function from; + public final Function from; + + /** + * The geometry type in this model + */ + public final Class geometryClass; Model( Class entityClass, - Function from + Function from, + Class geometryClass ) { this.entityClass = entityClass; this.from = from; + this.geometryClass = geometryClass; } } diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java index 14b2932584..78ea58e1c7 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java @@ -18,6 +18,7 @@ import org.hibernate.spatial.testing.datareader.TestData; import org.hibernate.spatial.testing.datareader.TestDataElement; import org.hibernate.spatial.testing.datareader.TestSupport; import org.hibernate.spatial.testing.dialects.NativeSQLTemplates; +import org.hibernate.spatial.testing.dialects.PredicateRegexes; import org.hibernate.spatial.testing.domain.GeomEntityLike; import org.hibernate.testing.orm.junit.DialectContext; @@ -29,6 +30,7 @@ import static org.hibernate.spatial.testing.datareader.TestSupport.TestDataPurpo public class SpatialTestDataProvider { protected final static String JTS = "jts"; protected final NativeSQLTemplates templates; + protected final PredicateRegexes predicateRegexes; protected final Map hqlOverrides; private final TestData funcTestData; protected TestData testData; @@ -38,6 +40,7 @@ public class SpatialTestDataProvider { try { TestSupport support = TestSupportFactories.instance().getTestSupportFactory( DialectContext.getDialect() ); templates = support.templates(); + predicateRegexes = support.predicateRegexes(); hqlOverrides = support.hqlOverrides(); codec = support.codec(); testData = support.createTestData( StoreRetrieveData ); diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java index 76fd4c0ff7..27c737db61 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java @@ -16,8 +16,10 @@ package org.hibernate.spatial.integration.functions; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.stream.Stream; +import org.hibernate.spatial.integration.Model; import org.hibernate.spatial.testing.IsSupportedBySpatial; import org.hibernate.spatial.testing.SpatialTestBase; import org.hibernate.spatial.testing.datareader.TestSupport; @@ -42,14 +44,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * *

*/ -@SuppressWarnings("ALL") + +@SuppressWarnings("rawtypes") @RequiresDialectFeature(feature = IsSupportedBySpatial.class) @SessionFactory public class CommonFunctionTests extends SpatialTestBase { public final static TestSupport.TestDataPurpose PURPOSE = TestSupport.TestDataPurpose.SpatialFunctionsData; - List received; List expected; @@ -58,7 +60,6 @@ public class CommonFunctionTests extends SpatialTestBase { return PURPOSE; } - @TestFactory public Stream testFunction() { @@ -73,13 +74,12 @@ public class CommonFunctionTests extends SpatialTestBase { } - protected Stream buildTests(FunctionTestTemplate template) { return Stream.of( template.getFunctionName(), template.getAltFunctionName() ) - .filter( s -> s != null ) + .filter( Objects::nonNull ) .map( fn -> DynamicTest.dynamicTest( displayName( template, fn ), executableTest( template, fn ) ) ); @@ -94,12 +94,11 @@ public class CommonFunctionTests extends SpatialTestBase { ); } - protected Executable executableTest(FunctionTestTemplate template, String fnName) { - Executable testF = () -> { + protected Executable executableTest(FunctionTestTemplate template, String fnName) { + return () -> { expected = template.executeNativeQuery( scope ); received = template.executeHQL( scope, fnName ); assertEquals( expected, received ); }; - return testF; } } diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java index 98080076ff..3878158308 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java @@ -25,6 +25,7 @@ import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; import org.hibernate.spatial.CommonSpatialFunction; import org.hibernate.spatial.GeomCodec; +import org.hibernate.spatial.integration.Model; import org.hibernate.spatial.testing.HQLTemplate; import org.hibernate.spatial.testing.NativeSQLTemplate; import org.hibernate.type.StandardBasicTypes; diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/GeolatteSpatialPredicatesTest.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/GeolatteSpatialPredicatesTest.java new file mode 100644 index 0000000000..e46dee7271 --- /dev/null +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/predicates/GeolatteSpatialPredicatesTest.java @@ -0,0 +1,154 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.integration.predicates; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Root; + +import org.hibernate.spatial.integration.Model; +import org.hibernate.spatial.predicate.GeolatteSpatialPredicates; +import org.hibernate.spatial.predicate.JTSSpatialPredicates; +import org.hibernate.spatial.testing.HSReflectionUtil; +import org.hibernate.spatial.testing.IsSupportedBySpatial; +import org.hibernate.spatial.testing.SpatialTestBase; +import org.hibernate.spatial.testing.datareader.TestSupport; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.function.Executable; +import org.junit.platform.commons.JUnitException; + +import org.geolatte.geom.G2D; +import org.geolatte.geom.Polygon; + +import static org.geolatte.geom.builder.DSL.g; +import static org.geolatte.geom.builder.DSL.polygon; +import static org.geolatte.geom.builder.DSL.ring; +import static org.geolatte.geom.crs.CoordinateReferenceSystems.WGS84; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({ "unchecked", "rawtypes" }) +@RequiresDialectFeature(feature = IsSupportedBySpatial.class) +@SessionFactory +public class GeolatteSpatialPredicatesTest extends SpatialTestBase { + + public final static TestSupport.TestDataPurpose PURPOSE = TestSupport.TestDataPurpose.SpatialFunctionsData; + + static final Polygon filter = polygon( + WGS84, + ring( g( 0, 0 ), g( 0, 10 ), g( 10, 10 ), g( 10, 0 ), g( 0, 0 ) ) + ); + + @Override + public TestSupport.TestDataPurpose purpose() { + return PURPOSE; + } + + @TestFactory + public Stream testFactory() { + return + predicateRegexes.all() + .flatMap( entry -> Stream.of( + new Args( entry.getKey(), entry.getValue(), Model.GLMODEL ), + new Args( entry.getKey(), entry.getValue(), Model.JTSMODEL ) + ) ) + .map( args -> DynamicTest.dynamicTest( + displayName( args ), + testPredicate( + args.method, + args.regex, + args.model + ) + ) ); + } + + @SuppressWarnings("rawtypes") + public Executable testPredicate(final String key, final String regex, Model model) { + return () -> scope.inSession( session -> { + SQLStatementInspector inspector = SQLStatementInspector.extractFromSession( session ); + inspector.clear(); + CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + CriteriaQuery query = getCriteriaQuery( + key, + criteriaBuilder, + model + ); + List resultList = session.createQuery( query ).getResultList(); + String stmt = inspector.getSqlQueries() + .get( 0 ) + .toLowerCase( Locale.ROOT ); + //TODO -- can't we use a (hamcrest) matcher here? + assertTrue( stmt.matches( regex ) ); + } ); + } + + private CriteriaQuery getCriteriaQuery( + String key, + CriteriaBuilder criteriaBuilder, Model model) { + CriteriaQuery query = criteriaBuilder.createQuery( model.entityClass ); + Root root = query.from( model.entityClass ); + Method method = predicateMethod( key, model ); + finalizeQuery( criteriaBuilder, model, query, root, method ); + return query; + } + + private void finalizeQuery( + CriteriaBuilder criteriaBuilder, + Model model, + CriteriaQuery query, + Root root, + Method method) { + try { + query.select( root ) + .where( (Expression) method.invoke( + null, criteriaBuilder, root.get( "geom" ), model.from.apply( filter ) ) ); + } + catch (IllegalAccessException | InvocationTargetException e) { + throw new JUnitException( "Failure to invoke Geometry Predicate", e ); + } + } + + @SuppressWarnings("rawtypes") + private Method predicateMethod(String key, Model model) { + Class predicateFactoryClass = model == Model.JTSMODEL ? + JTSSpatialPredicates.class : + GeolatteSpatialPredicates.class; + return HSReflectionUtil.getStaticMethod( + predicateFactoryClass, + key, + CriteriaBuilder.class, Expression.class, model.geometryClass + ); + } + + private String displayName(Args args) { + return String.format( "Predicate %s on %s", args.method, args.model.entityClass.getSimpleName() ); + } +} + +class Args { + final String method; + final String regex; + final Model model; + + public Args(String method, String regex, Model model) { + this.method = method; + this.regex = regex; + this.model = model; + } + +} \ No newline at end of file diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/HSReflectionUtil.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/HSReflectionUtil.java new file mode 100644 index 0000000000..6dde5c7e6a --- /dev/null +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/HSReflectionUtil.java @@ -0,0 +1,33 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.testing; + +import java.lang.reflect.Method; + +//TODO -- fold this into ReflectionUtil +public class HSReflectionUtil { + + /** + * Get target method + * + * @param target target class + * @param methodName method name + * @param parameterTypes method parameter types + * + * @return return value + */ + public static Method getStaticMethod(Class target, String methodName, Class... parameterTypes) { + try { + return target.getMethod( methodName, parameterTypes ); + } + catch (NoSuchMethodException e) { + throw new IllegalArgumentException( e ); + } + } + +} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/IsSupportedBySpatial.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/IsSupportedBySpatial.java index 960620bbac..7e9f188fa8 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/IsSupportedBySpatial.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/IsSupportedBySpatial.java @@ -13,6 +13,13 @@ import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.testing.orm.junit.DialectFeatureCheck; +/** + * Checks if the Dialect is actually supported by Spatial + * + * Note: the tests in this module need to be explicitly enabled in the gradle build config. So this check is + * maybe no longer needed. + * + */ public class IsSupportedBySpatial implements DialectFeatureCheck { @Override public boolean apply(Dialect dialect) { diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java index 86ff9f9c38..7d5a640c50 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java @@ -22,6 +22,7 @@ import org.hibernate.spatial.CommonSpatialFunction; import org.hibernate.spatial.GeomCodec; import org.hibernate.spatial.testing.AbstractExpectationsFactory; import org.hibernate.spatial.testing.dialects.NativeSQLTemplates; +import org.hibernate.spatial.testing.dialects.PredicateRegexes; /** @@ -36,6 +37,11 @@ public abstract class TestSupport { return null; } + //TODO -- make this abstract + public PredicateRegexes predicateRegexes() { + return null; + } + public Map hqlOverrides() { return new HashMap<>(); } diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/PredicateRegexes.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/PredicateRegexes.java new file mode 100644 index 0000000000..2e0da7e995 --- /dev/null +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/PredicateRegexes.java @@ -0,0 +1,61 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.testing.dialects; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + + +public class PredicateRegexes { + + protected final Map regexes = new HashMap<>(); + + // Note that we alias the function invocation so that + // we can map the return value to the required type + public PredicateRegexes() { + regexes.put( + "overlaps", + "select .* from .* where st_overlaps\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "intersects", + "select .* from .* where st_intersects\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "crosses", + "select .* from .* where st_crosses\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "contains", + "select .* from .* where st_contains\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "disjoint", + "select .* from .* where st_disjoint\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "touches", + "select .* from .* where st_touches\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "within", + "select .* from .* where st_within\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + regexes.put( + "eq", + "select .* from .* where st_equals\\(.*geom\\s*,.*st_geomfromewkt\\(.*\\)\\s*=\\?.*" + ); + + } + + public Stream> all() { + return this.regexes.entrySet().stream(); + } + +} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/cockroachdb/CockroachDBTestSupport.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/cockroachdb/CockroachDBTestSupport.java index 0dfe2cb50f..86e99c3494 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/cockroachdb/CockroachDBTestSupport.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/cockroachdb/CockroachDBTestSupport.java @@ -13,6 +13,7 @@ import org.hibernate.spatial.testing.AbstractExpectationsFactory; import org.hibernate.spatial.testing.datareader.TestData; import org.hibernate.spatial.testing.datareader.TestSupport; import org.hibernate.spatial.testing.dialects.NativeSQLTemplates; +import org.hibernate.spatial.testing.dialects.PredicateRegexes; import org.hibernate.spatial.testing.dialects.postgis.PostgisNativeSQLTemplates; import org.geolatte.geom.Geometry; @@ -25,6 +26,11 @@ public class CockroachDBTestSupport extends TestSupport { return new PostgisNativeSQLTemplates(); } + @Override + public PredicateRegexes predicateRegexes() { + return new CrPredicateRegexes(); + } + @Override public TestData createTestData(TestDataPurpose purpose) { switch ( purpose ) { @@ -50,3 +56,5 @@ public class CockroachDBTestSupport extends TestSupport { } } + +class CrPredicateRegexes extends PredicateRegexes{ } \ No newline at end of file diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/postgis/PostgisTestSupport.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/postgis/PostgisTestSupport.java index f590389ec4..77bb4a71b0 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/postgis/PostgisTestSupport.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/postgis/PostgisTestSupport.java @@ -18,6 +18,7 @@ import org.hibernate.spatial.testing.AbstractExpectationsFactory; import org.hibernate.spatial.testing.datareader.TestData; import org.hibernate.spatial.testing.datareader.TestSupport; import org.hibernate.spatial.testing.dialects.NativeSQLTemplates; +import org.hibernate.spatial.testing.dialects.PredicateRegexes; import org.geolatte.geom.Geometry; import org.geolatte.geom.codec.Wkt; @@ -35,6 +36,9 @@ public class PostgisTestSupport extends TestSupport { return new PostgisNativeSQLTemplates(); } + @Override + public PredicateRegexes predicateRegexes(){ return new PostgisPredicateRegexes();} + //TODO put this in its own class (analogous to NativeSQLTemplates) @Override public Map hqlOverrides() { @@ -68,3 +72,5 @@ public class PostgisTestSupport extends TestSupport { } } + +class PostgisPredicateRegexes extends PredicateRegexes { } \ No newline at end of file diff --git a/hibernate-spatial/src/test/resources/hibernate.properties b/hibernate-spatial/src/test/resources/hibernate.properties index 7c6759a22e..cd06430867 100644 --- a/hibernate-spatial/src/test/resources/hibernate.properties +++ b/hibernate-spatial/src/test/resources/hibernate.properties @@ -34,7 +34,6 @@ hibernate.cache.region.factory_class org.hibernate.testing.cache.CachingRegionFa #hibernate.connection.username hibernate_orm_test #hibernate.connection.password hibernate_orm_test ->>>>>>> 5aae4945cb (Update CockroachDB support for 6) ## GeoDb (H2 spatial extension) ## #hibernate.dialect org.hibernate.spatial.dialect.h2geodb.GeoDBDialect