news 2026/7/19 9:16:30

MyBatis-Plus + PostGIS 实战(五):批量导入与空间数据初始化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MyBatis-Plus + PostGIS 实战(五):批量导入与空间数据初始化

一、开篇痛点

前面我们解决了:

  • Geometry 怎么存(第一篇)
  • 怎么展示(1.1 篇 + 第 4 篇)
  • 怎么查(第 2 篇)
  • 怎么翻页(第 3 篇)

但有一个问题一直没解决:数据从哪来?

二、三种方案总览

方案速度适用场景难度
PostgreSQL COPY⭐⭐⭐⭐⭐大数据量首选★★☆☆☆
MP saveBatch⭐⭐⭐中小数据 / API 导入★☆☆☆☆
ogr2ogr⭐⭐⭐⭐Shapefile 导入★★★☆☆

三、方案一:PostgreSQL COPY(最快)

3.1 CSV 文件

以下面这个csv为例

name,category,lon,lat 天安门,景点,116.397,39.903 故宫,景点,116.397,39.916 王府井,商圈,116.417,39.914 西单,商圈,116.375,39.913 颐和园,景点,116.275,39.999

保存时编码选 UTF-8,避免中文乱码

3.2 建临时表

CREATETABLEpoi_temp(nameTEXT,categoryTEXT,lonDOUBLEPRECISION,latDOUBLEPRECISION);

3.3 COPY 导入数据

Windows 本地开发推荐使用\copy(客户端复制),避免 PostgreSQL 服务权限问题:

\copy poi_temp(name,category,lon,lat)FROM'D:\testw\tests.csv'WITH(FORMAT csv,HEADER,DELIMITER',');

3.4 转换为 Geometry

INSERTINTOpoi(name,category,geom)SELECTname,category,ST_SetSRID(ST_MakePoint(lon,lat),4326)::geometry(Point,4326)FROMpoi_temp;

3.5 清理与建索引

DROPTABLEpoi_temp;CREATEINDEXidx_poi_geomONpoiUSINGGIST(geom);

四、方案二:MyBatis-Plus saveBatch(灵活)

4.1 Import DTO

publicclassPoiImportDto{privateStringname;privateStringcategory;privateDoublelon;privateDoublelat;// getter / setter}

4.2 Service 实现

@Override@TransactionalpublicintbatchImport(List<PoiImportDto>list){List<PoiEntity>entities=list.stream().map(dto->{PoiEntitye=newPoiEntity();e.setName(dto.getName());e.setCategory(dto.getCategory());Stringwkt="POINT("+dto.getLon()+" "+dto.getLat()+")";try{Geometrygeom=newWKTReader().read(wkt);geom.setSRID(4326);e.setGeom(geom);}catch(ParseExceptionex){thrownewRuntimeException("Invalid WKT: "+wkt,ex);}returne;}).collect(Collectors.toList());saveBatch(entities,1000);// 每批 1000 条returnentities.size();}

4.3 Controller

@PostMapping("/import")publicR<Integer>importPoi(@RequestBodyList<PoiImportDto>list){returnR.ok(poiService.batchImport(list));}

4.4 curl 测试

curl-XPOST http://localhost:8080/poi/import\-H"Content-Type: application/json"\-d'[ {"name":"天安门","category":"景点","lon":116.397,"lat":39.903}, {"name":"故宫","category":"景点","lon":116.397,"lat":39.916} ]'

⚠️ saveBatch 的 batchSize 设太大(如 10000)会内存溢出,设太小(如 100)会频繁提交变慢。1000~2000 是甜点区。

五、方案三:ogr2ogr(Shapefile 导入)

5.1 安装 GDAL

# Ubuntuapt-getinstallgdal-bin# Macbrewinstallgdal

5.2 查看 Shapefile 信息

ogrinfo-sopoi.shp poi

5.3 导入命令

ogr2ogr-f"PostgreSQL"\PG:"dbname=postgis_demo user=postgres password=123456 host=localhost"\poi.shp\-nlnpoi\-overwrite\-lcoGEOMETRY_NAME=geom\-lcoFID=id\-nltPOINT\-s_srsEPSG:4326\-t_srsEPSG:4326

参数说明:

参数含义
-f “PostgreSQL”目标格式为 PostgreSQL
-nln poi目标表名
-overwrite覆盖已有表
-lco GEOMETRY_NAME=geomGeometry 列名
-lco FID=id主键列名
-nlt POINT几何类型
-s_srs / -t_srs源/目标坐标系

⚠️ Shapefile 中文乱码?加参数:--config SHAPE_ENCODING UTF-8
这个方案也可以用程序来实现,一般生产是采用这种方式,写一个使用gdal导入shp至postgis数据库的方法,前端通过传一个含shp的压缩文件,调用接口将数据发布到数据库,这个后面会单独出一期文章

六、导入后必做的 4 步验证

数据导入后,建议做以下四项验证:

-- 1. 总数SELECTCOUNT(*)FROMpoi;-- 2. SRID 一致性(期望结果:4326)SELECTDISTINCTST_SRID(geom)FROMpoi;-- 3. 几何有效性(期望结果:0)SELECTCOUNT(*)FROMpoiWHERENOTST_IsValid(geom);-- 4. 空间索引是否存在SELECTindexnameFROMpg_indexesWHEREtablename='poi';
验证项期望结果不通过怎么办
总数与实际数据量一致检查 CSV 行数 / 编码
SRID全部为 4326UPDATE 设置 SRID
几何有效性0 条无效排查坐标顺序 / 范围
空间索引idx_poi_geom 存在CREATE INDEX

七、踩坑复盘

⚠️ 坑 1:CSV 的经纬度顺序

很多人写成 lat, lon,但 PostGIS 要的是 lon, lat(即 X, Y)。写反了,数据会跑到非洲去。

-- 正确:经度在前,纬度在后ST_MakePoint(116.397,39.903)--

⚠️ 坑 2:SRID 忘记设置

ST_MakePoint(lon, lat) 不设置 SRID,默认是 0。后续空间查询报错:Mixed SRID。

-- 解决:显式设置 SRIDST_SetSRID(ST_MakePoint(lon,lat),4326)::geometry(Point,4326)

⚠️ 坑 3:saveBatch 的 batchSize

设太大(如 10000)→ 内存溢出。设太小(如 100)→ 频繁提交,慢。1000~2000 是甜点区。

⚠️ 坑 4:Shapefile 中文乱码

中文字段名 / 属性值乱码。解决:ogr2ogr 加--config SHAPE_ENCODING UTF-8

⚠️ 坑 5:事务太大导致超时

一次性 saveBatch(50 万条) → 事务超时。解决:分批 + @Transactional + 合理 batchSize。

⚠️ 坑 6:Windows 下 COPY 权限问题

在 Windows 上,COPY 要求 PostgreSQL 服务账户对目标路径有读取权限。如果报 Permission denied,改用\copy(客户端复制),只需要当前用户权限。

-- 服务端复制(需要数据库进程权限)COPY poi_tempFROM'/data/poi.csv'...-- 客户端复制(只需要当前用户权限,推荐本地开发使用)\copy poi_tempFROM'D:\testw\tests.csv'...

⚠️ 坑 7:Excel 打开 CSV 只占一个单元格

直接双击 CSV 用 Excel 打开,可能因系统默认分隔符不是英文逗号,导致整行被识别为一个字段。解决方案:用记事本 / VS Code 创建 CSV,或在 Excel 中通过“数据 → 获取数据 → 从文本/CSV”导入并显式指定逗号分隔符。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 9:15:38

Python控制结构实战:超市收银系统开发指南

1. 项目概述&#xff1a;用项目实战掌握Python基础控制结构刚接触Python编程时&#xff0c;循环和条件判断往往是第一个需要突破的难点。我在带新人时发现&#xff0c;单纯看语法示例的效果远不如动手做一个小项目。这次我们就用实际案例来打通for循环、while循环和if-else条件…

作者头像 李华
网站建设 2026/7/19 9:15:20

Python第一次相关练习作业

第一题&#xff1a;打印一个空心菱形【代码】n int(input("请输入您要打印的菱形的奇数的行数&#xff1a;")) for i in range(n):#打印空格if i < (n 1) / 2: for j in range((n 1) // 2 - 1 - i):print(" ",end"")else:for j in range(…

作者头像 李华
网站建设 2026/7/19 9:14:36

商城数据资产化:从数据孤岛到价值引擎的技术架构与实践

在电商竞争白热化的今天&#xff0c;你是否也面临这样的困境&#xff1a;投入大量资源开发了功能齐全的商城系统&#xff0c;却发现用户留存率始终上不去&#xff1f;促销活动做了不少&#xff0c;转化效果却越来越差&#xff1f;竞争对手似乎总能精准把握用户需求&#xff0c;…

作者头像 李华
网站建设 2026/7/19 9:13:43

【电路设计】TVS 瞬态抑制二极管选型与应用笔记

1. TVS 简介 TVS&#xff08;Transient Voltage Suppressor&#xff0c;瞬态电压抑制二极管&#xff09;是一种用于抑制电压瞬态冲击的保护器件。 它的主要作用&#xff1a; 吸收 ESD 静电抑制电源浪涌限制瞬态过电压保护后级 IC、电源芯片、MCU、通信接口等 TVS 的特点&#x…

作者头像 李华
网站建设 2026/7/19 9:12:41

Android安全机制解析:从内核到应用的全方位防护

1. Android安全机制概述 在移动互联网时代&#xff0c;Android作为全球市场份额最大的移动操作系统&#xff0c;其安全性直接影响着数十亿用户的隐私和数据安全。我从事Android开发十余年&#xff0c;见证了Android安全体系从基础沙箱隔离到如今多层次防御的演进历程。Android的…

作者头像 李华