HHH-14982 CriteriaBuilder extension mechanism and Spatial implementation

This commit is contained in:
Marco Belladelli 2022-11-28 12:01:39 +01:00 committed by Christian Beikov
parent f022d6ef3b
commit 3b14107c49
18 changed files with 1763 additions and 2 deletions

View File

@ -51,6 +51,8 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
@SuppressWarnings("unchecked")
JpaPredicate wrap(Expression<Boolean>... expressions);
<T extends HibernateCriteriaBuilder> T unwrap(Class<T> clazz);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Criteria creation

View File

@ -0,0 +1,23 @@
/*
* 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.query.criteria.spi;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.service.Service;
/**
* Interface which allows to extend {@link HibernateCriteriaBuilder}
* with additional functionality by registering a {@link Service}
*
* @author Marco Belladelli
*/
public interface CriteriaBuilderExtension extends Service {
HibernateCriteriaBuilder extend(HibernateCriteriaBuilder cb);
Class<? extends HibernateCriteriaBuilder> getRegistrationKey();
}

View File

@ -21,10 +21,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Supplier;
@ -44,6 +46,8 @@ import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.BindableType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.criteria.spi.CriteriaBuilderExtension;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCteCriteriaAttribute;
import org.hibernate.query.criteria.JpaSearchOrder;
import org.hibernate.query.criteria.JpaSubQuery;
@ -85,7 +89,6 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -187,6 +190,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
private transient BasicType<Boolean> booleanType;
private transient BasicType<Integer> integerType;
private transient BasicType<Character> characterType;
private final transient Map<Class<? extends HibernateCriteriaBuilder>, HibernateCriteriaBuilder> extensions;
public SqmCriteriaNodeBuilder(
String uuid,
@ -203,6 +207,12 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
this.domainModelAccess = domainModelAccess;
this.serviceRegistry = serviceRegistry;
this.criteriaValueHandlingMode = criteriaValueHandlingMode;
// load registered criteria builder extensions
this.extensions = new HashMap<>();
for (CriteriaBuilderExtension extension : ServiceLoader.load( CriteriaBuilderExtension.class ) ) {
HibernateCriteriaBuilder builder = extension.extend( this );
extensions.put(extension.getRegistrationKey(), builder);
}
}
@Override
@ -430,6 +440,11 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this );
}
@Override
public <T extends HibernateCriteriaBuilder> T unwrap(Class<T> clazz) {
return (T) extensions.get( clazz );
}
@Override
public <P, F> SqmExpression<F> fk(Path<P> path) {
if ( path.getModel().getBindableType() != Bindable.BindableType.SINGULAR_ATTRIBUTE ) {

View File

@ -0,0 +1,15 @@
/*
* 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.criteria;
import org.geolatte.geom.Geometry;
/**
* @author Marco Belladelli
*/
public interface GeolatteSpatialCriteriaBuilder extends SpatialCriteriaBuilder<Geometry<?>> {
}

View File

@ -0,0 +1,15 @@
/*
* 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.criteria;
import org.locationtech.jts.geom.Geometry;
/**
* @author Marco Belladelli
*/
public interface JTSSpatialCriteriaBuilder extends SpatialCriteriaBuilder<Geometry> {
}

View File

@ -0,0 +1,73 @@
/*
* 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.criteria;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
/**
* @author Marco Belladelli
*/
public interface SpatialCriteriaBuilder<T> extends HibernateCriteriaBuilder {
Predicate eq(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate eq(Expression<? extends T> geometry1, T geometry2);
Predicate within(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate within(Expression<? extends T> geometry1, T geometry2);
Predicate contains(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate contains(Expression<? extends T> geometry1, T geometry2);
Predicate crosses(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate crosses(Expression<? extends T> geometry1, T geometry2);
Predicate disjoint(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate disjoint(Expression<? extends T> geometry1, T geometry2);
Predicate intersects(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate intersects(Expression<? extends T> geometry1, T geometry2);
Predicate overlaps(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate overlaps(Expression<? extends T> geometry1, T geometry2);
Predicate touches(Expression<? extends T> geometry1, Expression<? extends T> geometry2);
Predicate touches(Expression<? extends T> geometry1, T geometry2);
Predicate distanceWithin(
Expression<? extends T> geometry1,
Expression<? extends T> geometry2,
Expression<Double> distance);
Predicate distanceWithin(Expression<? extends T> geometry1, T geometry2, Expression<Double> distance);
Predicate distanceWithin(Expression<? extends T> geometry1, T geometry2, double distance);
Predicate distanceWithin(
Expression<? extends T> geometry1,
Expression<? extends T> geometry2,
double distance);
Predicate havingSRID(Expression<? extends T> geometry, Expression<Integer> srid);
Predicate havingSRID(Expression<? extends T> geometry, int srid);
Predicate isEmpty(CriteriaBuilder criteriaBuilder, Expression<? extends T> geometry);
Predicate isNotEmpty(CriteriaBuilder criteriaBuilder, Expression<? extends T> geometry);
}

View File

@ -0,0 +1,24 @@
/*
* 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.criteria.internal;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.spatial.criteria.GeolatteSpatialCriteriaBuilder;
import org.geolatte.geom.Geometry;
/**
* @author Marco Belladelli
*/
public class GeolatteSpatialCriteriaBuilderImpl extends SpatialCriteriaBuilderImpl<Geometry<?>>
implements GeolatteSpatialCriteriaBuilder {
public GeolatteSpatialCriteriaBuilderImpl(HibernateCriteriaBuilder criteriaBuilder) {
super( criteriaBuilder );
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.criteria.internal;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.spi.CriteriaBuilderExtension;
import org.hibernate.spatial.criteria.GeolatteSpatialCriteriaBuilder;
/**
* @author Marco Belladelli
*/
public class GeolatteSpatialCriteriaExtension implements CriteriaBuilderExtension {
@Override
public HibernateCriteriaBuilder extend(HibernateCriteriaBuilder cb) {
return new GeolatteSpatialCriteriaBuilderImpl( cb );
}
@Override
public Class<? extends HibernateCriteriaBuilder> getRegistrationKey() {
return GeolatteSpatialCriteriaBuilder.class;
}
}

View File

@ -0,0 +1,23 @@
/*
* 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.criteria.internal;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.spatial.criteria.JTSSpatialCriteriaBuilder;
import org.locationtech.jts.geom.Geometry;
/**
* @author Marco Belladelli
*/
public class JTSSpatialCriteriaBuilderImpl extends SpatialCriteriaBuilderImpl<Geometry>
implements JTSSpatialCriteriaBuilder {
public JTSSpatialCriteriaBuilderImpl(HibernateCriteriaBuilder criteriaBuilder) {
super( criteriaBuilder );
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.criteria.internal;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.spi.CriteriaBuilderExtension;
import org.hibernate.spatial.criteria.JTSSpatialCriteriaBuilder;
/**
* @author Marco Belladelli
*/
public class JTSSpatialCriteriaExtension implements CriteriaBuilderExtension {
@Override
public HibernateCriteriaBuilder extend(HibernateCriteriaBuilder cb) {
return new JTSSpatialCriteriaBuilderImpl( cb );
}
@Override
public Class<? extends HibernateCriteriaBuilder> getRegistrationKey() {
return JTSSpatialCriteriaBuilder.class;
}
}

View File

@ -0,0 +1,162 @@
/*
* 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.criteria.internal;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.spi.HibernateCriteriaBuilderDelegate;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.criteria.SpatialCriteriaBuilder;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
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;
/**
* @author Marco Belladelli
*/
public abstract class SpatialCriteriaBuilderImpl<T> extends HibernateCriteriaBuilderDelegate
implements SpatialCriteriaBuilder<T> {
protected SpatialCriteriaBuilderImpl(HibernateCriteriaBuilder criteriaBuilder) {
super( criteriaBuilder );
}
@Override
public Predicate eq(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_EQUALS.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate eq(Expression<? extends T> geometry1, T geometry2) {
return eq( geometry1, value( geometry2 ) );
}
@Override
public Predicate within(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_WITHIN.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate within(Expression<? extends T> geometry1, T geometry2) {
return within( geometry1, value( geometry2 ) );
}
@Override
public Predicate contains(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_CONTAINS.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate contains(Expression<? extends T> geometry1, T geometry2) {
return contains( geometry1, value( geometry2 ) );
}
@Override
public Predicate crosses(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_CROSSES.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate crosses(Expression<? extends T> geometry1, T geometry2) {
return crosses( geometry1, value( geometry2 ) );
}
@Override
public Predicate disjoint(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_DISJOINT.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate disjoint(Expression<? extends T> geometry1, T geometry2) {
return disjoint( geometry1, value( geometry2 ) );
}
@Override
public Predicate intersects(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_INTERSECTS.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate intersects(Expression<? extends T> geometry1, T geometry2) {
return intersects( geometry1, value( geometry2 ) );
}
@Override
public Predicate overlaps(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_OVERLAPS.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate overlaps(Expression<? extends T> geometry1, T geometry2) {
return overlaps( geometry1, value( geometry2 ) );
}
@Override
public Predicate touches(Expression<? extends T> geometry1, Expression<? extends T> geometry2) {
return isTrue( function( ST_TOUCHES.name(), boolean.class, geometry1, geometry2 ) );
}
@Override
public Predicate touches(Expression<? extends T> geometry1, T geometry2) {
return touches( geometry1, value( geometry2 ) );
}
@Override
public Predicate distanceWithin(
Expression<? extends T> geometry1,
Expression<? extends T> geometry2,
Expression<Double> distance) {
return isTrue( function( SpatialFunction.dwithin.toString(), boolean.class, geometry1, geometry2, distance ) );
}
@Override
public Predicate distanceWithin(Expression<? extends T> geometry1, T geometry2, Expression<Double> distance) {
return distanceWithin( geometry1, value( geometry2 ), distance );
}
@Override
public Predicate distanceWithin(Expression<? extends T> geometry1, T geometry2, double distance) {
return distanceWithin( geometry1, value( geometry2 ), value( distance ) );
}
@Override
public Predicate distanceWithin(
Expression<? extends T> geometry1,
Expression<? extends T> geometry2,
double distance) {
return distanceWithin( geometry1, geometry2, value( distance ) );
}
@Override
public Predicate havingSRID(Expression<? extends T> geometry, Expression<Integer> srid) {
return equal( function( SpatialFunction.srid.toString(), int.class, geometry ), srid );
}
@Override
public Predicate havingSRID(Expression<? extends T> geometry, int srid) {
return havingSRID( geometry, value( srid ) );
}
@Override
public Predicate isEmpty(CriteriaBuilder criteriaBuilder, Expression<? extends T> geometry) {
return isTrue( function( SpatialFunction.isempty.toString(), boolean.class, geometry ) );
}
@Override
public Predicate isNotEmpty(CriteriaBuilder criteriaBuilder, Expression<? extends T> geometry) {
return isEmpty( criteriaBuilder, geometry ).not();
}
}

View File

@ -19,8 +19,10 @@ import org.geolatte.geom.crs.CoordinateReferenceSystem;
/**
* {@link JTSFilterPredicate}, but for geolatte-geom.
*
* @deprecated Use {@link org.hibernate.spatial.criteria.GeolatteSpatialCriteriaBuilder GeolatteSpatialCriteriaBuilder} instead
*/
//TODO update class to H6
@Deprecated(since = "6.2")
public class GeolatteFilterPredicate {
private final Expression<? extends Geometry> geometry;

View File

@ -28,7 +28,9 @@ import static org.hibernate.spatial.CommonSpatialFunction.ST_WITHIN;
* {@link JTSSpatialPredicates}, but for geolatte-geom.
*
* @author Daniel Shuy
* @deprecated Use {@link org.hibernate.spatial.criteria.GeolatteSpatialCriteriaBuilder GeolatteSpatialCriteriaBuilder} instead
*/
@Deprecated(since = "6.2")
@SuppressWarnings("rawtypes")
public class GeolatteSpatialPredicates {

View File

@ -11,7 +11,10 @@ import jakarta.persistence.criteria.Predicate;
/**
* JPA Spatial Filter {@link Predicate}
*
* @deprecated Use {@link org.hibernate.spatial.criteria.JTSSpatialCriteriaBuilder JTSSpatialCriteriaBuilder} instead
*/
@Deprecated(since = "6.2")
class JTSFilterPredicate {
private Expression<?> geometry;

View File

@ -28,7 +28,9 @@ import static org.hibernate.spatial.CommonSpatialFunction.ST_WITHIN;
* A factory for spatial JPA Criteria API {@link Predicate}s.
*
* @author Daniel Shuy
* @deprecated Use {@link org.hibernate.spatial.criteria.JTSSpatialCriteriaBuilder JTSSpatialCriteriaBuilder} instead
*/
@Deprecated(since = "6.2")
public class JTSSpatialPredicates {
protected JTSSpatialPredicates() {

View File

@ -0,0 +1,9 @@
#
# 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>.
#
org.hibernate.spatial.criteria.internal.GeolatteSpatialCriteriaExtension
org.hibernate.spatial.criteria.internal.JTSSpatialCriteriaExtension

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.integration.criteria;
import java.util.List;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.query.Query;
import org.hibernate.spatial.criteria.GeolatteSpatialCriteriaBuilder;
import org.hibernate.spatial.criteria.JTSSpatialCriteriaBuilder;
import org.hibernate.spatial.testing.IsSupportedBySpatial;
import org.hibernate.spatial.testing.SpatialTestBase;
import org.hibernate.spatial.testing.datareader.TestSupport;
import org.hibernate.spatial.testing.domain.GeomEntity;
import org.hibernate.spatial.testing.domain.JtsGeomEntity;
import org.hibernate.spatial.testing.domain.SpatialDomainModel;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.Test;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import static org.junit.jupiter.api.Assertions.assertFalse;
@DomainModel(modelDescriptorClasses = SpatialDomainModel.class)
@SessionFactory
@RequiresDialectFeature(feature = IsSupportedBySpatial.class)
@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 11, reason = "See https://hibernate.atlassian.net/browse/HHH-15669")
public class SpatialCriteriaTest extends SpatialTestBase {
@Override
public TestSupport.TestDataPurpose purpose() {
return TestSupport.TestDataPurpose.SpatialFunctionsData;
}
@Test
public void testJtsSpatialCriteriaIntersects() {
scope.inTransaction( (session) -> {
JTSSpatialCriteriaBuilder cb = session.getCriteriaBuilder().unwrap( JTSSpatialCriteriaBuilder.class );
CriteriaQuery<JtsGeomEntity> cr = cb.createQuery( JtsGeomEntity.class );
Root<JtsGeomEntity> root = cr.from( JtsGeomEntity.class );
cr.select( root ).where( cb.intersects(
root.get( "geom" ),
org.geolatte.geom.jts.JTS.to( filterGeometry )
) );
Query<JtsGeomEntity> query = session.createQuery( cr );
List<JtsGeomEntity> results = query.getResultList();
assertFalse( results.isEmpty() );
} );
}
@Test
public void testGeolatteSpatialCriteriaIntersects() {
scope.inTransaction( (session) -> {
GeolatteSpatialCriteriaBuilder cb = session.getCriteriaBuilder()
.unwrap( GeolatteSpatialCriteriaBuilder.class );
CriteriaQuery<GeomEntity> cr = cb.createQuery( GeomEntity.class );
Root<GeomEntity> root = cr.from( GeomEntity.class );
cr.select( root ).where( cb.intersects(
root.get( "geom" ),
filterGeometry
) );
Query<GeomEntity> query = session.createQuery( cr );
List<GeomEntity> results = query.getResultList();
assertFalse( results.isEmpty() );
} );
}
}