zer*_*gen 10 jpa haversine spring-data spring-data-jpa
我正在尝试使用Haversine公式来查找具有Pageable的Spring Data JPA Query中某个位置附近的实体,但我没有完成它.
我的第一种方法看起来像这样
@Query("SELECT m, (6371 * acos(cos(radians(:latitude)) * cos(radians(m.latitude)) * cos(radians(m.longitude) - radians(:longitude)) + sin(radians(:latitude)) * sin(radians(m.latitude)))) as dist FROM Entity m WHERE dist < :distance ORDER BY dist DESC")
public List<Entity> findEntitiesByLocation(@Param("latitude") final double latitude, @Param("longitude") final double longitude, @Param("distance") final double distance, Pageable pageable);
Run Code Online (Sandbox Code Playgroud)
但它失败了,因为Spring/JPA似乎无法在where子句中使用别名.stacktrace中的SQL看起来像这样
select (...),(haversine) as col_1_0_ where dist<? order by col_1_0_ DESC
Run Code Online (Sandbox Code Playgroud)
所以where子句中的别名不会被替换.使用"col_1_0_"(不使用")代替dist也不起作用.
根据这个SO答案,至少MySQL是由内部解释的,并且在Where子句中使用别名是不可能的.建议的解决方案是使用HAVING而不是WHERE,但在HAVING子句中,别名不会被解析.
我知道我可以将Haversine公式移动到where子句中,但我仍然需要在Order By子句中使用它,我认为它可能会在Order By子句中使用相同的长Haversine公式来降低性能,因为我选择了几百个数千个实体.
然后我尝试手动创建查询,但我不知道如何将Pageable应用于此:
@Override
public List<Entity> findEntitiesByLocation(final double latitude, final double longitude, final double distance, Pageable pageable) {
final javax.persistence.Query query = this.entityManager.createQuery(SELECT_ENTITES_BY_DISTANCE);
query.setParameter("latitude", latitude);
query.setParameter("longitude", longitude);
query.setParameter("distance", distance);
final List<Entity> entities = query.getResultList();
// order by distance
entities .sort(new EntityDistanceComparator(latitude, longitude));
return entities ;
}
Run Code Online (Sandbox Code Playgroud)
所以,我要么需要使用@Query的第一种方法(我更喜欢),要么使用Pageable的第二种方法
zer*_*gen 15
在Neil Stockton的帮助下,我决定在WHERE和ORDER BY子句中使用Haversine公式坚持非原生查询,这样我仍然可以使用Spring的分页功能.我的最终解决方案如下:
static final String HAVERSINE_PART = "(6371 * acos(cos(radians(:latitude)) * cos(radians(m.latitude)) * cos(radians(m.longitude) - radians(:longitude)) + sin(radians(:latitude)) * sin(radians(m.latitude))))";
@Query("SELECT m FROM Entity m WHERE "+HAVERSINE_PART+" < :distance ORDER BY "+HAVERSINE_PART+" DESC")
public List<Entity> findEntitiesByLocation(@Param("latitude") final double latitude, @Param("longitude") final double longitude, @Param("distance") final double distance, Pageable pageable);
Run Code Online (Sandbox Code Playgroud)
使用100.000个实体,这需要大约585毫秒才能找到给定位置的前10个最近实体,大约需要8秒才能找到1.000.000个实体中的前10个最近实体,这对我来说现在还可以.如果我优化查询,我会在这里发布.
| 归档时间: |
|
| 查看次数: |
1812 次 |
| 最近记录: |