MyBatis-Plus + PostGIS 实战(1.1):Geometry 字段在 Swagger 中的优雅展示

目录

    • 一、问题回顾
    • 二、解决思路
    • 三、核心实现
      • [3.1 GeometryJsonSerializer(核心)](#3.1 GeometryJsonSerializer(核心))
      • [3.2 Jackson 全局配置(推荐)](#3.2 Jackson 全局配置(推荐))
      • [3.3 Entity 是否需要修改?](#3.3 Entity 是否需要修改?)
      • [3.4 Controller 瘦身](#3.4 Controller 瘦身)
    • 四、效果展示
      • [4.1 接口返回](#4.1 接口返回)
      • [4.2 Swagger 文档效果](#4.2 Swagger 文档效果)
    • [五、进阶:返回 GeoJSON(加分项)](#五、进阶:返回 GeoJSON(加分项))
      • [5.1 GeometryGeoJsonSerializer](#5.1 GeometryGeoJsonSerializer)
      • [5.2 返回效果](#5.2 返回效果)
    • 六、常见坑位提醒
    • 七、小结

一、问题回顾

在上一篇(基础篇)中,https://blog.csdn.net/weixin_45011889/article/details/162789976?fromshare=blogdetail\&sharetype=blogdetail\&sharerId=162789976\&sharerefer=PC\&sharesource=weixin_45011889\&sharefrom=from_link,我们已经实现了 Geometry 字段的增删改查,TypeHandler 也能正确工作。但发现了一个问题:

Service 层会充斥着大量的 WKT 转换代码:

java 复制代码
resp.setGeom(wktWriter.write(entity.getGeom()));

这带来了几个问题:

  • Service 污染:Service 本应只关心业务逻辑,现在却要关心展示格式
  • Swagger 不友好:Geometry 字段在 Swagger 文档中显示为 Object,前端同事无法直观理解
  • 重复劳动:每个接口都要写一遍 WKT 转换

有没有更优雅的方式?

二、解决思路

核心观点:TypeHandler 负责"JTS ↔ JDBC",Jackson 负责"JTS ↔ JSON"。各司其职。

层级 职责
TypeHandler JTS Geometry ↔ PostGIS EWKB(JDBC 持久化)
Jackson Serializer JTS Geometry → WKT / GeoJSON(JSON 展示)

三、核心实现

3.1 GeometryJsonSerializer(核心)

新增文件:handler/GeometryJsonSerializer.java

java 复制代码
package org.pgd.postgisdemo.handler;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTWriter;

import java.io.IOException;

/**
 * Jackson 序列化器:将 JTS Geometry 转为 WKT 字符串
 */
public class GeometryJsonSerializer extends JsonSerializer<Geometry> {

    private static final WKTWriter WKT_WRITER = new WKTWriter();

    @Override
    public void serialize(Geometry value, JsonGenerator gen,
                         SerializerProvider serializers)
            throws IOException {
        if (value == null) {
            gen.writeNull();
            return;
        }
        // Geometry → WKT
        gen.writeString(WKT_WRITER.write(value));
    }
}

关键点:只做一件事------Geometry → WKT,不依赖 MyBatis,不依赖 Spring,职责单一。

3.2 Jackson 全局配置(推荐)

新增文件:config/JacksonConfig.java

java 复制代码
package org.pgd.postgisdemo.config;

import com.fasterxml.jackson.databind.module.SimpleModule;
import org.locationtech.jts.geom.Geometry;
import org.pgd.postgisdemo.handler.GeometryJsonSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    public SimpleModule geometryModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(
                Geometry.class,
                new GeometryJsonSerializer()
        );
        return module;
    }
}

为什么推荐全局配置?

  • Entity 保持纯净,不污染领域模型
  • 一处配置,全局生效
  • 以后想换 GeoJSON,只改这一个类

3.3 Entity 是否需要修改?

答:不需要。 Entity 完全不用改:

java 复制代码
@Data
@TableName(value = "poi", autoResultMap = true)
public class PoiEntity {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private String category;
    @TableField(typeHandler = GeometryTypeHandler.class)
    private Geometry geom;
}

Jackson 全局配置会自动对 Geometry 类型生效,不需要任何注解。

3.4 Controller 瘦身

改造前(第一篇):

java 复制代码
// Service 里要写
resp.setGeom(wktWriter.write(entity.getGeom()));

// 或者 Controller 里要写
resp.setGeom(new WKTWriter().write(entity.getGeom()));

改造后(1.1 篇):

java 复制代码
@GetMapping("/detail/{id}")
public R<PoiEntity> detail(@PathVariable Long id) {
    PoiEntity entity = poiService.getById(id);
    return entity != null ? R.ok(entity) : R.fail("POI 不存在");
}

Controller 彻底无空间逻辑,Service 不再关心展示格式。

四、效果展示

4.1 接口返回

改造前:

json 复制代码
// Service 手动转换后
"geom": "POINT(116.397 39.903)"

改造后:

json 复制代码
// 直接返回 PoiEntity,Jackson 自动序列化
{
  "id": 1,
  "name": "天安门",
  "category": "景点",
  "geom": "POINT(116.397 39.903)"
}

4.2 Swagger 文档效果

json 复制代码
{
  "id": 1,
  "name": "天安门",
  "category": "景点",
  "geom": "POINT(116.397 39.903)"
}

前端同事一眼看懂,联调效率大幅提升。

五、进阶:返回 GeoJSON(加分项)

如果前端使用地图组件(如 Mapbox / Leaflet / OpenLayers),GeoJSON 比 WKT 更友好。

5.1 GeometryGeoJsonSerializer

java 复制代码
public class GeometryGeoJsonSerializer
        extends JsonSerializer<Geometry> {

    private final ObjectMapper geoJsonMapper =
            new ObjectMapper(new GeoJsonObjectMapper());

    @Override
    public void serialize(Geometry value, JsonGenerator gen,
                         SerializerProvider serializers)
            throws IOException {
        if (value == null) {
            gen.writeNull();
            return;
        }
        gen.writeRawValue(
                geoJsonMapper.writeValueAsString(value)
        );
    }
}

5.2 返回效果

json 复制代码
{
  "id": 1,
  "name": "天安门",
  "category": "景点",
  "geom": {
    "type": "Point",
    "coordinates": [116.397, 39.903]
  }
}

前端地图组件可以直接消费,无需额外转换。

六、常见坑位提醒

坑位 说明
WKTWriter 线程安全 JTS WKTWriter 是线程安全的,可以定义为 static final
重复注册 不要同时在 @JsonSerialize 和全局配置里注册,会冲突
TypeHandler vs Jackson TypeHandler 管持久化(JDBC),Jackson 管展示(JSON),职责不同
Entity 纯洁性 推荐全局配置,不污染 Entity 领域模型

七、小结

本篇只新增了两个类:

  • GeometryJsonSerializer --- 核心序列化逻辑
  • JacksonConfig --- 全局注册

实现了 Geometry 字段在 Swagger 和接口返回中的优雅展示,Controller 与 Service 完全无需感知展示格式。

三句话总结:

  • TypeHandler 管持久化,Jackson 管展示
  • 全局配置优于注解,Entity 保持纯净
  • 需要 GeoJSON 时,只换 Serializer,不动业务代码
相关推荐
极客先躯2 小时前
高级java每日一道面试题-2026年04月05日-实战篇[Docker]-如何排查容器 OOM 的问题?
java·运维·docker·容器·内存溢出·高级面试
带娃的IT创业者2 小时前
监控并非安全:当隐私成为技术的祭品
java·开发语言·安全·数据安全·监控·隐私保护·加密技术
叩码以求索2 小时前
使用next数组加速匹配过程
java·数据结构·算法
余额瞒着我当琳2 小时前
C++ 基础核心概念精讲:引用、内联与 nullptr
java·开发语言·c++
再玩一会儿看代码3 小时前
JUnit 测试框架详解:从实际开发、业务测试到 Java 面试高频问题
java·经验分享·笔记·junit·面试
yxlalm3 小时前
2.java秒杀项目第二课-后端登录功能
java·开发语言
亦暖筑序4 小时前
AgentScope-Java 入门:用 SSE 展示评审任务进度
java·agent·ai编程
吴声子夜歌4 小时前
Reids 6.x——集群环境部署
java·redis