使用JPA向MySQL插入POINT空间数据时遇数据截断错误排查
尝试通过JPA向MySQL插入/读取POINT类型地理数据,流程为:前端获取经纬度→拼接成WKT字符串→用WKTReader转成org.locationtech.jts.geom.Point→构建实体调用save方法,报错:
Data truncation: Cannot get geometry object from data you send to the GEOMETRY field
直接插入WKT字符串可成功,但数据以BLOB形式存储,需解决上述两个问题。
核心问题分析
报错源于Hibernate Spatial未正确将JTS的Point对象转换为MySQL可识别的POINT格式,同时存在依赖版本不兼容、坐标顺序错误等潜在问题;直接存WKT字符串成BLOB是因为未触发MySQL的地理类型解析。
解决方案步骤
1. 统一依赖版本,消除兼容性冲突
当前hibernate-core与hibernate-spatial版本不一致,会导致空间类型转换逻辑异常,修改build.gradle统一版本:
implementation 'org.hibernate:hibernate-core:5.6.15.Final' implementation group: 'org.locationtech.jts', name: 'jts-core', version: '1.16.1' implementation group: 'org.hibernate', name: 'hibernate-spatial', version: '5.6.15.Final'
2. 修正实体字段注解配置
给Point字段添加@Type注解,明确指定空间类型处理逻辑,同时推荐指定坐标系SRID(常用WGS84坐标系SRID=4326):
import org.hibernate.annotations.Type; import org.locationtech.jts.geom.Point; // 其他实体代码... @Column(name = "club_point", columnDefinition = "POINT SRID=4326") @Type(type = "org.hibernate.spatial.GeometryType") private Point clubPoint;
3. 优化Point对象创建方式,修正坐标顺序
JTS的Coordinate构造参数为经度在前、纬度在后(与WKT格式一致),直接用GeometryFactory创建对象比WKT字符串转换更高效,还能避免格式错误:
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.PrecisionModel; // 替换原makePoint方法 public Point makePoint(Float longitude, Float latitude) { GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326); return geometryFactory.createPoint(new Coordinate(longitude, latitude)); }
注意:调用该方法时要传入正确的经纬度顺序,避免坐标颠倒。
4. 验证数据库连接配置
确保MySQL数据源配置正确(MySQL 8.0+需使用com.mysql.cj.jdbc.Driver),示例application.yml配置:
spring: datasource: url: jdbc:mysql://localhost:3306/your_db?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true driver-class-name: com.mysql.cj.jdbc.Driver jpa: properties: hibernate: database-platform: org.hibernate.spatial.dialect.mysql.MySQL8SpatialDialect
5. 解决WKT字符串存为BLOB的问题
若需直接用WKT字符串插入,需通过MySQL的ST_GeomFromText函数转换为POINT类型,可使用JPA原生查询实现:
@Modifying @Query(value = "INSERT INTO club (club_point, other_fields) VALUES (ST_GeomFromText(?1), ?2, ...)", nativeQuery = true) void saveClubWithWkt(String wktPoint, Object... otherParams);
但更推荐使用JTS的Point对象配合Hibernate Spatial,便于Java层直接操作地理数据及后续空间查询。
验证方法
- 重启应用后调用接口插入数据
- 在MySQL中执行
SELECT ST_AsText(club_point) FROM club;,若返回POINT(经度 纬度)格式字符串,说明插入成功 - 读取数据时,直接通过Entity的
clubPoint.getX()(经度)、clubPoint.getY()(纬度)获取坐标值
内容的提问来源于stack exchange,提问作者GyeongEun Kim

