HHH-13799 : Criteria API support for Hibernate Spatial (#3159)

* HHH-13799 : Criteria API support for Hibernate Spatial

Co-authored-by: Karel Maesen <karel@geovise.com>
This commit is contained in:
Daniel Shuy 2020-02-24 21:42:48 +08:00 committed by GitHub
parent b914b02dca
commit cab651e194
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1087 additions and 13 deletions

View File

@ -10,7 +10,7 @@ hibernate.dialect org.hibernate.spatial.dialect.db2.DB2SpatialDialect
hibernate.connection.driver_class com.ibm.db2.jcc.DB2Driver
hibernate.connection.url jdbc:db2://localhost:50000/hibern8
hibernate.connection.username db2inst1
hibernate.connection.password password
hibernate.connection.password oPucroAsMAgL
hibernate.connection.pool_size 5

View File

@ -9,7 +9,7 @@ hibernate.dialect org.hibernate.spatial.dialect.sqlserver.SqlServer2012SpatialDi
hibernate.connection.driver_class com.microsoft.sqlserver.jdbc.SQLServerDriver
hibernate.connection.url jdbc:sqlserver://localhost:1433;databaseName=TestDb
hibernate.connection.username hibern8
hibernate.connection.password hibern8Pass
hibernate.connection.password langpaswoord123A%1
hibernate.connection.pool_size 5

View File

@ -1,5 +1,8 @@
#! /bin/bash
## The same effect can be achieved by setting the system properties
# in ~/.gradle/gradle.properties
TASK=matrix
if [[ -n $@ ]]; then
TASK="$@"

View File

@ -164,8 +164,16 @@ public enum SpatialFunction {
/**
* the extents function
*/
extent( "common" );
extent( "common" ),
/**
* The filter function
* <p>
* <p>Corresponds to the Oracle Spatial's "SDO_FILTER" function, or the "&&" operator of PostGIS.
*/
filter( "filter" ),
;
private final String description;

View File

@ -0,0 +1,18 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.dialect;
/**
* An Interface for {@code SpatialDialect}s that require a custom
* rendering to JPAQL for the filter predicate
* <p>
* Created by Karel Maesen, Geovise BVBA on 09/02/2020.
*/
public interface WithCustomJPAFilter {
String filterExpression(String geometryParam, String filterParam);
}

View File

@ -7,9 +7,15 @@
package org.hibernate.spatial.dialect.h2geodb;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.hibernate.QueryException;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor;
import org.hibernate.spatial.GeolatteGeometryType;
@ -19,6 +25,7 @@ import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.SpatialRelation;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
/**
* Extends the H2Dialect by also including information on spatial functions.
@ -74,6 +81,8 @@ public class GeoDBDialect extends H2Dialect implements SpatialDialect {
registerFunction( "dwithin", new StandardSQLFunction( "ST_DWithin", StandardBasicTypes.BOOLEAN ) );
// Register Spatial Filter function
registerFunction( SpatialFunction.filter.name(), new FilterFunction() );
}
@Override
@ -157,4 +166,26 @@ public class GeoDBDialect extends H2Dialect implements SpatialDialect {
return function != SpatialFunction.difference && ( getFunctions().get( function.toString() ) != null );
}
private static class FilterFunction extends StandardSQLFunction {
public FilterFunction() {
super( "&&" );
}
@Override
public String render(
Type firstArgumentType, List arguments, SessionFactoryImplementor sessionFactory) {
int argumentCount = arguments.size();
if ( argumentCount != 2 ) {
throw new QueryException( String.format( "2 arguments expected, received %d", argumentCount ) );
}
return Stream.of(
String.valueOf( arguments.get( 0 ) ),
getRenderedName( arguments ),
String.valueOf( arguments.get( 1 ) )
).collect( Collectors.joining( " ", "(", ")" ) );
}
}
}

View File

@ -7,11 +7,13 @@
package org.hibernate.spatial.dialect.hana;
import java.sql.Types;
import java.util.List;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.HANAColumnStoreDialect;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.ConfigurationService.Converter;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor;
import org.hibernate.spatial.GeolatteGeometryType;
@ -22,6 +24,7 @@ import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.SpatialRelation;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
public class HANASpatialDialect extends HANAColumnStoreDialect implements SpatialDialect {
@ -102,6 +105,9 @@ public class HANASpatialDialect extends HANAColumnStoreDialect implements Spatia
registerFunction(
SpatialFunction.within.name(),
new HANASpatialFunction( "ST_Within", StandardBasicTypes.NUMERIC_BOOLEAN, true ) );
registerFunction(
SpatialFunction.filter.name(),
new FilterFunction() );
/*
* Additional HANA functions
@ -408,4 +414,17 @@ public class HANASpatialDialect extends HANAColumnStoreDialect implements Spatia
}
return false;
}
private static class FilterFunction extends HANASpatialFunction {
public FilterFunction() {
super( "ST_IntersectsFilter", StandardBasicTypes.NUMERIC_BOOLEAN, true );
}
@Override
public String render(
Type firstArgumentType, List arguments, SessionFactoryImplementor sessionFactory) {
return super.render( firstArgumentType, arguments, sessionFactory ) + " = 1";
}
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.spatial.dialect.mysql;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.type.StandardBasicTypes;
@ -163,6 +164,13 @@ class MySQL5SpatialFunctions extends SpatialFunctionsRegistry {
// "union"
// )
// );
functionMap.put(
SpatialFunction.filter.name(), new StandardSQLFunction(
"MBRIntersects",
StandardBasicTypes.BOOLEAN
)
);
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.spatial.dialect.mysql;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.type.StandardBasicTypes;
@ -169,6 +170,13 @@ class MySQL8SpatialFunctions extends SpatialFunctionsRegistry {
"ST_Union"
)
);
functionMap.put(
SpatialFunction.filter.name(), new StandardSQLFunction(
"MBRIntersects",
StandardBasicTypes.BOOLEAN
)
);
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.spatial.dialect.oracle;
import java.io.Serializable;
import java.util.Locale;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
@ -22,6 +23,7 @@ import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.SpatialRelation;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.spatial.dialect.WithCustomJPAFilter;
import org.jboss.logging.Logger;
@ -33,7 +35,7 @@ import org.geolatte.geom.codec.db.oracle.OracleJDBCTypeFactory;
* <p>
* Created by Karel Maesen, Geovise BVBA on 01/11/16.
*/
class OracleSDOSupport implements SpatialDialect, Serializable {
class OracleSDOSupport implements SpatialDialect, Serializable, WithCustomJPAFilter {
private static final HSMessageLogger log = Logger.getMessageLogger(
HSMessageLogger.class,
@ -290,7 +292,7 @@ class OracleSDOSupport implements SpatialDialect, Serializable {
*/
@Override
public String getHavingSridSQL(String columnName) {
return String.format( " (MDSYS.ST_GEOMETRY(%s).ST_SRID() = ?)", columnName );
return String.format( " (MDSYS.ST_GEOMETRY(%s).ST_SRID() = ?)", columnName , Locale.US);
}
/**
@ -304,7 +306,7 @@ class OracleSDOSupport implements SpatialDialect, Serializable {
*/
@Override
public String getIsEmptySQL(String columnName, boolean isEmpty) {
return String.format( "( MDSYS.ST_GEOMETRY(%s).ST_ISEMPTY() = %d )", columnName, isEmpty ? 1 : 0 );
return String.format( "( MDSYS.ST_GEOMETRY(%s).ST_ISEMPTY() = %d )", columnName, isEmpty ? 1 : 0 , Locale.US);
}
/**
@ -331,4 +333,8 @@ class OracleSDOSupport implements SpatialDialect, Serializable {
}
@Override
public String filterExpression(String geometryParam, String filterParam) {
return SpatialFunction.filter.name() + "(" + geometryParam + ", " + filterParam + ") = 'TRUE' ";
}
}

View File

@ -18,6 +18,7 @@ import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.HSMessageLogger;
import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.WithCustomJPAFilter;
import org.jboss.logging.Logger;
@ -26,7 +27,7 @@ import org.jboss.logging.Logger;
*
* @author Karel Maesen
*/
public class OracleSpatial10gDialect extends Oracle10gDialect implements SpatialDialect, Serializable {
public class OracleSpatial10gDialect extends Oracle10gDialect implements SpatialDialect, WithCustomJPAFilter, Serializable {
private static final HSMessageLogger log = Logger.getMessageLogger(
HSMessageLogger.class,
@ -101,5 +102,8 @@ public class OracleSpatial10gDialect extends Oracle10gDialect implements Spatial
return ( getFunctions().get( function.toString() ) != null );
}
@Override
public String filterExpression(String geometryParam, String filterParam) {
return sdoSupport.filterExpression( geometryParam, filterParam );
}
}

View File

@ -12,6 +12,7 @@ import org.hibernate.QueryException;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.spatial.SpatialAnalysis;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.SpatialRelation;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.spatial.dialect.oracle.criterion.OracleSpatialAggregate;
@ -138,6 +139,12 @@ class OracleSpatialFunctions extends SpatialFunctionsRegistry {
new SpatialAggregationFunction( "extent", OracleSpatialAggregate.EXTENT, sdoSupport )
);
// spatial filter function
put(
SpatialFunction.filter.name(),
new StandardSQLFunction( "SDO_FILTER" )
);
//other common functions
put( "transform", new StandardSQLFunction( "SDO_CS.TRANSFORM" ) );

View File

@ -17,6 +17,7 @@ import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.HSMessageLogger;
import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.WithCustomJPAFilter;
import org.jboss.logging.Logger;
@ -25,7 +26,8 @@ import org.jboss.logging.Logger;
* <p>
* Created by Karel Maesen, Geovise BVBA on 11/02/17.
*/
public class OracleSpatialSDO10gDialect extends Oracle10gDialect implements SpatialDialect, Serializable {
public class OracleSpatialSDO10gDialect extends Oracle10gDialect
implements SpatialDialect, WithCustomJPAFilter, Serializable {
private static final HSMessageLogger log = Logger.getMessageLogger(
HSMessageLogger.class,
@ -100,5 +102,8 @@ public class OracleSpatialSDO10gDialect extends Oracle10gDialect implements Spat
return !function.equals( SpatialFunction.crosses ) && ( getFunctions().get( function.toString() ) != null );
}
@Override
public String filterExpression(String geometryParam, String filterParam) {
return sdoSupport.filterExpression( geometryParam, filterParam );
}
}

View File

@ -7,9 +7,13 @@
package org.hibernate.spatial.dialect.postgis;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.hibernate.QueryException;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
@ -177,6 +181,11 @@ class PostgisFunctions extends SpatialFunctionsRegistry {
"extent", new ExtentFunction()
);
//register Spatial Filter function
put(
SpatialFunction.filter.name(), new FilterFunction()
);
//other common functions
put(
"dwithin", new StandardSQLFunction(
@ -206,4 +215,26 @@ class PostgisFunctions extends SpatialFunctionsRegistry {
}
}
private static class FilterFunction extends StandardSQLFunction {
public FilterFunction() {
super( "&&" );
}
@Override
public String render(
Type firstArgumentType, List arguments, SessionFactoryImplementor sessionFactory) {
int argumentCount = arguments.size();
if ( argumentCount != 2 ) {
throw new QueryException( String.format( "2 arguments expected, received %d", argumentCount ) );
}
return Stream.of(
String.valueOf( arguments.get( 0 ) ),
getRenderedName( arguments ),
String.valueOf( arguments.get( 1 ) )
).collect( Collectors.joining( " ", "(", ")" ) );
}
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.spatial.dialect.sqlserver;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.type.StandardBasicTypes;
@ -62,5 +63,7 @@ class SqlServerFunctions extends SpatialFunctionsRegistry {
"pointonsurface", new SqlServerMethod( "STPointOnSurface" )
);
// Register spatial filter function.
put( SpatialFunction.filter.name(), new SQLFunctionTemplate( StandardBasicTypes.BOOLEAN, "?1.Filter(?2)" ) );
}
}

View File

@ -0,0 +1,80 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.predicate;
import java.io.Serializable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import org.hibernate.dialect.Dialect;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.ParameterRegistry;
import org.hibernate.query.criteria.internal.Renderable;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.predicate.AbstractSimplePredicate;
import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.criterion.SpatialFilter;
import org.hibernate.spatial.dialect.WithCustomJPAFilter;
import org.hibernate.spatial.jts.EnvelopeAdapter;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
/**
* JPA Criteria API {@link Predicate} equivalent of {@link SpatialFilter}.
*/
public class FilterPredicate extends AbstractSimplePredicate implements Serializable {
private final Expression<? extends Geometry> geometry;
private final Expression<? extends Geometry> filter;
public FilterPredicate(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry,
Expression<? extends Geometry> filter) {
super( (CriteriaBuilderImpl) criteriaBuilder );
this.geometry = geometry;
this.filter = filter;
}
public FilterPredicate(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry,
Geometry filter) {
this( criteriaBuilder, geometry, criteriaBuilder.literal( filter )
);
}
public FilterPredicate(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry,
Envelope envelope, int srid) {
this( criteriaBuilder, geometry, EnvelopeAdapter.toPolygon( envelope, srid )
);
}
@Override
public void registerParameters(ParameterRegistry registry) {
Helper.possibleParameter( geometry, registry );
Helper.possibleParameter( filter, registry );
}
@Override
public String render(boolean isNegated, RenderingContext renderingContext) {
String geometryParameter = ( (Renderable) geometry ).render( renderingContext );
String filterParameter = ( (Renderable) filter ).render( renderingContext );
Dialect dialect = renderingContext.getDialect();
if ( !( dialect instanceof SpatialDialect ) ) {
throw new IllegalStateException( "Dialect must be spatially enabled dialect" );
}
if ( dialect instanceof WithCustomJPAFilter ) {
return ( (WithCustomJPAFilter) dialect ).filterExpression( geometryParameter, filterParameter );
}
else {
return SpatialFunction.filter.name() + "(" + geometryParameter + ", " + filterParameter + ") = true";
}
}
}

View File

@ -0,0 +1,575 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.predicate;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.criterion.SpatialRestrictions;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Polygon;
/**
* A factory for spatial JPA Criteria API {@link Predicate}s.
*
* @author Daniel Shuy
* @see SpatialRestrictions
*/
public final class SpatialPredicates {
private SpatialPredicates() {
}
/**
* Create a predicate for testing the arguments for "spatially equal" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially equal" predicate
*
* @see SpatialRestrictions#eq(String, Geometry)
*/
public static Predicate eq(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.equals.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially equal" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially equal" predicate
*
* @see #eq(CriteriaBuilder, Expression, Expression)
*/
public static Predicate eq(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return eq( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially within" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially within" predicate
*
* @see SpatialRestrictions#within(String, Geometry)
*/
public static Predicate within(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.within.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially within" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially within" predicate
*
* @see #within(CriteriaBuilder, Expression, Expression)
*/
public static Predicate within(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return within( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially contains" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially contains" predicate
*
* @see SpatialRestrictions#contains(String, Geometry)
*/
public static Predicate contains(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.contains.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially contains" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially contains" predicate
*
* @see #contains(CriteriaBuilder, Expression, Expression)
*/
public static Predicate contains(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return contains( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially crosses" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially crosses" predicate
*
* @see SpatialRestrictions#crosses(String, Geometry)
*/
public static Predicate crosses(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.crosses.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially crosses" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially crosses" predicate
*
* @see #crosses(CriteriaBuilder, Expression, Expression)
*/
public static Predicate crosses(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return crosses( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially disjoint" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially disjoint" predicate
*
* @see SpatialRestrictions#disjoint(String, Geometry)
*/
public static Predicate disjoint(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.disjoint.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially disjoint" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially disjoint" predicate
*
* @see #disjoint(CriteriaBuilder, Expression, Expression)
*/
public static Predicate disjoint(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return disjoint( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially intersects" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially intersects" predicate
*
* @see SpatialRestrictions#intersects(String, Geometry)
*/
public static Predicate intersects(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.intersects.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially intersects" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially intersects" predicate
*
* @see #intersects(CriteriaBuilder, Expression, Expression)
*/
public static Predicate intersects(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return intersects( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially overlaps" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially overlaps" predicate
*
* @see SpatialRestrictions#overlaps(String, Geometry)
*/
public static Predicate overlaps(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.overlaps.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially overlaps" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially overlaps" predicate
*
* @see #overlaps(CriteriaBuilder, Expression, Expression)
*/
public static Predicate overlaps(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return overlaps( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for "spatially touches" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
*
* @return "spatially touches" predicate
*
* @see SpatialRestrictions#touches(String, Geometry)
*/
public static Predicate touches(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.touches.toString(), boolean.class,
geometry1, geometry2
)
);
}
/**
* Create a predicate for testing the arguments for "spatially touches" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
*
* @return "spatially touches" predicate
*
* @see #touches(CriteriaBuilder, Expression, Expression)
*/
public static Predicate touches(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return touches( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 )
);
}
/**
* Create a predicate for testing the arguments for bounding box overlap constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression whose bounding box to use in the comparison
*
* @return bounding box overlap predicate
*
* @see FilterPredicate
* @see SpatialRestrictions#filter(String, Geometry)
*/
public static Predicate filter(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2) {
return new FilterPredicate( criteriaBuilder, geometry1, geometry2 );
}
/**
* Create a predicate for testing the arguments for bounding box overlap constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value whose bounding box to use in the comparison
*
* @return bounding box overlap predicate
*
* @see FilterPredicate
* @see SpatialRestrictions#filter(String, Geometry)
*/
public static Predicate filter(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2) {
return new FilterPredicate( criteriaBuilder, geometry1, geometry2 );
}
/**
* Create a predicate for testing the arguments for bounding box overlap constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry geometry expression
* @param envelope envelope or bounding box to use in the comparison
* @param srid the SRID of the bounding box
*
* @return bounding box overlap predicate
*
* @see FilterPredicate
* @see SpatialRestrictions#filter(String, Envelope, int)
*/
public static Predicate filterByPolygon(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry,
Envelope envelope, int srid) {
return new FilterPredicate( criteriaBuilder, geometry, envelope, srid );
}
/**
* Create a predicate for testing the arguments for "distance within" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
* @param distance distance expression
*
* @return "distance within" predicate
*
* @see SpatialRestrictions#distanceWithin(String, Geometry, double)
*/
public static Predicate distanceWithin(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2, Expression<Double> distance) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.dwithin.toString(), boolean.class,
geometry1, geometry2, distance
)
);
}
/**
* Create a predicate for testing the arguments for "distance within" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
* @param distance distance expression
*
* @return "distance within" predicate
*
* @see #distanceWithin(CriteriaBuilder, Expression, Expression, Expression)
*/
public static Predicate distanceWithin(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2, Expression<Double> distance) {
return distanceWithin( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 ), distance
);
}
/**
* Create a predicate for testing the arguments for "distance within" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry value
* @param distance distance value
*
* @return "distance within" predicate
*
* @see #distanceWithin(CriteriaBuilder, Expression, Expression, Expression)
*/
public static Predicate distanceWithin(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Geometry geometry2, double distance) {
return distanceWithin( criteriaBuilder, geometry1,
criteriaBuilder.literal( geometry2 ), criteriaBuilder.literal( distance )
);
}
/**
* Create a predicate for testing the arguments for "distance within" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry1 geometry expression
* @param geometry2 geometry expression
* @param distance distance value
*
* @return "distance within" predicate
*
* @see #distanceWithin(CriteriaBuilder, Expression, Expression, Expression)
*/
public static Predicate distanceWithin(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry1,
Expression<? extends Geometry> geometry2, double distance) {
return distanceWithin( criteriaBuilder, geometry1, geometry2,
criteriaBuilder.literal( distance )
);
}
/**
* Create a predicate for testing the arguments for "having srid" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry geometry expression
* @param srid SRID expression
*
* @return "having srid" predicate
*
* @see SpatialRestrictions#havingSRID(String, int)
*/
public static Predicate havingSRID(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry,
Expression<Integer> srid) {
return criteriaBuilder.equal(
criteriaBuilder.function( SpatialFunction.srid.toString(), int.class, geometry ),
srid
);
}
/**
* Create a predicate for testing the arguments for "having srid" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry geometry expression
* @param srid SRID expression
*
* @return "having srid" predicate
*
* @see #havingSRID(CriteriaBuilder, Expression, Expression)
*/
public static Predicate havingSRID(
CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry,
int srid) {
return havingSRID( criteriaBuilder, geometry,
criteriaBuilder.literal( srid )
);
}
/**
* Create a predicate for testing the arguments for "is empty" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry geometry expression
*
* @return "is empty" predicate
*
* @see SpatialRestrictions#isEmpty(String)
*/
public static Predicate isEmpty(CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry) {
return booleanExpressionToPredicate(
criteriaBuilder,
criteriaBuilder.function( SpatialFunction.isempty.toString(), boolean.class,
geometry
)
);
}
/**
* Create a predicate for testing the arguments for "is not empty" constraint.
*
* @param criteriaBuilder CriteriaBuilder
* @param geometry geometry expression
*
* @return "is not empty" predicate
*
* @see SpatialRestrictions#isNotEmpty(String)
*/
public static Predicate isNotEmpty(CriteriaBuilder criteriaBuilder, Expression<? extends Geometry> geometry) {
return isEmpty( criteriaBuilder, geometry )
.not();
}
private static Predicate booleanExpressionToPredicate(
CriteriaBuilder criteriaBuilder,
Expression<Boolean> expression) {
return criteriaBuilder.equal( expression, true );
}
}

View File

@ -0,0 +1,264 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.integration;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.spatial.HSMessageLogger;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.hana.HANASpatialDialect;
import org.hibernate.spatial.integration.jts.JtsGeomEntity;
import org.hibernate.spatial.predicate.SpatialPredicates;
import org.hibernate.spatial.testing.SpatialDialectMatcher;
import org.hibernate.spatial.testing.SpatialFunctionalTestCase;
import org.hibernate.testing.Skip;
import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @see TestSpatialRestrictions
*/
@Skip(condition = SpatialDialectMatcher.class, message = "No Spatial Dialect")
@SkipForDialect(value = HANASpatialDialect.class, comment = "The HANA dialect is tested via org.hibernate.spatial.dialect.hana.TestHANASpatialFunctions", jiraKey = "HHH-12426")
public class TestSpatialPredicates extends SpatialFunctionalTestCase {
private static HSMessageLogger LOG = Logger.getMessageLogger(
HSMessageLogger.class,
TestSpatialPredicates.class.getName()
);
protected HSMessageLogger getLogger() {
return LOG;
}
@Test
public void within() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.within ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getWithin( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.within( criteriaBuilder, root.get( "geom" ), expectationsFactory.getTestPolygon() );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void filter() throws SQLException {
if ( !dialectSupportsFiltering() ) {
LOG.info( "Filtering is not supported by Dialect" );
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getFilter( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.filter( criteriaBuilder, root.get( "geom" ), expectationsFactory.getTestPolygon() );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void contains() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.contains ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getContains( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.contains(
criteriaBuilder,
root.get( "geom" ),
expectationsFactory.getTestPolygon()
);
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void crosses() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.crosses ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getCrosses( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.crosses( criteriaBuilder, root.get( "geom" ), expectationsFactory.getTestPolygon() );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void touches() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.touches ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getTouches( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.touches( criteriaBuilder, root.get( "geom" ), expectationsFactory.getTestPolygon() );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void disjoint() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.disjoint ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getDisjoint( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.disjoint(
criteriaBuilder,
root.get( "geom" ),
expectationsFactory.getTestPolygon()
);
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void eq() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.equals ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getEquals( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.eq( criteriaBuilder, root.get( "geom" ), expectationsFactory.getTestPolygon() );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void intersects() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.intersects ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getIntersects( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.intersects(
criteriaBuilder,
root.get( "geom" ),
expectationsFactory.getTestPolygon()
);
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void overlaps() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.overlaps ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getOverlaps( expectationsFactory.getTestPolygon() );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.overlaps(
criteriaBuilder,
root.get( "geom" ),
expectationsFactory.getTestPolygon()
);
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void dwithin() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.dwithin ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getDwithin( expectationsFactory.getTestPoint(), 30.0 );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.distanceWithin(
criteriaBuilder,
root.get( "geom" ),
expectationsFactory.getTestPoint(),
30.0
);
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void isEmpty() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.isempty ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getIsEmpty();
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.isEmpty( criteriaBuilder, root.get( "geom" ) );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void isNotEmpty() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.isempty ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.getIsNotEmpty();
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.isNotEmpty( criteriaBuilder, root.get( "geom" ) );
retrieveAndCompare( dbexpected, predicateFactory );
}
@Test
public void havingSRID() throws SQLException {
if ( !isSupportedByDialect( SpatialFunction.srid ) ) {
return;
}
Map<Integer, Boolean> dbexpected = expectationsFactory.havingSRID( 4326 );
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.havingSRID( criteriaBuilder, root.get( "geom" ), 4326 );
retrieveAndCompare( dbexpected, predicateFactory );
dbexpected = expectationsFactory.havingSRID( 31370 );
predicateFactory = (criteriaBuilder, root) ->
SpatialPredicates.havingSRID( criteriaBuilder, root.get( "geom" ), 31370 );
retrieveAndCompare( dbexpected, predicateFactory );
}
private void retrieveAndCompare(
Map<Integer, Boolean> dbexpected,
BiFunction<CriteriaBuilder, Root<JtsGeomEntity>, Predicate> predicateFactory) {
try (Session session = openSession()) {
Transaction tx = null;
try {
tx = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<JtsGeomEntity> criteriaQuery = criteriaBuilder.createQuery( JtsGeomEntity.class );
Root<JtsGeomEntity> root = criteriaQuery.from( JtsGeomEntity.class );
criteriaQuery.select( root )
.where( predicateFactory.apply( criteriaBuilder, root ) );
List<JtsGeomEntity> list = session.createQuery( criteriaQuery )
.getResultList();
compare( dbexpected, list );
}
finally {
if ( tx != null ) {
tx.rollback();
}
}
}
}
private void compare(Map<Integer, Boolean> dbexpected, List<JtsGeomEntity> list) {
int cnt = dbexpected.entrySet()
.stream()
.filter( Map.Entry::getValue )
.reduce( 0, (accumulator, entry) -> {
if ( !findInList( entry.getKey(), list ) ) {
fail( String.format( "Expected object with id= %d, but not found in result", entry.getKey() ) );
}
return accumulator + 1;
}, Integer::sum );
assertEquals( cnt, list.size() );
LOG.infof( "Found %d objects within testsuite-suite polygon.", cnt );
}
private boolean findInList(Integer id, List<JtsGeomEntity> list) {
return list.stream()
.anyMatch( entity -> entity.getId().equals( id ) );
}
}

View File

@ -38,7 +38,6 @@ public class TestSupportFactories {
private static Class<? extends TestSupport> getSupportFactoryClass(Dialect dialect) {
String canonicalName = dialect.getClass().getCanonicalName();
if ( ( dialect instanceof SpatialDialect ) && PostgreSQL82Dialect.class.isAssignableFrom( dialect.getClass() ) ) {
//this test works because all postgis dialects ultimately derive of the Postgresql82Dialect
return PostgisTestSupport.class;

View File

@ -8,6 +8,9 @@
package org.hibernate.spatial.testing.dialects.postgis;
import org.hibernate.spatial.integration.TestSpatialFunctions;
import org.hibernate.spatial.integration.TestSpatialPredicates;
import org.hibernate.spatial.integration.TestSpatialRestrictions;
import org.hibernate.spatial.testing.AbstractExpectationsFactory;
import org.hibernate.spatial.testing.DataSourceUtils;
import org.hibernate.spatial.testing.SQLExpressionTemplate;
@ -24,8 +27,10 @@ public class PostgisTestSupport extends TestSupport {
public TestData createTestData(BaseCoreFunctionalTestCase testcase) {
if ( testcase.getClass().getCanonicalName().contains( "TestSpatialFunctions" ) ||
testcase.getClass().getCanonicalName().contains( "TestSpatialRestrictions" ) ) {
Class<? extends BaseCoreFunctionalTestCase> testcaseClass = testcase.getClass();
if ( testcaseClass == TestSpatialFunctions.class ||
testcaseClass == TestSpatialRestrictions.class ||
testcaseClass == TestSpatialPredicates.class ) {
return TestData.fromFile( "postgis-functions-test.xml" );
}
return TestData.fromFile( "test-data-set.xml" );