/* * 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.oracle; import java.io.Serializable; import org.geolatte.geom.codec.db.oracle.ConnectionFinder; import org.geolatte.geom.codec.db.oracle.OracleJDBCTypeFactory; import org.jboss.logging.Logger; import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.registry.selector.spi.StrategySelector; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.service.ServiceRegistry; import org.hibernate.spatial.GeolatteGeometryType; import org.hibernate.spatial.HSMessageLogger; import org.hibernate.spatial.HibernateSpatialConfigurationSettings; 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; /** * SDO Geometry support for Oracle dialects * <p> * Created by Karel Maesen, Geovise BVBA on 01/11/16. */ class OracleSDOSupport implements SpatialDialect, Serializable { private static final HSMessageLogger log = Logger.getMessageLogger( HSMessageLogger.class, OracleSpatial10gDialect.class.getName() ); private final boolean isOgcStrict; private final SpatialFunctionsRegistry sdoFunctions; OracleSDOSupport(boolean isOgcStrict) { this.isOgcStrict = isOgcStrict; this.sdoFunctions = new OracleSpatialFunctions( isOgcStrict, this ); } SpatialFunctionsRegistry functionsToRegister() { return this.sdoFunctions; } public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { final SDOGeometryTypeDescriptor sdoGeometryTypeDescriptor = mkSdoGeometryTypeDescriptor( serviceRegistry ); typeContributions.contributeType( new GeolatteGeometryType( sdoGeometryTypeDescriptor ) ); typeContributions.contributeType( new JTSGeometryType( sdoGeometryTypeDescriptor ) ); } private SDOGeometryTypeDescriptor mkSdoGeometryTypeDescriptor(ServiceRegistry serviceRegistry) { final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class ); final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class ); final ConnectionFinder connectionFinder = strategySelector.resolveStrategy( ConnectionFinder.class, cfgService.getSetting( HibernateSpatialConfigurationSettings.CONNECTION_FINDER, String.class, "org.geolatte.geom.codec.db.oracle.DefaultConnectionFinder" ) ); log.connectionFinder( connectionFinder.getClass().getCanonicalName() ); return new SDOGeometryTypeDescriptor( new OracleJDBCTypeFactory( connectionFinder ) ); } /** * Returns the SQL fragment for the SQL WHERE-clause when parsing * <code>org.hibernatespatial.criterion.SpatialRelateExpression</code>s * into prepared statements. * <p/> * * @param columnName The name of the geometry-typed column to which the relation is * applied * @param spatialRelation The type of spatial relation (as defined in * <code>SpatialRelation</code>). * * @return SQL fragment {@code SpatialRelateExpression} */ @Override public String getSpatialRelateSQL(String columnName, int spatialRelation) { String sql = getOGCSpatialRelateSQL( columnName, "?", spatialRelation ) + " = 1"; sql += " and " + columnName + " is not null"; return sql; } public String getOGCSpatialRelateSQL(String arg1, String arg2, int spatialRelation) { final StringBuffer ogcFunction = new StringBuffer( "MDSYS." ); switch ( spatialRelation ) { case SpatialRelation.INTERSECTS: ogcFunction.append( "OGC_INTERSECTS" ); break; case SpatialRelation.CONTAINS: ogcFunction.append( "OGC_CONTAINS" ); break; case SpatialRelation.CROSSES: ogcFunction.append( "OGC_CROSS" ); break; case SpatialRelation.DISJOINT: ogcFunction.append( "OGC_DISJOINT" ); break; case SpatialRelation.EQUALS: ogcFunction.append( "OGC_EQUALS" ); break; case SpatialRelation.OVERLAPS: ogcFunction.append( "OGC_OVERLAP" ); break; case SpatialRelation.TOUCHES: ogcFunction.append( "OGC_TOUCH" ); break; case SpatialRelation.WITHIN: ogcFunction.append( "OGC_WITHIN" ); break; default: throw new IllegalArgumentException( "Unknown SpatialRelation (" + spatialRelation + ")." ); } ogcFunction.append( "(" ).append( "MDSYS.ST_GEOMETRY.FROM_SDO_GEOM(" ) .append( arg1 ).append( ")," ).append( "MDSYS.ST_GEOMETRY.FROM_SDO_GEOM(" ).append( arg2 ) .append( ")" ).append( ")" ); return ogcFunction.toString(); } /** * Returns the SQL fragment for the SQL WHERE-clause when parsing * <code>org.hibernatespatial.criterion.SpatialRelateExpression</code>s * into prepared statements. * <p/> * * @param columnName The name of the geometry-typed column to which the relation is * applied * @param spatialRelation The type of spatial relation (as defined in * <code>SpatialRelation</code>). * * @return SQL fragment {@code SpatialRelateExpression} */ public String getSDOSpatialRelateSQL(String columnName, int spatialRelation) { String sql = getNativeSpatialRelateSQL( columnName, "?", spatialRelation ) + " = 1"; sql += " and " + columnName + " is not null"; return sql; } String getNativeSpatialRelateSQL(String arg1, String arg2, int spatialRelation) { String mask; boolean negate = false; switch ( spatialRelation ) { case SpatialRelation.INTERSECTS: mask = "ANYINTERACT"; break; case SpatialRelation.CONTAINS: mask = "CONTAINS+COVERS"; break; case SpatialRelation.CROSSES: throw new UnsupportedOperationException( "Oracle Spatial does't have equivalent CROSSES relationship" ); case SpatialRelation.DISJOINT: mask = "ANYINTERACT"; negate = true; break; case SpatialRelation.EQUALS: mask = "EQUAL"; break; case SpatialRelation.OVERLAPS: mask = "OVERLAPBDYDISJOINT+OVERLAPBDYINTERSECT"; break; case SpatialRelation.TOUCHES: mask = "TOUCH"; break; case SpatialRelation.WITHIN: mask = "INSIDE+COVEREDBY"; break; default: throw new IllegalArgumentException( "undefined SpatialRelation passed (" + spatialRelation + ")" ); } final StringBuilder buffer = new StringBuilder( "CASE SDO_RELATE(" ).append( arg1 ) .append( "," ) .append( arg2 ) .append( ",'mask=" ) .append( mask ) .append( "') " ); if ( !negate ) { buffer.append( " WHEN 'TRUE' THEN 1 ELSE 0 END" ); } else { buffer.append( " WHEN 'TRUE' THEN 0 ELSE 1 END" ); } return buffer.toString(); } /** * Returns the SQL fragment for the SQL WHERE-expression when parsing * <code>org.hibernate.spatial.criterion.SpatialFilterExpression</code>s * into prepared statements. * * @param columnName The name of the geometry-typed column to which the filter is * be applied * * @return Rhe SQL fragment for the {@code SpatialFilterExpression} */ @Override public String getSpatialFilterExpression(String columnName) { final StringBuffer buffer = new StringBuffer( "SDO_FILTER(" ); buffer.append( columnName ); buffer.append( ",?) = 'TRUE' " ); return buffer.toString(); } /** * Returns the SQL fragment for the specfied Spatial aggregate expression. * * @param columnName The name of the Geometry property * @param aggregation The type of <code>SpatialAggregate</code> * * @return The SQL fragment for the projection */ @Override public String getSpatialAggregateSQL(String columnName, int aggregation) { final StringBuffer aggregateFunction = new StringBuffer(); final SpatialAggregate sa = new SpatialAggregate( aggregation ); if ( sa.getAggregateSyntax() == null ) { throw new IllegalArgumentException( "Unknown Spatial Aggregation (" + aggregation + ")." ); } aggregateFunction.append( sa.getAggregateSyntax() ); aggregateFunction.append( "(" ); if ( sa.isAggregateType() ) { aggregateFunction.append( "SDOAGGRTYPE(" ); } aggregateFunction.append( columnName ); // TODO tolerance must by configurable if ( sa.isAggregateType() ) { aggregateFunction.append( ", " ).append( .001 ).append( ")" ); } aggregateFunction.append( ")" ); return aggregateFunction.toString(); } /** * Returns The SQL fragment when parsing a <code>DWithinExpression</code>. * * @param columnName The geometry column to test against * * @return The SQL fragment when parsing a <code>DWithinExpression</code>. */ @Override public String getDWithinSQL(String columnName) { return "SDO_WITHIN_DISTANCE (" + columnName + ",?, ?) = 'TRUE' "; } /** * Returns the SQL fragment when parsing an <code>HavingSridExpression</code>. * * @param columnName The geometry column to test against * * @return The SQL fragment for an <code>HavingSridExpression</code>. */ @Override public String getHavingSridSQL(String columnName) { return String.format( " (MDSYS.ST_GEOMETRY(%s).ST_SRID() = ?)", columnName ); } /** * Returns the SQL fragment when parsing a <code>IsEmptyExpression</code> or * <code>IsNotEmpty</code> expression. * * @param columnName The geometry column * @param isEmpty Whether the geometry is tested for empty or non-empty * * @return The SQL fragment for the isempty function */ @Override public String getIsEmptySQL(String columnName, boolean isEmpty) { return String.format( "( MDSYS.ST_GEOMETRY(%s).ST_ISEMPTY() = %d )", columnName, isEmpty ? 1 : 0 ); } /** * Returns true if this <code>SpatialDialect</code> supports a specific filtering function. * <p> This is intended to signal DB-support for fast window queries, or MBR-overlap queries.</p> * * @return True if filtering is supported */ @Override public boolean supportsFiltering() { return true; } /** * Does this dialect supports the specified <code>SpatialFunction</code>. * * @param function <code>SpatialFunction</code> * * @return True if this <code>SpatialDialect</code> supports the spatial function specified by the function parameter. */ @Override public boolean supports(SpatialFunction function) { return false; } }