使用Hibernate-Spatial查询范围内实体时遇WITHIN函数未找到错误
解决Hibernate-Spatial中WITHIN函数找不到的问题
看起来你遇到的Function "WITHIN" not found错误,核心原因是实体映射的空间类型配置错误,导致Hibernate无法生成正确的SQL调用MySQL的空间函数,另外还有一些依赖和配置细节需要调整。下面一步步帮你解决:
1. 核心问题:空间字段映射错误
你的Address类中,location字段用了@Embedded的Location类(包含lat和lon两个Double字段),同时加了@Column(columnDefinition = "point")——这是冲突的:
@Embedded会把Location的lat和lon映射成数据库中两个独立的列,而不是单个POINT类型的列- Hibernate-Spatial的
WITHIN函数需要操作的是数据库空间类型字段(比如POINT),而不是两个普通数值列,这直接导致SQL生成错误,数据库无法识别WITHIN函数的用法
修正实体映射
修改Address类,直接使用Geometry类型存储位置,替代Location嵌入类:
@Entity public class Address extends UUIDEntity2 implements HasEmailAddress, HasLocation { // 使用Hibernate-Spatial的GeometryType映射POINT类型字段 @Type(type = "org.hibernate.spatial.GeometryType") @Column(columnDefinition = "POINT SRID 4326") // 指定SRID为WGS84坐标系 private Geometry location; // 保留lat/lon的访问方法(可选) public Double getLat() { return location != null ? location.getCoordinate().y : null; } public Double getLon() { return location != null ? location.getCoordinate().x : null; } // 设置lat/lon的方法(可选) public void setLocationFromLatLon(Double lat, Double lon) { if (lat != null && lon != null) { this.location = new GeometryFactory(new PrecisionModel(), 4326) .createPoint(new Coordinate(lon, lat)); } } // 标准getter/setter public Geometry getLocation() { return location; } public void setLocation(Geometry location) { this.location = location; } }
同时Resource类的关联逻辑无需修改,只需确保后续查询时引用的是Geometry类型的字段即可。
2. 修正Repository查询语句
现在resource.address.location是标准的Geometry类型了,查询语句可以保持不变,但要确保Hibernate能正确解析:
public interface ResourceRepository extends ExtendedJpaRepository<Resource, String> { @Query(value = "select resource from Resource resource where within(resource.address.location, :circle) = true") List<Resource> test(@Param("circle") Geometry circle); }
3. 确保依赖版本兼容
你的hibernate-entitymanager没有指定版本,可能和hibernate-spatial(5.2.12.Final)版本不匹配,导致方言或函数映射错误。统一版本:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.2.12.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-spatial</artifactId> <version>5.2.12.Final</version> </dependency>
4. 调整测试代码的Circle生成
确保生成的Circle带有正确的SRID(和数据库字段一致,这里用4326):
public Geometry createCircle(double x, double y, double radius) { // 指定SRID为4326(WGS84),和数据库字段匹配 GeometricShapeFactory shapeFactory = new GeometricShapeFactory( new GeometryFactory(new PrecisionModel(), 4326) ); shapeFactory.setNumPoints(32); shapeFactory.setCentre(new Coordinate(x, y)); shapeFactory.setSize(radius * 2); // size是直径 return shapeFactory.createCircle(); }
5. 验证数据库配置
- 确保你的MySQL版本是5.6+(支持空间函数)
- 确保
address表的location字段是POINT SRID 4326类型,且表的存储引擎是InnoDB(MyISAM也支持,但InnoDB更推荐)
完成这些调整后,Hibernate会正确生成调用MySQLST_Within函数的SQL,就不会再出现找不到函数的错误了。
内容的提问来源于stack exchange,提问作者Chinmoy Acharjee




