Используйте только один SQL-запрос, чтобы получить результат в страницах - PullRequest
0 голосов
/ 10 октября 2018

Я хочу разбить ответ XML на страницы, потому что у меня слишком много элементов XML для отправки обратно.Я пробовал это:

XML-запрос:

<?xml version="1.0" encoding="UTF-8"?>
<reconcile>
  <start_date>2018-04-08T11:02:44</start_date>
  <end_date>2019-10-08T11:02:44</end_date>
  <page>1</page>
</reconcile>

JAXB:

@XmlRootElement(name = "reconcile")
@XmlAccessorType(XmlAccessType.FIELD)
public class Reconcile {

    @XmlElement(name = "start_date")
    @XmlJavaTypeAdapter(LocalDateTimeXmlAdapter.class)
    private LocalDateTime start_date;
    @XmlElement(name = "end_date")
    @XmlJavaTypeAdapter(LocalDateTimeXmlAdapter.class)
    private LocalDateTime end_date;
    @XmlElement(name = "page")
    private String page;
    ...../// getters and setters
}

SQL-запрос:

public List<PaymentTransactions> transactionsByDate(LocalDateTime start_date, LocalDateTime end_date, Merchants merchant, Terminals terminal) throws Exception {

        String hql = "select e from " + PaymentTransactions.class.getName() + " e where e.created_at >= ? and e.created_at <= ?";
        Query query = entityManager.createQuery(hql).setParameter(0, start_date).setParameter(1, end_date);
        List<PaymentTransactions> paymentTransactions = (List<PaymentTransactions>) query.getResultList();
        return paymentTransactions;
}

Возвращаемый XML:

 List<PaymentTransactions> paymentTransactions = transactionsService
                    .transactionsByDate(reconcile.getStart_date(), reconcile.getEnd_date(), merchant, terminal);

            ReconcilePaymentResponses pr = new ReconcilePaymentResponses();
            pr.setPage("1");
            pr.setPages_count("10");
            pr.setPer_page("4");
            pr.setTotal_count(String.valueOf(paymentTransactions.size()));

            for (int e = 0; e < paymentTransactions.size(); e++) {
                PaymentTransactions pt = paymentTransactions.get(e);

                ReconcilePaymentResponse obj = new ReconcilePaymentResponse();
                obj.setTransaction_type(pt.getType());
                pr.getPaymentResponse().add(obj);
            }

            return pr;

Ответ XML:

<?xml version='1.0' encoding='UTF-8'?>
<payment_responses page="1" per_page="4" total_count="5" pages_count="10">
    <payment_response>
        <transaction_type>Type</transaction_type>
    </payment_response>
    <payment_response>
        <transaction_type>Type</transaction_type>
    </payment_response>
    <payment_response>
        <transaction_type>Type</transaction_type>
    </payment_response>
    .........
</payment_responses>

Я бы хотел как-то разбить <payment_response>....</payment_response> на страницы, чтобы уменьшить накладные расходы памяти.Например, когда я отправляю 1, я хотел бы вернуть первые 10.

В текущем предложении используются 2 запроса SQL.Я хотел бы использовать только один SQL-запрос:

Я создал новый класс PageInfo для хранения информации о подкачке.Добавлен запрос для получения общего количества строк и установки моей page_info.Затем ограничил количество результатов по вашему запросу.Наконец, установите значения ReconcilePaymentResponse.

Class PageInfo {
    int current_page;
    int page_count;
    int per_page;
    int total_page;

    //constructor
    public PageInfo(int current_page, int page_count, int per_page) {
        //assign them
    }
    //getters
    //setters
}

Запрос SQL:

public List<PaymentTransactions> transactionsByDate(LocalDateTime start_date, LocalDateTime end_date, Merchants merchant, Terminals terminal,
    PageInfo pageInfo) throws Exception {

    //figure out number of total rows
    String count_hql = "select count(*) from " + PaymentTransactions.class.getName() + " e where e.created_at >= ? and e.created_at <= ?";
    Query count_query = entityManager.createQuery(count_hql);
    int count = countQuery.uniqueResult();

    //figure out total pages
    int total_page = (int)Math.ceil(count/(double)pageInfo.getPerPage());
    pageInfo.setTotal_Page(total_page);

    String hql = "select e from " + PaymentTransactions.class.getName() + " e where e.created_at >= ? and e.created_at <= ?";
    Query query = entityManager.createQuery(hql)
        //set starting point
        .setFirstResult((pageInfo.getCurrentPage()-1) * pageInfo.getPerPage)
        //set max rows to return
        .setMaxResults(pageInfo.getPerPage)
        .setParameter(0, start_date).setParameter(1, end_date);
    List<PaymentTransactions> paymentTransactions = (List<PaymentTransactions>) query.getResultList();
    return paymentTransactions;
}

Возвращаемый XML:

        //initialize PageInfo with desired values
        PageInfo page_info = new PageInfo(1,10,4);
        List<PaymentTransactions> paymentTransactions = transactionsService
            .transactionsByDate(reconcile.getStart_date(), reconcile.getEnd_date(), merchant, terminal, page_info);  // pass in page_info

        ReconcilePaymentResponses pr = new ReconcilePaymentResponses();
        pr.setPage(page_info.getCurrentPage());
        pr.setPages_count(page_info.getPageCount());
        pr.setPer_page(page_info.getPerPage());
        pr.setTotal_count(String.valueOf(paymentTransactions.size()));

        for (int e = 0; e < paymentTransactions.size(); e++) {
            PaymentTransactions pt = paymentTransactions.get(e);

            ReconcilePaymentResponse obj = new ReconcilePaymentResponse();
            obj.setTransaction_type(pt.getType());
            pr.getPaymentResponse().add(obj);
        }

        return pr;

Как использовать только один запрос SQL?

Ответы [ 3 ]

0 голосов
/ 14 октября 2018

В Hibernate есть еще один вариант сделать это с одним запросом, но он не является частью JPA.

ScrollableResults работает против открытого JDBC ResultSet за кулисами, который затем можно перейти к последней строке.

Например, начиная с запроса JPA:

    ScrollableResults scrollableResults = jpaQuery.unwrap(org.hibernate.query.Query.class).scroll();
    scrollableResults.scroll(offset);
    // Extract rows ...
    while (scrollableResults.next()) {
        PaymentTransactions paymentTransactions = (PaymentTransactions) scrollableResults.get(0);
        // ... process and check count
    }
    // Find the last row
    scrollableResults.last();
    int totalCount = scrollableResults.getRowNumber() + 1;

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

0 голосов
/ 19 октября 2018

Быстрая опция:

Вы можете использовать COUNT (*) OVER () в качестве последнего столбца в вашем наборе результатов, он будет хранить общее количество строкэто было бы возвращено, если бы не было предела:

SELECT col1, col2, col3, COUNT(*) OVER() as Total
FROM yourTable 
LIMIT 0,10;

Возможно, это не самое элегантное решение, поскольку вы повторяете это число во всех строках, но оно решает вашу проблему и должно быть довольно производительным.

Элегантная опция:

использовать хранимую процедуру с выходным параметром - процедура будет содержать 2 запроса, но у вас будет один элегантный вызов хранимой процедуры.

0 голосов
/ 10 октября 2018

Если вам нужен общий счетчик (для подсчета общего количества страниц), тогда вам нужно два запроса, в hibernate нет никакого способа обойти это (я говорю это, потому что вы могли бы (хотя и не уверены) построить противный SQLзапрос, где вы объединяете запрос подсчета с запросом выбора, но это действительно не стоит, и я не буду делать это даже для оптимизации).

см .: https://www.baeldung.com/hibernate-pagination

, что говоритсяесли у вас есть опция, то вы можете переключиться, чтобы не иметь определенного количества страниц (ваш пользовательский интерфейс может быть виртуальной прокруткой или просто [Предыдущий / Следующий] без окончания.

...