仅使用一个 SQL 查询来获取页面中的结果
Use only one SQL query to get result in pages
我想将 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;
}
Return 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 时,我想 return 前 10 个。
当前提案使用 2 SQL 个查询。我只想使用一个 SQL 查询:
我创建了一个新的 PageInfo class 来存储分页信息。添加了查询以获取总行数并设置我的 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;
}
Return 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 查询?
如果你想要总计数(计算总页数)那么你需要两个查询,在休眠中没有办法绕过它(我这么说是因为你可以(虽然不确定)构建一个令人讨厌的 SQL 查询,您将计数查询与 select 查询合并,但这真的不值得,即使为了优化我也不会这样做)。
参见:https://www.baeldung.com/hibernate-pagination
话虽这么说,如果您有这个选项,那么您可以切换到没有确定页数的页面(您的 UI 可以是虚拟滚动,也可以只是 [上一页/下一页] 而没有结束。
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;
请注意,这不一定比两个查询选项更有效 - 特别是对于大量结果。
快速选项:
您可以使用 COUNT(*) OVER() 作为结果集中的最后一列,它将存储如果没有限制将返回的总行数:
SELECT col1, col2, col3, COUNT(*) OVER() as Total
FROM yourTable
LIMIT 0,10;
这可能不是最优雅的解决方案,因为您会在所有行中重复这个数字,但它可以解决您的问题并且应该非常高效。
优雅选项:
使用带有输出参数的存储过程 - 该过程将包含 2 个查询,但您可以优雅地调用一个存储过程。
我想将 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;
}
Return 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 时,我想 return 前 10 个。
当前提案使用 2 SQL 个查询。我只想使用一个 SQL 查询:
我创建了一个新的 PageInfo class 来存储分页信息。添加了查询以获取总行数并设置我的 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;
}
Return 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 查询?
如果你想要总计数(计算总页数)那么你需要两个查询,在休眠中没有办法绕过它(我这么说是因为你可以(虽然不确定)构建一个令人讨厌的 SQL 查询,您将计数查询与 select 查询合并,但这真的不值得,即使为了优化我也不会这样做)。
参见:https://www.baeldung.com/hibernate-pagination
话虽这么说,如果您有这个选项,那么您可以切换到没有确定页数的页面(您的 UI 可以是虚拟滚动,也可以只是 [上一页/下一页] 而没有结束。
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;
请注意,这不一定比两个查询选项更有效 - 特别是对于大量结果。
快速选项:
您可以使用 COUNT(*) OVER() 作为结果集中的最后一列,它将存储如果没有限制将返回的总行数:
SELECT col1, col2, col3, COUNT(*) OVER() as Total
FROM yourTable
LIMIT 0,10;
这可能不是最优雅的解决方案,因为您会在所有行中重复这个数字,但它可以解决您的问题并且应该非常高效。
优雅选项:
使用带有输出参数的存储过程 - 该过程将包含 2 个查询,但您可以优雅地调用一个存储过程。