HHH-14906 Support for H2GIS

Notice that the tests cannot be run from the gradlew cli due to
compatability issues with the H2 version
This commit is contained in:
Karel Maesen 2021-11-03 20:26:07 +01:00
parent 9f70a6b100
commit 2deee38103
18 changed files with 189 additions and 54 deletions

View File

@ -151,6 +151,7 @@ ext {
byteman_install: "org.jboss.byteman:byteman-install:${bytemanVersion}",
byteman_bmunit: "org.jboss.byteman:byteman-bmunit:${bytemanVersion}",
h2: "com.h2database:h2:${h2Version}",
h2gis: "org.orbisgis:h2gis:1.5.0",
hsqldb: "org.hsqldb:hsqldb:2.3.6",
derby: "org.apache.derby:derby:10.14.2.0",
postgresql: 'org.postgresql:postgresql:42.2.16',

View File

@ -22,7 +22,6 @@ dependencies {
testImplementation project(':hibernate-testing')
testImplementation project(':hibernate-ant')
testImplementation project( path: ':hibernate-core', configuration: 'tests' )
testImplementation libraries.jakarta_validation
testImplementation libraries.jandex
testImplementation libraries.classmate
@ -30,6 +29,7 @@ dependencies {
testImplementation libraries.dom4j
testImplementation libraries.postgresql
testImplementation libraries.h2gis
testRuntimeOnly libraries.jakarta_el
testRuntimeOnly 'jaxen:jaxen:1.1'
@ -41,7 +41,9 @@ sourceSets.test.resources {
}
tasks.test {
enabled = ['pgsql', 'cockroachdb', 'mariadb', 'mysql_docker', 'mysql_docker'].contains( project.db )
// for now we cannot run with 'h2' project.db due to compatability issues
// H2 requires 4.200 but H2GIS 1.5 needs 4.197
enabled = ['pgsql', 'pgsql_ci', 'cockroachdb', 'mariadb', 'mysql_docker', 'mysql_docker'].contains( project.db )
}
tasks.test.include '**/*'

View File

@ -13,12 +13,14 @@ import java.util.function.Function;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.dialect.cockroachdb.CockroachDbContributor;
import org.hibernate.spatial.dialect.h2gis.H2GisDialectContributor;
import org.hibernate.spatial.dialect.mariadb.MariaDBDialectContributor;
import org.hibernate.spatial.dialect.mysql.MySQLDialectContributor;
import org.hibernate.spatial.dialect.postgis.PostgisDialectContributor;
@ -28,13 +30,13 @@ class ContributorResolver {
private final static Map<Class<? extends Dialect>,
Function<ServiceRegistry, ContributorImplementor>> CONTRIBUTOR_MAP = new HashMap<>();
static {
//TypeContributorImplementor
CONTRIBUTOR_MAP.put( PostgreSQLDialect.class, PostgisDialectContributor::new );
CONTRIBUTOR_MAP.put( CockroachDialect.class, CockroachDbContributor::new );
CONTRIBUTOR_MAP.put( MariaDBDialect.class, MariaDBDialectContributor::new );
CONTRIBUTOR_MAP.put( MySQLDialect.class, MySQLDialectContributor::new );
CONTRIBUTOR_MAP.put( H2Dialect.class, H2GisDialectContributor::new );
}
private ContributorResolver() {

View File

@ -5,7 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.dialect.h2geodb;
package org.hibernate.spatial.dialect.h2gis;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.spatial.SpatialDialect;

View File

@ -5,7 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.dialect.h2geodb;
package org.hibernate.spatial.dialect.h2gis;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
@ -13,6 +13,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
@ -20,9 +21,12 @@ import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
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.JdbcType;
import org.geolatte.geom.Geometry;
import org.geolatte.geom.codec.Wkt;
import org.geolatte.geom.jts.JTS;
/**
* Descriptor for GeoDB Geometries.
@ -46,6 +50,30 @@ public class GeoDBGeometryType implements JdbcType {
return SqlTypes.GEOMETRY;
}
//todo -- simplify as with postgis/mariadb
@Override
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaTypeDescriptor) {
return (appender, value, dialect, wrapperOptions) -> {
Geometry<?> geom;
if ( javaTypeDescriptor instanceof GeolatteGeometryJavaTypeDescriptor ) {
geom = (Geometry<?>) value;
}
else {
geom = jts2Gl( value );
}
appender.appendSql( "ST_GeomFromText('" );
appender.appendSql( Wkt.toWkt( geom, Wkt.Dialect.SFA_1_1_0 ) );
appender.appendSql( "'," );
appender.appendSql( ( geom.getSRID() ) );
appender.appendSql( ")" );
};
}
private <T> Geometry<?> jts2Gl(T value) {
return JTS.from( (org.locationtech.jts.geom.Geometry) value );
}
@Override
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {

View File

@ -5,7 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.dialect.h2geodb;
package org.hibernate.spatial.dialect.h2gis;
import java.io.ByteArrayOutputStream;

View File

@ -0,0 +1,51 @@
/*
* 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.h2gis;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.GeolatteGeometryType;
import org.hibernate.spatial.HSMessageLogger;
import org.hibernate.spatial.JTSGeometryType;
import org.hibernate.spatial.contributor.ContributorImplementor;
import org.hibernate.spatial.dialect.postgis.PGGeometryType;
import org.hibernate.spatial.dialect.postgis.PostgisSqmFunctionDescriptors;
public class H2GisDialectContributor implements ContributorImplementor {
private final ServiceRegistry serviceRegistryegistry;
public H2GisDialectContributor(ServiceRegistry serviceRegistry) {
this.serviceRegistryegistry = serviceRegistry;
}
public void contributeTypes(TypeContributions typeContributions) {
HSMessageLogger.LOGGER.typeContributions( this.getClass().getCanonicalName() );
typeContributions.contributeType( new GeolatteGeometryType( GeoDBGeometryType.INSTANCE ) );
typeContributions.contributeType( new JTSGeometryType( GeoDBGeometryType.INSTANCE ) );
}
@Override
public void contributeFunctions(FunctionContributions functionContributions) {
HSMessageLogger.LOGGER.functionContributions( this.getClass().getCanonicalName() );
final PostgisSqmFunctionDescriptors postgisFunctions = new PostgisSqmFunctionDescriptors( functionContributions );
final SqmFunctionRegistry functionRegistry = functionContributions.getFunctionRegistry();
postgisFunctions.asMap().forEach( (key, desc) -> {
functionRegistry.register( key.getName(), desc );
key.getAltName().ifPresent( altName -> functionRegistry.registerAlternateKey( altName, key.getName() ) );
} );
}
@Override
public ServiceRegistry getServiceRegistry() {
return this.serviceRegistryegistry;
}
}

View File

@ -75,7 +75,7 @@ public class StoreAndRetrieveTests extends SpatialTestDataProvider implements Se
}
@Test
public void testStoringNullGeometries(SessionFactoryScope scope) {
public void testStoringNullGeometries(SessionFactoryScope scope) {
scope.inTransaction( this::storeNullGeometry );
scope.inTransaction( this::retrieveAndCompareNullGeometry );
}

View File

@ -9,6 +9,7 @@ package org.hibernate.spatial.testing;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.PostgreSQLDialect;
@ -17,10 +18,9 @@ import org.hibernate.testing.orm.junit.DialectFeatureCheck;
/**
* Checks if the Dialect is actually supported by Spatial
*
* <p>
* 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
@ -28,6 +28,7 @@ public class IsSupportedBySpatial implements DialectFeatureCheck {
return dialect instanceof PostgreSQLDialect
|| dialect instanceof CockroachDialect
|| dialect instanceof MySQLDialect
|| dialect instanceof MariaDBDialect;
|| dialect instanceof MariaDBDialect
|| dialect instanceof H2Dialect;
}
}

View File

@ -7,8 +7,11 @@
package org.hibernate.spatial.testing;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Set;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.spatial.CommonSpatialFunction;
import org.hibernate.spatial.integration.SpatialTestDataProvider;
import org.hibernate.spatial.testing.datareader.TestSupport;
@ -16,12 +19,15 @@ 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.DialectContext;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.h2gis.functions.factory.H2GISFunctions;
@DomainModel(modelDescriptorClasses = SpatialDomainModel.class)
abstract public class SpatialTestBase
extends SpatialTestDataProvider implements SessionFactoryScopeAware {
@ -41,7 +47,17 @@ abstract public class SpatialTestBase
.getSqmFunctionRegistry()
.getFunctions()
.keySet();
if ( DialectContext.getDialect() instanceof H2Dialect ) {
this.scope.inSession( session -> {
try {
Connection cn = session.getJdbcConnectionAccess().obtainConnection();
H2GISFunctions.load( cn );
}
catch (SQLException e) {
throw new RuntimeException( e );
}
} );
}
}
}

View File

@ -9,19 +9,18 @@ package org.hibernate.spatial.testing;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.testing.datareader.TestSupport;
import org.hibernate.spatial.testing.dialects.cockroachdb.CockroachDBTestSupport;
import org.hibernate.spatial.testing.dialects.db2.DB2TestSupport;
import org.hibernate.spatial.testing.dialects.h2geodb.GeoDBTestSupport;
import org.hibernate.spatial.testing.dialects.h2geodb.H2GisTestSupport;
import org.hibernate.spatial.testing.dialects.hana.HANATestSupport;
import org.hibernate.spatial.testing.dialects.mariadb.MariaDBTestSupport;
import org.hibernate.spatial.testing.dialects.mysql.MySQL56TestSupport;
import org.hibernate.spatial.testing.dialects.mysql.MySQL8TestSupport;
import org.hibernate.spatial.testing.dialects.mysql.MySQLTestSupport;
import org.hibernate.spatial.testing.dialects.oracle.OracleSDOTestSupport;
import org.hibernate.spatial.testing.dialects.postgis.PostgisTestSupport;
import org.hibernate.spatial.testing.dialects.sqlserver.SQLServerTestSupport;
@ -58,13 +57,15 @@ public class TestSupportFactories {
return CockroachDBTestSupport.class;
}
if ( MySQLDialect.class.isAssignableFrom( dialect.getClass() ) ) {
return dialect.getVersion() >= 800 ? MySQL8TestSupport.class : MySQL56TestSupport.class;
return dialect.getVersion() >= 800 ? MySQL8TestSupport.class : MySQL56TestSupport.class;
}
if ( "org.hibernate.spatial.dialect.h2geodb.GeoDBDialect".equals( canonicalName ) ) {
return GeoDBTestSupport.class;
if ( H2Dialect.class.isAssignableFrom( dialect.getClass() ) ) {
return H2GisTestSupport.class;
}
if ( "org.hibernate.spatial.dialect.sqlserver.SqlServer2008SpatialDialect".equals( canonicalName ) ) {
return SQLServerTestSupport.class;
}

View File

@ -9,7 +9,7 @@ package org.hibernate.spatial.testing.converter;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import org.hibernate.spatial.dialect.h2geodb.GeoDbWkb;
import org.hibernate.spatial.dialect.h2gis.GeoDbWkb;
import org.geolatte.geom.Geometry;

View File

@ -15,7 +15,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor;
import org.hibernate.spatial.dialect.h2geodb.GeoDBDialect;
import org.hibernate.spatial.dialect.h2gis.GeoDBDialect;
import org.hibernate.tool.schema.Action;
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
import org.hibernate.type.spi.TypeConfiguration;

View File

@ -10,7 +10,7 @@
*/
package org.hibernate.spatial.testing.dialects.h2geodb;
import org.hibernate.spatial.dialect.h2geodb.GeoDbWkb;
import org.hibernate.spatial.dialect.h2gis.GeoDbWkb;
import org.hibernate.spatial.testing.AbstractExpectationsFactory;
import org.hibernate.spatial.testing.NativeSQLStatement;

View File

@ -1,34 +0,0 @@
/*
* 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.testing.dialects.h2geodb;
import org.hibernate.spatial.testing.AbstractExpectationsFactory;
import org.hibernate.spatial.testing.JTSGeometryEquality;
import org.hibernate.spatial.testing.datareader.TestData;
import org.hibernate.spatial.testing.datareader.TestSupport;
/**
* @author Karel Maesen, Geovise BVBA
* creation-date: Oct 2, 2010
*/
public class GeoDBTestSupport extends TestSupport {
@Override
public TestData createTestData(TestDataPurpose purpose) {
return TestData.fromFile( "h2geodb/test-geodb-data-set.xml" );
}
public JTSGeometryEquality createGeometryEquality() {
return new GeoDBGeometryEquality();
}
public AbstractExpectationsFactory createExpectationsFactory() {
return new GeoDBExpectationsFactory();
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.testing.dialects.h2geodb;
import java.util.Map;
import org.hibernate.spatial.CommonSpatialFunction;
import org.hibernate.spatial.GeomCodec;
import org.hibernate.spatial.dialect.h2gis.GeoDbWkb;
import org.hibernate.spatial.testing.AbstractExpectationsFactory;
import org.hibernate.spatial.testing.JTSGeometryEquality;
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;
/**
* @author Karel Maesen, Geovise BVBA
* creation-date: Oct 2, 2010
*/
public class H2GisTestSupport extends TestSupport {
@Override
public TestData createTestData(TestDataPurpose purpose) {
return TestData.fromFile( "h2gis/test-geodb-data-set.xml" );
}
public JTSGeometryEquality createGeometryEquality() {
return new GeoDBGeometryEquality();
}
public AbstractExpectationsFactory createExpectationsFactory() {
return new GeoDBExpectationsFactory();
}
@Override
public NativeSQLTemplates templates() {
return new NativeSQLTemplates();
}
@Override
public PredicateRegexes predicateRegexes() {
return new PredicateRegexes("st_geomfromtext");
}
@Override
public Map<CommonSpatialFunction, String> hqlOverrides() {
return super.hqlOverrides();
}
@Override
public GeomCodec codec() {
return new GeomCodec() {
@Override
public Geometry<?> toGeometry(Object in) {
return GeoDbWkb.from( in );
}
};
}
}