Oracle Spatial
The Oracle Spatial starter adds Spring Boot auto-configuration for GeoJSON-first Oracle Spatial development with SDO_GEOMETRY.
This starter is for geographic and topographic spatial data.
Dependency Coordinates
<dependency>
<groupId>com.oracle.database.spring</groupId>
<artifactId>oracle-spring-boot-starter-spatial</artifactId>
</dependency>
Provided Beans
When Oracle JDBC is on the classpath and a DataSource is present, the starter auto-configures:
OracleSpatialJdbcOperationsOracleSpatialProperties
If your application provides its own bean of the same type, the starter backs off and uses your custom bean instead.
What You Inject vs What You Build
The starter injects one main working bean:
OracleSpatialJdbcOperations- the Spring JDBC entry point for spatial work
- creates GeoJSON-backed bind values
- creates bindable SQL expressions and predicates
- provides a
RowMapper<String>for projected GeoJSON columns - applies spatial bind parameters to
JdbcClient.StatementSpec
Per query, OracleSpatialJdbcOperations creates lightweight value objects:
SpatialGeometry- a GeoJSON payload plus SRID
SpatialExpression- a SQL expression such as
SDO_UTIL.TO_GEOJSON(...)orSDO_GEOM.SDO_DISTANCE(...)
- a SQL expression such as
SpatialPredicate- a SQL predicate such as
SDO_FILTER(...) = 'TRUE'
- a SQL predicate such as
SpatialRelationMask- enum values for
SDO_RELATEmasks such asANYINTERACT,INSIDE, andCONTAINS
- enum values for
These are not Spring beans. They are query parts that keep the spatial SQL fragment and its JDBC bind values together.
Configuration Properties
oracle:
database:
spatial:
enabled: true
default-srid: 4326
default-distance-unit: M
default-distance-unit is intentionally flexible and can be set to Oracle-style unit tokens such as M, KM, or UNIT=MILE.
These properties affect generated SQL directly:
default-sridis used when GeoJSON is converted toSDO_GEOMETRYdefault-distance-unitis used when distance clauses are generated forSDO_WITHIN_DISTANCEandSDO_GEOM.SDO_DISTANCE
If you are new to SRIDs, Oracle uses the SRID value to identify the geometry's spatial reference system or coordinate system. Oracle's coordinate system documentation and the SDO_SRID section of the SDO_GEOMETRY reference are the best places to start.
Using the Starter
Inject OracleSpatialJdbcOperations into a Spring JDBC service and let it supply the spatial expressions, predicates, and row mapping while your code still owns the full SQL statement:
@Service
class LandmarkService {
private final JdbcClient jdbcClient;
private final OracleSpatialJdbcOperations spatial;
LandmarkService(JdbcClient jdbcClient, OracleSpatialJdbcOperations spatial) {
this.jdbcClient = jdbcClient;
this.spatial = spatial;
}
Landmark create(Landmark landmark) {
SpatialGeometry geometry = spatial.geometry(landmark.geometry());
SpatialExpression insertGeometry = spatial.fromGeoJson(geometry);
spatial.bind(
jdbcClient.sql("insert into landmarks (id, name, category, geometry) values (:id, :name, :category, "
+ insertGeometry.expression() + ")"),
insertGeometry)
.param("id", landmark.id())
.param("name", landmark.name())
.param("category", landmark.category())
.update();
SpatialExpression projectedGeometry = spatial.toGeoJson("geometry");
return jdbcClient.sql("select id, name, category, "
+ projectedGeometry.selection("geometry")
+ " from landmarks where id = :id")
.param("id", landmark.id())
.query((rs, rowNum) -> new Landmark(
rs.getLong("id"),
rs.getString("name"),
rs.getString("category"),
rs.getString("geometry")))
.single();
}
}
In this pattern:
- the application boundary stays GeoJSON-first
- the starter keeps Oracle Spatial SQL fragments attached to their JDBC bind values
JdbcClientstill owns the statement lifecycle- schema creation, metadata registration, and spatial index creation remain outside the starter
- generated bind names such as
spatialGeometry1are internal implementation details and may increment over time, so callers should not depend on specific parameter names in logs
OracleSpatialJdbcOperations
OracleSpatialJdbcOperations is the main API exposed by the starter.
Geometry creation:
geometry(String geoJson)geometry(String geoJson, int srid)
Expression creation:
fromGeoJson(SpatialGeometry geometry)- returns
SDO_UTIL.FROM_GEOJSON(...)
- returns
toGeoJson(String geometryColumn)- returns
SDO_UTIL.TO_GEOJSON(...)
- returns
nearestNeighborDistance()- returns
SDO_NN_DISTANCE(1)
- returns
distance(String geometryColumn, SpatialGeometry geometry, Number tolerance)distance(String geometryColumn, SpatialGeometry geometry, Number tolerance, String unit)- return
SDO_GEOM.SDO_DISTANCE(...)
- return
Predicate creation:
filter(String geometryColumn, SpatialGeometry geometry)- wraps
SDO_FILTER
- wraps
relate(String geometryColumn, SpatialGeometry geometry, SpatialRelationMask mask)- wraps
SDO_RELATE
- wraps
withinDistance(String geometryColumn, SpatialGeometry geometry, Number distance)withinDistance(String geometryColumn, SpatialGeometry geometry, Number distance, String unit)- wrap
SDO_WITHIN_DISTANCE
- wrap
nearestNeighbor(String geometryColumn, SpatialGeometry geometry, int numResults)- wraps
SDO_NN
- wraps
Spring JDBC integration:
bind(JdbcClient.StatementSpec statement, SpatialJdbcBindable... parts)- applies bind values from spatial expressions and predicates to a
JdbcClientstatement
- applies bind values from spatial expressions and predicates to a
geoJsonRowMapper(String columnLabel)- returns a
RowMapper<String>for GeoJSON projections
- returns a
Query Patterns
Insert GeoJSON as SDO_GEOMETRY:
SpatialGeometry geometry = spatial.geometry(geoJson);
SpatialExpression insertGeometry = spatial.fromGeoJson(geometry);
spatial.bind(
jdbcClient.sql("insert into landmarks (geometry) values (" + insertGeometry.expression() + ")"),
insertGeometry)
.update();
Project SDO_GEOMETRY back to GeoJSON:
SpatialExpression projectedGeometry = spatial.toGeoJson("geometry");
String geoJson = jdbcClient.sql("select " + projectedGeometry.selection("geometry") + " from landmarks where id = :id")
.param("id", id)
.query(spatial.geoJsonRowMapper("geometry"))
.single();
Apply a filter plus exact relationship check:
SpatialGeometry searchGeometry = spatial.geometry(polygonGeoJson);
SpatialPredicate filter = spatial.filter("geometry", searchGeometry);
SpatialPredicate relate = spatial.relate("geometry", searchGeometry, SpatialRelationMask.ANYINTERACT);
spatial.bind(
jdbcClient.sql("select id from landmarks where "
+ filter.clause() + " and " + relate.clause()),
filter, relate)
.query(Long.class)
.list();
Find nearby rows and order by distance:
SpatialGeometry referenceGeometry = spatial.geometry(pointGeoJson);
SpatialPredicate within = spatial.withinDistance("geometry", referenceGeometry, 2000);
SpatialExpression distance = spatial.distance("geometry", referenceGeometry, 0.005);
spatial.bind(
jdbcClient.sql("select id, " + distance.selection("distance")
+ " from landmarks where " + within.clause()
+ " order by distance fetch first 3 rows only"),
within, distance)
.query((rs, rowNum) -> rs.getLong("id"))
.list();
Usage Notes
- Manage spatial table DDL,
USER_SDO_GEOM_METADATA, and spatial index creation in your migrations or setup SQL rather than expecting starter beans to create them. - Use
SDO_FILTERas a primary filter andSDO_RELATEfor exact mask-based checks. - Use
SDO_WITHIN_DISTANCEfor radius filtering andSDO_NNfor nearest-neighbor searches. - Do not combine
SDO_NNandSDO_WITHIN_DISTANCEin the sameWHEREclause. - Use
SDO_WITHIN_DISTANCEordered bySDO_GEOM.SDO_DISTANCEwhen you need both a distance bound and a result count. nearestNeighbor(...)andnearestNeighborDistance()currently assume Oracle operator id1. If you need more advanced SQL with multipleSDO_NNoperators in a single statement, build that query manually.
Further Reading
- Oracle Spatial Concepts
- SDO_GEOMETRY Object Type
- Coordinate Systems (Spatial Reference Systems)
- SDO_SRID in the spatial data types and metadata reference
- SDO_UTIL.FROM_GEOJSON
- SDO_UTIL.TO_GEOJSON
- SDO_FILTER
- SDO_RELATE
- SDO_WITHIN_DISTANCE
- SDO_NN
- SDO_NN_DISTANCE
Sample
See the spatial sample application under database/starters/oracle-spring-boot-starter-samples/oracle-spring-boot-sample-spatial for a REST-based example that stores and queries SDO_GEOMETRY values using GeoJSON. Its GET /landmarks/near endpoint accepts compact GeoJSON in the geometry query parameter.