Spring Query:具有可分页的Haversine公式

时间:2015-04-11 10:14:05

标签: jpa spring-data spring-data-jpa haversine

我正在尝试使用Haversine formula查找Spring Data JPA Query中某个位置附近的实体Pageable,但我还没完成。

我的第一种方法看起来像这样

    @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);

但它失败了,因为Spring / JPA似乎无法在where子句中使用别名。 stacktrace中的SQL看起来像这样

select (...),(haversine) as col_1_0_ where dist<? order by col_1_0_ DESC

所以不替换where子句中的别名。使用“col_1_0_”(不使用“)代替dist也不起作用。

根据这个SO Answer,至少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 ;
}

所以,我要么需要第一种使用@Query的方法(我更喜欢)或第二种使用Pageable的方法

2 个答案:

答案 0 :(得分:14)

在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);

对于100.000个实体,这需要大约585毫秒才能找到到达给定位置的前10个最近实体,大约需要8秒才能找到1.000.000个实体中的前10个最近实体,这对我来说现在还可以。如果我优化了查询,我会在此处发布。

答案 1 :(得分:3)

JPQL不允许&#34;结果别名&#34;在WHERE,HAVING条款中;它们只能在ORDER子句中使用。同样地,使用RADIANS,COS,ACOS,SIN,ASIN等是不可移植的JPQL;一些实现可能会支持它们(我知道DataNucleus JPA确实如此,看起来你也有Hibernate),但不能保证。

只需使用NativeQuery并推送您需要的任何SQL。您丢失了(RDBMS)可移植性,但是您没有使用上面尝试的内容(对于JPA提供程序或RDBMS)。