MyBatis-Plus + PostGIS 实战(1.1):Geometry 字段在 Swagger 中的优雅展示
目录一、问题回顾二、解决思路三、核心实现3.1 GeometryJsonSerializer核心3.2 Jackson 全局配置推荐3.3 Entity 是否需要修改3.4 Controller 瘦身四、效果展示4.1 接口返回4.2 Swagger 文档效果五、进阶返回 GeoJSON加分项5.1 GeometryGeoJsonSerializer5.2 返回效果六、常见坑位提醒七、小结一、问题回顾在上一篇基础篇中https://blog.csdn.net/weixin_45011889/article/details/162789976?fromshareblogdetailsharetypeblogdetailsharerId162789976sharereferPCsharesourceweixin_45011889sharefromfrom_link我们已经实现了 Geometry 字段的增删改查TypeHandler 也能正确工作。但发现了一个问题Service 层会充斥着大量的 WKT 转换代码resp.setGeom(wktWriter.write(entity.getGeom()));这带来了几个问题Service 污染Service 本应只关心业务逻辑现在却要关心展示格式Swagger 不友好Geometry 字段在 Swagger 文档中显示为 Object前端同事无法直观理解重复劳动每个接口都要写一遍 WKT 转换有没有更优雅的方式二、解决思路核心观点TypeHandler 负责JTS ↔ JDBCJackson 负责JTS ↔ JSON。各司其职。层级职责TypeHandlerJTS Geometry ↔ PostGIS EWKBJDBC 持久化Jackson SerializerJTS Geometry → WKT / GeoJSONJSON 展示三、核心实现3.1 GeometryJsonSerializer核心新增文件handler/GeometryJsonSerializer.javapackageorg.pgd.postgisdemo.handler;importcom.fasterxml.jackson.core.JsonGenerator;importcom.fasterxml.jackson.databind.JsonSerializer;importcom.fasterxml.jackson.databind.SerializerProvider;importorg.locationtech.jts.geom.Geometry;importorg.locationtech.jts.io.WKTWriter;importjava.io.IOException;/** * Jackson 序列化器将 JTS Geometry 转为 WKT 字符串 */publicclassGeometryJsonSerializerextendsJsonSerializerGeometry{privatestaticfinalWKTWriterWKT_WRITERnewWKTWriter();Overridepublicvoidserialize(Geometryvalue,JsonGeneratorgen,SerializerProviderserializers)throwsIOException{if(valuenull){gen.writeNull();return;}// Geometry → WKTgen.writeString(WKT_WRITER.write(value));}}关键点只做一件事——Geometry → WKT不依赖 MyBatis不依赖 Spring职责单一。3.2 Jackson 全局配置推荐新增文件config/JacksonConfig.javapackageorg.pgd.postgisdemo.config;importcom.fasterxml.jackson.databind.module.SimpleModule;importorg.locationtech.jts.geom.Geometry;importorg.pgd.postgisdemo.handler.GeometryJsonSerializer;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassJacksonConfig{BeanpublicSimpleModulegeometryModule(){SimpleModulemodulenewSimpleModule();module.addSerializer(Geometry.class,newGeometryJsonSerializer());returnmodule;}}为什么推荐全局配置Entity 保持纯净不污染领域模型一处配置全局生效以后想换 GeoJSON只改这一个类3.3 Entity 是否需要修改答不需要。 Entity 完全不用改DataTableName(valuepoi,autoResultMaptrue)publicclassPoiEntity{TableId(typeIdType.AUTO)privateLongid;privateStringname;privateStringcategory;TableField(typeHandlerGeometryTypeHandler.class)privateGeometrygeom;}Jackson 全局配置会自动对 Geometry 类型生效不需要任何注解。3.4 Controller 瘦身改造前第一篇// Service 里要写resp.setGeom(wktWriter.write(entity.getGeom()));// 或者 Controller 里要写resp.setGeom(newWKTWriter().write(entity.getGeom()));改造后1.1 篇GetMapping(/detail/{id})publicRPoiEntitydetail(PathVariableLongid){PoiEntityentitypoiService.getById(id);returnentity!null?R.ok(entity):R.fail(POI 不存在);}Controller 彻底无空间逻辑Service 不再关心展示格式。四、效果展示4.1 接口返回改造前// Service 手动转换后geom:POINT(116.397 39.903)改造后// 直接返回 PoiEntityJackson 自动序列化{id:1,name:天安门,category:景点,geom:POINT(116.397 39.903)}4.2 Swagger 文档效果{id:1,name:天安门,category:景点,geom:POINT(116.397 39.903)}前端同事一眼看懂联调效率大幅提升。五、进阶返回 GeoJSON加分项如果前端使用地图组件如 Mapbox / Leaflet / OpenLayersGeoJSON 比 WKT 更友好。5.1 GeometryGeoJsonSerializerpublicclassGeometryGeoJsonSerializerextendsJsonSerializerGeometry{privatefinalObjectMappergeoJsonMappernewObjectMapper(newGeoJsonObjectMapper());Overridepublicvoidserialize(Geometryvalue,JsonGeneratorgen,SerializerProviderserializers)throwsIOException{if(valuenull){gen.writeNull();return;}gen.writeRawValue(geoJsonMapper.writeValueAsString(value));}}5.2 返回效果{id:1,name:天安门,category:景点,geom:{type:Point,coordinates:[116.397,39.903]}}前端地图组件可以直接消费无需额外转换。六、常见坑位提醒坑位说明WKTWriter 线程安全JTS WKTWriter 是线程安全的可以定义为 static final重复注册不要同时在 JsonSerialize 和全局配置里注册会冲突TypeHandler vs JacksonTypeHandler 管持久化JDBCJackson 管展示JSON职责不同Entity 纯洁性推荐全局配置不污染 Entity 领域模型七、小结本篇只新增了两个类GeometryJsonSerializer — 核心序列化逻辑JacksonConfig — 全局注册实现了 Geometry 字段在 Swagger 和接口返回中的优雅展示Controller 与 Service 完全无需感知展示格式。三句话总结TypeHandler 管持久化Jackson 管展示全局配置优于注解Entity 保持纯净需要 GeoJSON 时只换 Serializer不动业务代码