无法使用 FROM 子句中的子查询编写 JPQL 查询 - Spring Data Jpa - Hibernate
Cannot writing JPQL query with subquery in FROM clause - Spring Data Jpa - Hibernate
我的 ParkingLotEntity:
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 服务器中的定义函数计算出来了。方法 2 的结果集是成功的,但没有 Distance returned back(这是我的需要),只有一组 parkingLotEntity 而已!
我尝试在 SQL 服务器中编写本机查询。我的方法是使用子查询和连接,如下代码:
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 服务器中定义的函数:
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
本次查询示例结果:(除distance recently joined外,其他字段均属于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 引导程序上实现它。
我搜索了一些资源,它说 JPQL 不能在 FROM 子句中使用子查询。
另外,我尝试在JPQL @Query 中使用原生查询,但似乎行不通!而且我不知道我是否 return 正确的数据类型。
请告诉我如何 select 我的 ParkingLotEntity 中的所有字段和结果集中的每一行,通过上面的函数计算到特定坐标的距离,加入 'distance' 列到结果集,然后使用 Hibernate/JPA/JPQL/... 查询 return 到 spring 服务器?我非常需要 return 到服务器的距离。
在 jpql 中是不可能的。看看documentation。它说:
Subqueries may be used in the WHERE or HAVING clause.
您可以使用 Query
并将 native
参数设置为 true
或其他解决方案,例如 EntityManager#createNativeQuery
。
但要做到这一点,您必须提供映射。 ParkingLotEntity
没有 distance
字段,不可能从查询中返回两个实体。您必须查询 Object[]
或 Tuple
。有关详细信息,请参阅 here。
我的 ParkingLotEntity:
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 服务器中的定义函数计算出来了。方法 2 的结果集是成功的,但没有 Distance returned back(这是我的需要),只有一组 parkingLotEntity 而已!
我尝试在 SQL 服务器中编写本机查询。我的方法是使用子查询和连接,如下代码:
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 服务器中定义的函数:
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
本次查询示例结果:(除distance recently joined外,其他字段均属于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 引导程序上实现它。
我搜索了一些资源,它说 JPQL 不能在 FROM 子句中使用子查询。
另外,我尝试在JPQL @Query 中使用原生查询,但似乎行不通!而且我不知道我是否 return 正确的数据类型。
请告诉我如何 select 我的 ParkingLotEntity 中的所有字段和结果集中的每一行,通过上面的函数计算到特定坐标的距离,加入 'distance' 列到结果集,然后使用 Hibernate/JPA/JPQL/... 查询 return 到 spring 服务器?我非常需要 return 到服务器的距离。
在 jpql 中是不可能的。看看documentation。它说:
Subqueries may be used in the WHERE or HAVING clause.
您可以使用 Query
并将 native
参数设置为 true
或其他解决方案,例如 EntityManager#createNativeQuery
。
但要做到这一点,您必须提供映射。 ParkingLotEntity
没有 distance
字段,不可能从查询中返回两个实体。您必须查询 Object[]
或 Tuple
。有关详细信息,请参阅 here。