Невозможно написать JPQL-запрос с подзапросом в предложении FROM - Spring Data Jpa - Hibernate - PullRequest
2 голосов
/ 20 февраля 2020

My 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
}

Два вышеуказанных метода работают хорошо, но проблема в том, что мне также нужно расстояние между парковкой от результирующий набор к местоположению во втором методе "getAllParkingLotCurrentlyWorkingInRegionOfRadius"! и расстояние уже было рассчитано моей определяющей функцией на сервере SQL. Набор результатов из метода 2 является успешным, но без расстояния, возвращаемого назад (что мне нужно), только набор 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 Сервер:

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.

Я ищу какой-то ресурс, и он говорит, что JPQL не может использовать подзапрос в предложении FROM.

Также я попытался использовать собственный запрос в JPQL @Query, но, похоже, это не работает! и я не знаю, верну ли я правильный тип данных или нет.

Пожалуйста, дайте мне знать, как я могу выбрать все поля из моего ParkingLotEntity, и для каждой строки в наборе результатов вычислите расстояние до заданного значения c координируйте с помощью функции выше, присоедините столбец «расстояние» к результирующему набору, а затем вернитесь к серверу Spring с помощью запроса Hibernate / JPA / JPQL / ...? Мне очень нужно расстояние, чтобы вернуться на сервер.

1 Ответ

0 голосов
/ 20 февраля 2020

Это невозможно в jpql. Взгляните на документацию . В нем говорится:

Подзапросы могут использоваться в выражении WHERE или HAVING.

Вы можете использовать Query с параметром native, установленным на true, или другими решениями, такими как EntityManager#createNativeQuery.

Но чтобы сделать это, вы должны предоставить карту. ParkingLotEntity не имеет поля distance, и возврат двух сущностей из запроса невозможен. Вам нужно запросить Object[] или Tuple. См. здесь для получения дополнительной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...