Skip to content

Instantly share code, notes, and snippets.

@abdelghanyMh
Created February 28, 2025 14:47
Show Gist options
  • Save abdelghanyMh/4f09f0ee3326952fba08bfacd2f9e551 to your computer and use it in GitHub Desktop.
Save abdelghanyMh/4f09f0ee3326952fba08bfacd2f9e551 to your computer and use it in GitHub Desktop.

Understanding Your Spring Boot Hibernate Spatial Solution

I'm glad the solution worked! Let's break down what happened and why it worked, which will help you understand the entire Spring Boot + Hibernate Spatial + GeoJSON + PostGIS stack better.

The Problem

You were experiencing an issue where your Point geometry field (cityCoordinates) wasn't being serialized correctly to JSON. Instead of getting a proper GeoJSON Point representation, you were getting a deeply nested structure with repeated "envelope" objects:

"cityCoordinates": {
  "envelope": {
    "envelope": {
      "envelope": {
        // more nesting...

Why This Happened

This happened because of how the serialization process works in Spring Boot and how geometry objects are structured:

  1. JPA/Hibernate Side: Hibernate Spatial correctly maps your database's PostGIS geography(Point,4326) type to the JTS Point Java object.

  2. Serialization Side: When Spring tries to convert your entities to JSON (using Jackson), it doesn't know how to handle the complex Point object from the JTS (Java Topology Suite) library.

  3. Default Behavior: Without guidance, Jackson tries to serialize all fields and methods of the Point object, including internal implementation details like envelope boundaries, which leads to the deeply nested structure you saw.

The Solution Explained

The solution that worked has two key parts:

1. @JsonIgnore on the Original Field

@JsonIgnore
@Column(name = "city_coordinates", nullable = false, columnDefinition = "geography(Point,4326)")
private Point cityCoordinates;

This annotation tells Jackson: "Don't try to serialize this field at all." This prevents the problematic serialization of the complex JTS Point object.

2. Custom JSON Property Getter Method

@JsonProperty("cityCoordinates")
public Map<String, Object> getCityCoordinatesJson() {
    if (this.cityCoordinates == null) {
        return null;
    }
    
    Map<String, Object> geoJson = new HashMap<>();
    geoJson.put("type", "Point");
    geoJson.put("coordinates", new double[] {
        this.cityCoordinates.getX(),  // longitude
        this.cityCoordinates.getY()   // latitude
    });
    
    return geoJson;
}

This method:

  • Is annotated with @JsonProperty("cityCoordinates") which tells Jackson: "When serializing, use this method's return value for the 'cityCoordinates' property"
  • Manually creates a Map structure that follows the GeoJSON format for Point objects
  • Extracts just the X (longitude) and Y (latitude) values from the JTS Point object
  • Returns a simple Map with arrays that Jackson knows how to serialize properly

Understanding Each Technology in the Stack

PostGIS and Geography Type

PostGIS extends PostgreSQL with geospatial capabilities. The geography(Point,4326) type:

  • geography: Stores coordinates on a spheroid (Earth's surface), accounting for curvature
  • Point: The geometry type (as opposed to LineString, Polygon, etc.)
  • 4326: The SRID (Spatial Reference ID) for WGS84, the standard coordinate system used in GPS

Hibernate Spatial

Hibernate Spatial is the bridge between your Java application and the PostGIS database:

  • It translates between Java objects and PostGIS types
  • The JTS Point class represents a point in two-dimensional space
  • It provides methods like getX() and getY() to access coordinates

Spring Boot and Jackson

Spring Boot uses Jackson for JSON serialization/deserialization:

  • By default, it tries to convert all properties of your entities to JSON
  • It doesn't know how to handle specialized objects like JTS Point
  • Annotations like @JsonIgnore and @JsonProperty control this process

GeoJSON Format

GeoJSON is a standard format for encoding geographic data structures:

  • For a Point, it looks like: {"type": "Point", "coordinates": [longitude, latitude]}
  • The coordinates array has longitude first, then latitude (x, y)
  • This is the format expected by most mapping libraries (like Leaflet, Mapbox, etc.)

Why Other Solutions Didn't Work

The other solutions (custom Jackson serializers or modules) didn't work likely because:

  1. They might not have been properly registered with Spring Boot
  2. They might not have been compatible with your specific JTS version
  3. There could have been conflicts with other Jackson configurations

The solution that worked is more direct—it bypasses Jackson's complex object serialization entirely and instead provides a simple structure that Jackson can easily convert to JSON.

When to Use This Approach

This approach (using @JsonIgnore + custom getter) is particularly useful when:

  1. You need to transform complex objects into a specific JSON format
  2. You want to avoid adding extra libraries or configurations
  3. You prefer to keep the transformation logic within your entity class

For larger projects, you might eventually want to separate these concerns by using DTOs (Data Transfer Objects) or dedicated serializers, but for your immediate needs, this approach works very well and is straightforward to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment