Как выполнить подкачку запроса в JPA с помощью nativeQuery и Spring PagingAndSortingRepository - PullRequest
1 голос
/ 16 мая 2019

Несколько дней назад коллега задал этот вопрос: Порядок и группировка по одному столбцу

Это было решено. Простой и чистый запрос, очень круто. Решение, похоже, стоило того, за исключением того, что нам нужно реализовать его в JPA.

Поскольку JPA не принимает подзапросы в JOIN, нам пришлось делать это как nativeQuery, но при этом у нас возникают проблемы с подкачкой, поскольку JPA не объединяет это с нативными запросами. https://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/

Собственные запросы. Аннотация @Query позволяет выполнять собственные запросы. установив для флага nativeQuery значение true. Обратите внимание, что в настоящее время мы не поддержка выполнения нумерации страниц или динамической сортировки для собственных запросов так как нам пришлось бы манипулировать объявленным фактическим запросом, и мы не можем сделать это надежно для нативного SQL.

Мы не знаем, как продолжить это.

Что нам нужно сделать: у нас есть ряд записей с номером телефона, пользователем и датой, и каждый пользователь смог позвонить N раз. Нам нужно получить все записи, сгруппированные по номеру телефона и отсортированные (DESC) по самой последней дате каждой группы номеров.

например:.

С этими данными:

+--------------+---------------------+-------------+---------------+
| phone_number | registered          | name        | first_surname |
+--------------+---------------------+-------------+---------------+
|    222005001 | 2019-05-10 10:01:01 | Alvaro      | Garcia        |
|    222004001 | 2019-05-13 16:14:21 | David       | Garcia        |
|    111003001 | 2019-05-13 16:14:43 | Roberto     | Martin        |
|    111001000 | 2019-05-13 16:14:50 | Juan Manuel | Martin        |
|    111001000 | 2019-05-13 16:14:50 | Maria       | Alonso        |
|    111001000 | 2019-05-13 16:14:50 | Roberto     | Martin        |
|    333006001 | 2019-05-13 16:14:55 | Benito      | Lopera        |
|    123456789 | 2019-05-13 16:15:00 | NULL        | NULL          |
|    987654321 | 2019-05-13 16:15:08 | NULL        | NULL          |
|    123456789 | 2019-05-13 16:15:13 | NULL        | NULL          |
|    666999666 | 2019-05-13 16:15:18 | NULL        | NULL          |
|    454545458 | 2019-05-13 16:15:27 | NULL        | NULL          |
|    333006001 | 2019-05-13 16:23:36 | Benito      | Lopera        |
|    987654321 | 2019-05-13 16:23:46 | NULL        | NULL          |
|    666999666 | 2019-05-13 16:23:50 | NULL        | NULL          |
|    454545458 | 2019-05-13 16:23:55 | NULL        | NULL          |
|    666999666 | 2019-05-13 16:24:03 | NULL        | NULL          |
|    222004001 | 2019-05-13 16:24:10 | David       | Garcia        |
+--------------+---------------------+-------------+---------------+

Сортируйте их так:

+--------------+---------------------+-------------+---------------+
| phone_number | registered          | name        | first_surname |
+--------------+---------------------+-------------+---------------+
|    222004001 | 2019-05-13 16:24:10 | David       | Garcia        |
|    222004001 | 2019-05-13 16:14:21 | David       | Garcia        |
|    666999666 | 2019-05-13 16:24:03 | NULL        | NULL          |
|    666999666 | 2019-05-13 16:23:50 | NULL        | NULL          |
|    666999666 | 2019-05-13 16:15:18 | NULL        | NULL          |
|    454545458 | 2019-05-13 16:23:55 | NULL        | NULL          |
|    454545458 | 2019-05-13 16:15:27 | NULL        | NULL          |
|    987654321 | 2019-05-13 16:23:46 | NULL        | NULL          |
|    987654321 | 2019-05-13 16:15:08 | NULL        | NULL          |
|    333006001 | 2019-05-13 16:23:36 | Benito      | Lopera        |
|    333006001 | 2019-05-13 16:14:55 | Benito      | Lopera        |
|    123456789 | 2019-05-13 16:15:13 | NULL        | NULL          |
|    123456789 | 2019-05-13 16:15:00 | NULL        | NULL          |
|    111001000 | 2019-05-13 16:14:50 | Maria       | Alonso        |
|    111001000 | 2019-05-13 16:14:50 | Roberto     | Martin        |
|    111001000 | 2019-05-13 16:14:50 | Juan Manuel | Martin        |
|    111003001 | 2019-05-13 16:14:43 | Roberto     | Martin        |
|    222005001 | 2019-05-10 10:01:01 | Alvaro      | Garcia        |
+--------------+---------------------+-------------+---------------+

Это можно сделать с помощью этого запроса:

SELECT c.phone_number, c.registered, cl.name, cl.first_surname
FROM callers cl
    INNER JOIN callers_phones cp ON cl.caller_id = cp.caller_id
    RIGHT OUTER JOIN calls c ON c.phone_number = cp.phone_number
    JOIN (
        SELECT phone_number, MAX(registered) AS registered
        FROM calls
        GROUP BY phone_number) aux_c ON aux_c.phone_number = c.phone_number
WHERE c.answered = FALSE
    AND (null is null or null is null or c.registered between null and null)
    AND (null is null or c.phone_number = null)
    AND (null is null or cl.caller_id = null)
ORDER BY aux_c.registered DESC, c.registered DESC

Это таблицы:

CREATE TABLE callers
(
    caller_id int NOT NULL UNIQUE AUTO_INCREMENT,
    name varchar(50) NOT NULL,
    first_surname varchar(50) NOT NULL,
    CONSTRAINT callers_pkey PRIMARY KEY (caller_id)
);

CREATE TABLE callers_phones
(
    phone_id int NOT NULL UNIQUE AUTO_INCREMENT,
    caller_id int NOT NULL,
    phone_number int NOT NULL,
    CONSTRAINT callers_phones_pkey PRIMARY KEY (phone_id)
);

ALTER TABLE callers_phones
    ADD CONSTRAINT callers_phones_fkey_callers FOREIGN KEY (caller_id)
    REFERENCES callers (caller_id);

CREATE TABLE calls
(   
    call_id int NOT NULL UNIQUE AUTO_INCREMENT,
    phone_number int NOT NULL,
    answered boolean NOT NULL DEFAULT false,
    registered datetime NOT NULL,
    CONSTRAINT calls_pkey PRIMARY KEY (call_id)
);

Проблема в том, что мы должны реализовать его в JPA с подкачкой, но подзапросы не работают в предложении JOIN, а подкачка не работает с nativeQuery.

Вот что мы сделали:

@ Entity:

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.Id;
import javax.persistence.NamedNativeQuery;
import javax.persistence.SqlResultSetMapping;

@SqlResultSetMapping (name = "MissedCallResult",
        entities = {
                     @EntityResult (entityClass = MissedCallEntity.class,
                             fields = {
                                        @FieldResult (name = "callId", column = "id"),
                                        @FieldResult (name = "phoneNumber", column = "pH"),
                                        @FieldResult (name = "registered", column = "reg"),
                                        @FieldResult (name = "callerName", column = "cN"),
                                        @FieldResult (name = "callerFirstSurname", column = "cFS")
                             })
        })
@NamedNativeQuery (name = "findMissedCalls",
        query = "select c.call_id as id, c.phone_number as pH, c.registered as reg, cl.name as cN, cl.first_surname as cFS "
                + "from callers cl "
                + "    inner join callers_phones cp on cl.caller_id = cp.caller_id "
                + "    right outer join calls c on c.phone_number = cp.phone_number "
                + "    join (select c2.phone_number, MAX(c2.registered) as registered "
                + "        from calls c2 "
                + "        group by c2.phone_number) aux_c on aux_c.phone_number = c.phone_number "
                + "where c.answered = false "
                + "    and (:startDate is null or :endDate is null or c.registered between :startDate and :endDate) "
                + "    and (:callerId is null or cl.caller_id = :callerId) "
                + "    and (:phoneNumber is null or c.phone_number = :phoneNumber) "
                + "order by aux_c.registered desc, c.registered desc",
        resultSetMapping = "MissedCallResult")
@Entity
public class MissedCallEntity
{
    @Id
    private Integer callId;
    private Integer phoneNumber;
    private Date registered;
    private String callerName;
    private String callerFirstSurname;
    private String callerSecondSurname;

...

}

@ Repository:

import java.util.Date;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import es.panel.domain.MissedCallEntity;

@RepositoryRestResource (path = "missedCalls", collectionResourceRel = "missedCalls")
public interface MissedCallRepository extends PagingAndSortingRepository<MissedCallEntity, Integer>
{
    @Query (nativeQuery = true, name = "findMissedCalls")
    Page<MissedCallEntity> findMissedCalls(@Param ("startDate") Date startDate,
                                           @Param ("endDate") Date endDate,
                                           @Param ("callerId") Integer callerId,
                                           @Param ("phoneNumber") Integer phoneNumber,
                                           Pageable page);
}

В @Service:

public Page<MissedCallEntity> getMissedCalls(Date startDate,
                                                 Date endDate,
                                                 Integer callerId,
                                                 Integer phoneNumber,
                                                 int actualPage,
                                                 int limit)
    {
        Page<MissedCallEntity> calls = mcRepository.findMissedCalls(
                startDate, endDate, callerId, phoneNumber, PageRequest.of(1, 5));

        return calls;
    }

Заранее спасибо!

1 Ответ

0 голосов
/ 16 мая 2019

Очень простое решение - создать представление базы данных на основе вашего запроса для вычисленных значений, то есть счетчиков и максимальных значений и т. Д.

Вы можете сопоставить это с соответствующим объектом, используя аннотацию JPA @SecondaryTable, которая позволяет сопоставить объект более чем с 1 таблицей (или представлением).

С этим местом вы можете сортировать и фильтровать, используя стандартную функциональность данных JPA / Spring, как и для любого другого поля, и вы можете в значительной степени удалить весь написанный код.

Я бы уточнил подробнее, однако не очень понятно, чего вы пытаетесь достичь: вы спрашиваете о своем попытанном решении, а не о самой проблеме. MissedCall также не является сущностью. Объекты в вашей системе - это пользователи, звонки, телефоны и т. Д.

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