Java 如何使用Hibernate/JPA标准根据派生值进行选择和排序。例如,使用纬度和经度计算距离

Java 如何使用Hibernate/JPA标准根据派生值进行选择和排序。例如,使用纬度和经度计算距离,java,hibernate,jpa,orm,spring-data-jpa,Java,Hibernate,Jpa,Orm,Spring Data Jpa,我有一个web应用程序,允许用户根据多种条件搜索餐厅,例如:;菜肴、座位、BYOB等 我有新的要求,允许按距离(以英里为单位)搜索这些餐厅,并允许按距离对这些餐厅进行排序。我希望能够将此作为当前搜索实现的一部分来完成,但我一直在研究如何在一次获取中完成此操作 我目前的搜索实现涉及ORM。我使用Hibernate作为我的JPA提供者,并在MySQL 5.7上使用Spring数据JPA。 现有搜索动态地使用JPA标准和Spring数据JPA规范API,我可以在RestaurantPository界面

我有一个web应用程序,允许用户根据多种条件搜索餐厅,例如:;菜肴、座位、BYOB等 我有新的要求,允许按距离(以英里为单位)搜索这些餐厅,并允许按距离对这些餐厅进行排序。我希望能够将此作为当前搜索实现的一部分来完成,但我一直在研究如何在一次获取中完成此操作

我目前的搜索实现涉及ORM。我使用Hibernate作为我的JPA提供者,并在MySQL 5.7上使用Spring数据JPA。 现有搜索动态地使用JPA标准和Spring数据JPA规范API,我可以在RestaurantPository界面上调用findAll(规范规范,可分页)或findAll(规范规范,排序)。此接口扩展了Crudepository和JpaSpecificationExecutor。 这使得根据需要为数据库表中已有的字段添加谓词变得容易。问题是,距离是基于提供的纬度和经度导出的值

我需要能够做这样的事情:

select r.*, acos(sin(radians([USER_LATITUDE])) * sin(radians(latitude)) + cos(radians([USER_LATITUDE])) * cos(radians(latitude)) * cos(radians(longitude) - (radians([USER_LONGITUDE])))) * 3959 as distance from
restaurant r join town t on r.town_id = t.id join
where r.byob = 'Y' and r.capacity > 200
and distance < [DISTANCE] order by [DISTANCE];
选择r.*,acos(sin(弧度([USER_纬度])*sin(弧度(纬度))+cos(弧度([USER_纬度])*cos(弧度(纬度))*cos(弧度(经度)-(弧度([USER_经度])*3959作为到的距离
餐厅r加入城镇t在r.town_id=t.id加入
其中r.byob='Y'和r.capacity>200
距离<[distance]按[distance]顺序排列;
在这个查询中,距离是一个计算值,它也用于排序。提供的值是用户的纬度和经度值以及最大距离

我研究/考虑过的选项:

  • 放弃ORMs,使用普通JDBC。将解决问题,但失去ORM的所有好处

  • 使用Hibernate@Formula,但这不可行,因为我无法为@Formula提供参数。我需要提供的参数是距离

  • 研究了HibernateSpatial,但它不支持这个用例,除非我忽略了一些东西

  • 考虑将我的所有数据扁平化为支持基于距离的查找的ElasticSearch或SOLR之类的东西,但这将是一场维护噩梦,在关系数据更改时保持索引

  • 我的数据库中有45k个“城镇”,我考虑预先计算它们之间的距离。不太可行,因为这将导致18亿行

  • 目前正在研究使用一个边界圆来优化具有最小和最大纬度和经度的select查询,然后以某种方式使用本机函数调用来按计算的距离排序,然后在获取实体时,使用侦听器加载后调用来再次推导出应用程序中的距离

  • 如果所有这些都失败了,我可能不得不咬紧牙关,使用我上面描述的SQL的本机查询进行第二次查找,但这将涉及第二次获取,并将导致我不希望的性能下降


    有人能为我提供解决这个问题的建议吗?

    您没有提到的另一种方法是使用存储过程,并让hibernate使用各种参数调用它。然后,存储过程将返回映射的餐厅列表,与常规查询类似,只是存储过程负责应用距离要求。我可以这样做,但仍需要为其他条件运行单独的查询。我需要能够做,距离搜索和烹饪类型搜索,并添加任何其他条件在一个单一的查询。这就是问题所在。您没有提到的另一种选择是使用存储过程,并让hibernate使用各种参数调用它。然后,存储过程将返回映射的餐厅列表,与常规查询类似,只是存储过程负责应用距离要求。我可以这样做,但仍需要为其他条件运行单独的查询。我需要能够做,距离搜索和烹饪类型搜索,并添加任何其他条件在一个单一的查询。这就是问题所在。