HHH-14090 Add MariaDB103SpatialDialect

This commit is contained in:
Karel Maesen 2021-03-17 21:53:53 +01:00 committed by Yoann Rodière
parent df6cde898a
commit c36aecfc73
11 changed files with 647 additions and 4 deletions

View File

@ -83,6 +83,13 @@ ext {
'jdbc.pass' : 'hibernate_orm_test',
'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test'
],
mariadb_spatial_ci : [
'db.dialect' : 'org.hibernate.spatial.dialect.mariadb.MariaDB103SpatialDialect',
'jdbc.driver': 'org.mariadb.jdbc.Driver',
'jdbc.user' : 'root',
'jdbc.pass' : 'hibernate_orm_test',
'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test'
],
postgis : [
'db.dialect' : 'org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect',
'jdbc.driver': 'org.postgresql.Driver',

View File

@ -0,0 +1,42 @@
/*
* 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.mariadb;
import java.util.Map;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.MariaDB103Dialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
public class MariaDB103SpatialDialect extends MariaDB103Dialect implements MariaDBSpatialDialectTrait {
final private SpatialFunctionsRegistry spatialFunctions = new MariaDB103SpatialFunctions();
public MariaDB103SpatialDialect() {
super();
registerColumnType(
MariaDBGeometryTypeDescriptor.INSTANCE.getSqlType(),
"GEOMETRY"
);
for ( Map.Entry<String, SQLFunction> entry : spatialFunctions ) {
registerFunction( entry.getKey(), entry.getValue() );
}
}
@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
delegateContributeTypes( typeContributions, serviceRegistry );
}
@Override
public SpatialFunctionsRegistry spatialFunctions() {
return spatialFunctions;
}
}

View File

@ -0,0 +1,179 @@
/*
* 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.mariadb;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
import org.hibernate.type.StandardBasicTypes;
class MariaDB103SpatialFunctions extends SpatialFunctionsRegistry {
public MariaDB103SpatialFunctions() {
functionMap.put(
"dimension", new StandardSQLFunction(
"ST_Dimension",
StandardBasicTypes.INTEGER
)
);
functionMap.put(
"geometrytype", new StandardSQLFunction(
"ST_GeometryType", StandardBasicTypes.STRING
)
);
functionMap.put(
"srid", new StandardSQLFunction(
"ST_SRID",
StandardBasicTypes.INTEGER
)
);
functionMap.put(
"envelope", new StandardSQLFunction(
"ST_Envelope"
)
);
functionMap.put(
"astext", new StandardSQLFunction(
"ST_Astext",
StandardBasicTypes.STRING
)
);
functionMap.put(
"asbinary", new StandardSQLFunction(
"ST_Asbinary",
StandardBasicTypes.BINARY
)
);
functionMap.put(
"isempty", new StandardSQLFunction(
"ST_IsEmpty",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"issimple", new StandardSQLFunction(
"ST_IsSimple",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"boundary", new StandardSQLFunction(
"ST_Boundary"
)
);
// Register functions for spatial relation constructs
functionMap.put(
"overlaps", new StandardSQLFunction(
"ST_Overlaps",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"intersects", new StandardSQLFunction(
"ST_Intersects",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"equals", new StandardSQLFunction(
"ST_Equals",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"contains", new StandardSQLFunction(
"ST_Contains",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"crosses", new StandardSQLFunction(
"ST_Crosses",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"disjoint", new StandardSQLFunction(
"ST_Disjoint",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"touches", new StandardSQLFunction(
"ST_Touches",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"within", new StandardSQLFunction(
"ST_Within",
StandardBasicTypes.BOOLEAN
)
);
functionMap.put(
"relate", new StandardSQLFunction(
"ST_Relate",
StandardBasicTypes.BOOLEAN
)
);
// register the spatial analysis functions
functionMap.put(
"distance", new StandardSQLFunction(
"ST_Distance",
StandardBasicTypes.DOUBLE
)
);
functionMap.put(
"buffer", new StandardSQLFunction(
"ST_Buffer"
)
);
functionMap.put(
"convexhull", new StandardSQLFunction(
"ST_ConvexHull"
)
);
functionMap.put(
"difference", new StandardSQLFunction(
"ST_Difference"
)
);
functionMap.put(
"intersection", new StandardSQLFunction(
"ST_Intersection"
)
);
functionMap.put(
"symdifference", new StandardSQLFunction(
"ST_SymDifference"
)
);
functionMap.put(
"geomunion", new StandardSQLFunction(
"ST_Union"
)
);
functionMap.put(
SpatialFunction.filter.name(), new StandardSQLFunction(
"MBRIntersects",
StandardBasicTypes.BOOLEAN
)
);
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.mariadb;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
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.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.geolatte.geom.ByteBuffer;
import org.geolatte.geom.ByteOrder;
import org.geolatte.geom.Geometry;
import org.geolatte.geom.codec.Wkb;
import org.geolatte.geom.codec.WkbDecoder;
import org.geolatte.geom.codec.WkbEncoder;
public class MariaDBGeometryTypeDescriptor implements SqlTypeDescriptor {
public static final MariaDBGeometryTypeDescriptor INSTANCE = new MariaDBGeometryTypeDescriptor();
final WkbEncoder encoder = Wkb.newEncoder( Wkb.Dialect.MYSQL_WKB );
final WkbDecoder decoder = Wkb.newDecoder( Wkb.Dialect.MYSQL_WKB );
@Override
public int getSqlType() {
return Types.ARRAY;
}
@Override
public boolean canBeRemapped() {
return false;
}
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(
PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
final byte[] bytes = valueToByteArray( value, options );
st.setBytes( index, bytes );
}
@Override
protected void doBind(
CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
final byte[] bytes = valueToByteArray( value, options );
st.setBytes( name, bytes );
}
private byte[] valueToByteArray(X value, WrapperOptions options) {
final Geometry<?> geometry = getJavaDescriptor().unwrap( value, Geometry.class, options );
final ByteBuffer buffer = encoder.encode( geometry, ByteOrder.NDR );
return buffer == null ? null : buffer.toByteArray();
}
};
}
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return getJavaDescriptor().wrap( toGeometry( rs.getBytes( name ) ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return getJavaDescriptor().wrap( toGeometry( statement.getBytes( index ) ), options );
}
@Override
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
throws SQLException {
return getJavaDescriptor().wrap( toGeometry( statement.getBytes( name ) ), options );
}
};
}
private Geometry<?> toGeometry(byte[] bytes) {
if ( bytes == null ) {
return null;
}
final ByteBuffer buffer = ByteBuffer.from( bytes );
return decoder.decode( buffer );
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.mariadb;
import java.util.Locale;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor;
import org.hibernate.spatial.GeolatteGeometryType;
import org.hibernate.spatial.JTSGeometryJavaTypeDescriptor;
import org.hibernate.spatial.JTSGeometryType;
import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.SpatialRelation;
import org.hibernate.spatial.dialect.SpatialFunctionsRegistry;
public interface MariaDBSpatialDialectTrait extends SpatialDialect {
default void delegateContributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
typeContributions.contributeType( new GeolatteGeometryType( MariaDBGeometryTypeDescriptor.INSTANCE ) );
typeContributions.contributeType( new JTSGeometryType( MariaDBGeometryTypeDescriptor.INSTANCE ) );
typeContributions.contributeJavaTypeDescriptor( GeolatteGeometryJavaTypeDescriptor.INSTANCE );
typeContributions.contributeJavaTypeDescriptor( JTSGeometryJavaTypeDescriptor.INSTANCE );
}
SpatialFunctionsRegistry spatialFunctions();
@Override
default String getSpatialRelateSQL(String columnName, int spatialRelation) {
switch ( spatialRelation ) {
case SpatialRelation.WITHIN:
return " ST_within(" + columnName + ",?)";
case SpatialRelation.CONTAINS:
return " ST_contains(" + columnName + ", ?)";
case SpatialRelation.CROSSES:
return " ST_crosses(" + columnName + ", ?)";
case SpatialRelation.OVERLAPS:
return " ST_overlaps(" + columnName + ", ?)";
case SpatialRelation.DISJOINT:
return " ST_disjoint(" + columnName + ", ?)";
case SpatialRelation.INTERSECTS:
return " ST_intersects(" + columnName
+ ", ?)";
case SpatialRelation.TOUCHES:
return " ST_touches(" + columnName + ", ?)";
case SpatialRelation.EQUALS:
return " ST_equals(" + columnName + ", ?)";
default:
throw new IllegalArgumentException(
"Spatial relation is not known by this dialect"
);
}
}
@Override
default String getSpatialFilterExpression(String columnName) {
return String.format( Locale.ENGLISH, "MBRIntersects(%s,?)", columnName
);
}
@Override
default String getSpatialAggregateSQL(String columnName, int aggregation) {
throw new UnsupportedOperationException( "MariaDB has no spatial aggregate functions." );
}
@Override
default String getDWithinSQL(String columnName) {
throw new UnsupportedOperationException( "MariaDB doesn't support the DWithin function." );
}
@Override
default String getHavingSridSQL(String columnName) {
return " (ST_SRID(" + columnName + ") = ?) ";
}
@Override
default String getIsEmptySQL(String columnName, boolean isEmpty) {
final String emptyExpr = " ST_IsEmpty(" + columnName + ") ";
return isEmpty ? emptyExpr : "( NOT " + emptyExpr + ")";
}
@Override
default boolean supportsFiltering() {
return true;
}
@Override
default boolean supports(SpatialFunction function) {
return spatialFunctions().get( function.toString() ) != null;
}
}

View File

@ -9,12 +9,14 @@ package org.hibernate.spatial.testing;
import org.hibernate.dialect.CockroachDB192Dialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.PostgreSQL82Dialect;
import org.hibernate.spatial.SpatialDialect;
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.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;
@ -45,7 +47,11 @@ public class TestSupportFactories {
return PostgisTestSupport.class;
}
if ( ( dialect instanceof SpatialDialect ) && CockroachDB192Dialect.class.isAssignableFrom( dialect.getClass() ) ){
if ( ( dialect instanceof SpatialDialect ) && MariaDBDialect.class.isAssignableFrom( dialect.getClass() ) ) {
return MariaDBTestSupport.class;
}
if ( ( dialect instanceof SpatialDialect ) && CockroachDB192Dialect.class.isAssignableFrom( dialect.getClass() ) ) {
return CockroachDBTestSupport.class;
}

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.testing.dialects.mariadb;
import org.hibernate.spatial.testing.DataSourceUtils;
import org.hibernate.spatial.testing.dialects.mysql.MySQL8ExpectationsFactory;
//for now, create the same expectations as for MySQL8
public class MariaDBExpectationsFactory extends MySQL8ExpectationsFactory {
public MariaDBExpectationsFactory(DataSourceUtils dataSourceUtils) {
super( dataSourceUtils );
}
}

View File

@ -0,0 +1,16 @@
/*
* 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.mariadb;
import org.hibernate.spatial.testing.dialects.mysql.MySQL8ExpressionTemplate;
//for now, just extends the MySQL8 template
public class MariaDBExpressionTemplate extends MySQL8ExpressionTemplate {
}

View File

@ -0,0 +1,35 @@
/*
* 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.mariadb;
import org.hibernate.spatial.testing.AbstractExpectationsFactory;
import org.hibernate.spatial.testing.DataSourceUtils;
import org.hibernate.spatial.testing.SQLExpressionTemplate;
import org.hibernate.spatial.testing.TestData;
import org.hibernate.spatial.testing.TestSupport;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
public class MariaDBTestSupport extends TestSupport {
@Override
public TestData createTestData(BaseCoreFunctionalTestCase testcase) {
return TestData.fromFile( "mariadb/test-mariadb-functions-data-set.xml" );
}
@Override
public AbstractExpectationsFactory createExpectationsFactory(DataSourceUtils dataSourceUtils) {
return new MariaDBExpectationsFactory( dataSourceUtils );
}
@Override
public SQLExpressionTemplate getSQLExpressionTemplate() {
return new MariaDBExpressionTemplate();
}
}

View File

@ -24,7 +24,7 @@ import org.locationtech.jts.geom.Point;
public class MySQL8ExpectationsFactory extends AbstractExpectationsFactory {
MySQL8ExpectationsFactory(DataSourceUtils dataSourceUtils) {
public MySQL8ExpectationsFactory(DataSourceUtils dataSourceUtils) {
super( dataSourceUtils );
}
@ -95,7 +95,8 @@ public class MySQL8ExpectationsFactory extends AbstractExpectationsFactory {
@Override
protected NativeSQLStatement createNativeRelateStatement(Geometry geom, String matrix) {
throw new UnsupportedOperationException();
String sql = "select t.id, ST_Relate(t.geom, ST_GeomFromText(?, 31370), '" + matrix + "' ) from geomtest t where ST_Relate(t.geom, ST_GeomFromText(?, 31370), '" + matrix + "') = 1 ";
return createNativeSQLStatementAllWKTParams( sql, geom.toText() );
}
@Override
@ -200,7 +201,7 @@ public class MySQL8ExpectationsFactory extends AbstractExpectationsFactory {
@Override
protected NativeSQLStatement createNativeBoundaryStatement() {
throw new UnsupportedOperationException();
return createNativeSQLStatement( "select id, st_boundary(geom) from geomtest" );
}
@Override

View File

@ -0,0 +1,135 @@
<!--
~ 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>.
-->
<!--
Tests for MariaDB spatial
-->
<TestData>
<Element>
<id>1</id>
<type>POINT</type>
<wkt>SRID=31370;POINT(10 5)</wkt>
</Element>
<Element>
<id>2</id>
<type>POINT</type>
<wkt>SRID=31370;POINT(52.25 2.53)</wkt>
</Element>
<Element>
<id>3</id>
<type>POINT</type>
<wkt>SRID=31370;POINT(51 12)</wkt>
</Element>
<Element>
<id>4</id>
<type>POINT</type>
<wkt>SRID=31370;POINT(10.0 2.0)</wkt>
</Element>
<Element>
<id>5</id>
<type>LINESTRING</type>
<wkt>SRID=31370;LINESTRING(10.0 5.0, 20.0 15.0)</wkt>
</Element>
<Element>
<id>6</id>
<type>LINESTRING</type>
<wkt>SRID=31370;LINESTRING(10.0 5.0, 20.0 15.0, 30.3 22.4, 10 30.0)</wkt>
</Element>
<Element>
<id>11</id>
<type>MULTILINESTRING</type>
<wkt>SRID=31370;MULTILINESTRING((10.0 5.0, 20.0 15.0),( 25.0 30.0, 30.0 20.0))</wkt>
</Element>
<Element>
<id>12</id>
<type>MULTILINESTRING</type>
<wkt>SRID=31370;MULTILINESTRING((10.0 5.0, 20.0 15.0, 30.3 22.4, 10 30.0), (40.0 20.0, 42.0 18.0, 43.0 16.0, 40
14.0))
</wkt>
</Element>
<Element>
<id>16</id>
<type>POLYGON</type>
<wkt>SRID=31370;POLYGON( (0 0, 0 10, 10 10, 10 0, 0 0) )</wkt>
</Element>
<Element>
<id>18</id>
<type>POLYGON</type>
<wkt>SRID=31370;POLYGON( (0 0, 0 10, 10 10, 10 0, 0 0), (2 2, 2 5, 5 5,5 2, 2 2))</wkt>
</Element>
<Element>
<id>19</id>
<type>POLYGON</type>
<wkt>SRID=31370;POLYGON( (50 50, 50 70, 70 70, 70 50, 50 50) )</wkt>
</Element>
<Element>
<id>20</id>
<type>MULTIPOLYGON</type>
<wkt>SRID=31370;MULTIPOLYGON( ((10 20, 30 40, 44 50, 10 20)), ((55 50, 60 70, 70 78, 55 50)) )</wkt>
</Element>
<Element>
<id>22</id>
<type>MULTIPOLYGON</type>
<wkt>SRID=31370;MULTIPOLYGON(( (0 0, 0 50, 50 50, 50 0, 0 0), (10 10, 10 20, 20 20, 20 10, 10 10) ),((15 10, 12
14, 13
14, 15 10)) )
</wkt>
</Element>
<Element>
<id>25</id>
<type>MULTIPOINT</type>
<wkt>SRID=31370;MULTIPOINT(21 2, 25 5, 30 3)</wkt>
</Element>
<Element>
<id>26</id>
<type>MULTIPOINT</type>
<wkt>SRID=31370;MULTIPOINT(21 2)</wkt>
</Element>
<Element>
<id>30</id>
<type>GEOMETRYCOLLECTION</type>
<wkt>SRID=31370;GEOMETRYCOLLECTION(POINT(4 0), LINESTRING(4 2, 5 3))</wkt>
</Element>
<Element>
<id>31</id>
<type>GEOMETRYCOLLECTION</type>
<wkt>SRID=31370;GEOMETRYCOLLECTION(POINT(4 0), LINESTRING(4 2, 5 3), POLYGON((0 0, 3 0, 3 3,0 3, 0 0)))</wkt>
</Element>
<Element>
<id>32</id>
<type>GEOMETRYCOLLECTION</type>
<wkt>SRID=31370;GEOMETRYCOLLECTION(POINT(4 0), LINESTRING(4 2, 5 3), POLYGON((0 0, 3 0, 3 3,0 3, 0 0),(1 1, 2 1,
2 2, 1 2,
1 1)))
</wkt>
</Element>
<Element>
<id>33</id>
<type>GEOMETRYCOLLECTION</type>
<wkt>SRID=31370;GEOMETRYCOLLECTION( MULTIPOINT(21 2, 25 5, 30 3), MULTIPOLYGON( ((10 20, 30 40, 44 50, 10 20)),
((15 10,
12 14, 13 14, 15 10)) ), MULTILINESTRING((10.0 5.0, 20.0 15.0),( 25.0 30.0, 30.0 20.0)))
</wkt>
</Element>
</TestData>