无法在 FROM 子句中使用子查询编写 JPQL 查询 - Spring Data Jpa - Hibernate

Posted

技术标签:

【中文标题】无法在 FROM 子句中使用子查询编写 JPQL 查询 - Spring Data Jpa - Hibernate【英文标题】:Cannot writing JPQL query with subquery in FROM clause - Spring Data Jpa - Hibernate 【发布时间】:2021-12-16 01:07:07 【问题描述】:

我的停车场实体:

public final class ParkingLotEntity 

    @Id
    @Column(name = "[ID]", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "[PARKING_LOT_TYPE_ID]", referencedColumnName = "[ID]", nullable = false)
    private ParkingLotTypeEntity parkingLotTypeEntity;

    @Column(name = "[LATITUDE]", nullable = false)
    private Double latitude;

    @Column(name = "[LONGITUDE]", nullable = false)
    private Double longitude;

    @Column(name = "[OPENING_HOUR]", nullable = false)
    private Time openingHour;

    @Column(name = "[CLOSING_HOUR]", nullable = false)
    private Time closingHour;

    @ColumnDefault("true")
    @Column(name = "[IS_AVAILABLE]")
    private Boolean isAvailable;

    @Column(name = "[LAST_UPDATED]")
    private Timestamp lastUpdated;

    @Version
    private Long version;

我的 Jpa 存储库界面

@Repository
public interface ParkingLotRepository extends JpaRepository<ParkingLotEntity, Long> 

    @Lock(LockModeType.OPTIMISTIC)
    @Query("SELECT P " +
            "FROM ParkingLotEntity P " +
            "WHERE P.latitude BETWEEN ?3 AND ?1 " +
            "AND P.longitude BETWEEN ?4 AND ?2 " +
            "AND FUNCTION('CONVERT', TIME, FUNCTION('CURRENT_TIME')) BETWEEN P.openingHour AND P.closingHour " +
            "AND P.isAvailable = TRUE")
    List<ParkingLotEntity> getAllParkingLotCurrentlyWorkingInRegion(
            double top,     // north limit
            double right,   // east limit
            double bottom,  // south limit
            double left);   // west limit


    @Lock(LockModeType.OPTIMISTIC)
    @Query("SELECT P " +
            "FROM ParkingLotEntity P " +
            "WHERE FUNCTION('dbo.GET_DISTANCE_IN_KILOMETRE', ?1, ?2, P.latitude, P.longitude) <= ?3 " +
            "AND FUNCTION('CONVERT', TIME, FUNCTION('CURRENT_TIME')) BETWEEN P.openingHour AND P.closingHour " +
            "AND P.isAvailable = TRUE")
    List<ParkingLotEntity> getAllParkingLotCurrentlyWorkingInRegionOfRadius(
            double latitude,            // target location's latitude
            double longitude,           // target location's longitude
            short radiusInKilometre);   // radius in kilometre to scan

上面的 2 种方法运行良好,但问题是我还需要从结果集到第 2 种方法“getAllParkingLotCurrentlyWorkingInRegionOfRadius”位置的任何停车场之间的距离!并且距离已经由我在 SQL Server 中的定义函数计算出来了。方法2的结果集是成功的,但没有返回Distance(这是我的需要),只是一组parkingLotEntity而已!

我尝试在 SQL Server 中编写本机查询。我的方法是使用子查询和连接,如下代码:

SELECT P.*, DISTANCE
FROM PARKING_LOT P INNER JOIN (
    SELECT PL.ID, dbo.GET_DISTANCE_IN_KILOMETRE(10.784122211291663, 106.71152489259839, PL.LATITUDE, PL.LONGITUDE) AS DISTANCE
    FROM PARKING_LOT PL) AS PL 
ON P.ID = PL.ID AND DISTANCE <= 10
AND CONVERT(TIME, GETDATE()) BETWEEN P.OPENING_HOUR AND P.CLOSING_HOUR 

这是我在 SQL Server 中的定义函数:

CREATE FUNCTION [dbo].[GET_DISTANCE_IN_KILOMETRE](
    @LAT1 FLOAT, /* POINT1.LATITUDE */
    @LNG1 FLOAT, /* POINT1.LONGITUDE */
    @LAT2 FLOAT, /* POINT2.LATITUDE */
    @LNG2 FLOAT) /* POINT2.LONGITUDE */
RETURNS SMALLINT
AS
BEGIN
    DECLARE @DEGTORAD FLOAT
    DECLARE @ANS FLOAT
    DECLARE @KILOMETRE FLOAT

    SET @DEGTORAD = 57.29577951
    SET @ANS = 0
    SET @KILOMETRE = 0

    IF @LAT1 IS NULL OR @LAT1 = 0 OR 
        @LNG1 IS NULL OR @LNG1 = 0 OR 
        @LAT2 IS NULL OR @LAT2 = 0 OR
        @LNG2 IS NULL OR @LNG2 = 0

        BEGIN
            RETURN @KILOMETRE
        END

    SET @ANS = SIN(@LAT1 / @DEGTORAD) * SIN(@LAT2 / @DEGTORAD) + COS(@LAT1 / @DEGTORAD) * COS(@LAT2 / @DEGTORAD) * COS(ABS(@LNG2 - @LNG1)/@DEGTORAD)
    SET @KILOMETRE = 6371 * ATAN(SQRT(1 - SQUARE(@ANS)) / @ANS)

    RETURN CEILING(@KILOMETRE)
END

此查询的示例结果:(除了最近加入的列距离,所有其他字段都属于 ParkingLotEntity)

ID  PARKING_LOT_TYPE_ID LATITUDE    LONGITUDE   OPENING_HOUR       CLOSING_HOUR     IS_AVAILABLE    LAST_UPDATED    VERSION DISTANCE
56  1                   10.798351   106.696676  00:00:00.0000000    20:30:00.0000000    1        2020-02-20 23:57:00    0   3
57  1                   10.73789    106.723666  00:00:00.0000000    23:00:00.0000000    1        2020-02-20 23:57:00    0   6
58  1                   10.740738   106.704265  00:00:00.0000000    21:30:00.0000000    1        2020-02-20 00:03:00    0   5
67  1                   10.771154   106.671152  00:00:00.0000000    22:00:00.0000000    1        2020-02-20 00:12:00    0   5

该查询在本机查询中运行良好,但我无法使用 JPQL 在我的 Spring Boot 程序上实现它。

我搜索了一些资源,它说 JPQL 不能在 FROM 子句中使用子查询。

另外,我尝试在 JPQL @Query 中使用本机查询,但似乎没有用!而且我不知道我是否返回了正确的数据类型。

请告诉我如何从我的 ParkingLotEntity 和结果集中的每一行中选择所有字段,通过上面的函数计算到特定坐标的距离,将“距离”列加入结果集中,然后返回通过使用 Hibernate/JPA/JPQL/... 查询到 spring 服务器?我非常需要返回服务器的距离。

【问题讨论】:

【参考方案1】:

在 jpql 中是不可能的。看看documentation。它说:

子查询可以用在 WHERE 或 HAVING 子句中。

您可以使用Query 并将native 参数设置为true 或其他解决方案,例如EntityManager#createNativeQuery

但要做到这一点,您必须提供映射。 ParkingLotEntity 没有 distance 字段,并且无法从查询中返回两个实体。您必须查询Object[]Tuple。请参阅here 了解更多信息。

【讨论】:

能否告诉我查询返回的数据类型? 我会看看这个!非常感谢您对我的支持! 将本机查询与元组一起使用是可行的!非常感谢您对我的支持!

以上是关于无法在 FROM 子句中使用子查询编写 JPQL 查询 - Spring Data Jpa - Hibernate的主要内容,如果未能解决你的问题,请参考以下文章

从子句中的jpa eclipselink子查询

如何在 django ORM 的 From 子句中编写子查询

在 JPA 的 SQL 查询中的 FROM 语句中包含子查询

我无法在 FROM 子句中创建包含子查询的视图

MySQL的子查询中FROM和EXISTS子句的使用教程

如何在 JPQL 中正确执行 LIKE 和 CASE 子句