org.dbflute.cbean.paging.PagingInvoker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dbflute-runtime Show documentation
Show all versions of dbflute-runtime Show documentation
The runtime library of DBFlute
/*
* Copyright 2014-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.dbflute.cbean.paging;
import java.util.List;
import org.dbflute.cbean.ConditionBean;
import org.dbflute.cbean.result.PagingResultBean;
import org.dbflute.cbean.result.ResultBeanBuilder;
import org.dbflute.exception.DangerousResultSizeException;
import org.dbflute.exception.PagingOverSafetySizeException;
import org.dbflute.exception.PagingStatusInvalidException;
import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.jdbc.ManualThreadDataSourceHandler;
import org.dbflute.system.DBFluteSystem;
/**
* The invoker of paging.
* @param The type of entity.
* @author jflute
*/
public class PagingInvoker {
// ===================================================================================
// Attribute
// =========
protected final String _tableDbName;
// ===================================================================================
// Constructor
// ===========
public PagingInvoker(String tableDbName) {
_tableDbName = tableDbName;
}
// ===================================================================================
// Invoke
// ======
/**
* Invoke select-page by handler.
* @param handler The handler of paging. (NotNull)
* @return The result bean of paging. (NotNull)
* @throws org.dbflute.exception.PagingStatusInvalidException When the paging status is invalid.
* @throws org.dbflute.exception.PagingOverSafetySizeException When the paging is over safety size.
*/
public PagingResultBean invokePaging(PagingHandler handler) {
assertObjectNotNull("handler", handler);
final PagingBean pagingBean = handler.getPagingBean();
assertObjectNotNull("handler.getPagingBean()", pagingBean);
if (!pagingBean.isFetchScopeEffective()) {
throwPagingStatusInvalidException(pagingBean);
}
final ResultBeanBuilder builder = createResultBeanBuilder();
final boolean useManualThreadDataSource = isUseManualThreadDataSource();
if (useManualThreadDataSource) {
ManualThreadDataSourceHandler.prepareDataSourceHandler();
}
try {
final InvocationResultResource resource = doPaging(handler, pagingBean, builder);
final int allRecordCount = resource.getAllRecordCount();
final List selectedList = resource.getSelectedList();
final PagingResultBean rb = builder.buildPagingOfPaging(pagingBean, allRecordCount, selectedList);
if (pagingBean.canPagingReSelect() && isNecessaryToReadPageAgain(rb)) {
return reselect(handler, pagingBean, builder, rb);
} else {
return rb;
}
} finally {
pagingBean.xsetPaging(true); // restore its paging state finally
if (useManualThreadDataSource) {
ManualThreadDataSourceHandler.closeDataSourceHandler();
}
}
}
protected InvocationResultResource doPaging(PagingHandler handler, PagingBean pagingBean,
ResultBeanBuilder builder) {
final int safetyMaxResultSize = pagingBean.getSafetyMaxResultSize();
final int allRecordCount;
final List selectedList;
if (pagingBean.canPagingCountLater()) { // faster when last page selected (contains zero record)
selectedList = executePaging(handler);
if (isCurrentLastPage(selectedList, pagingBean)) {
allRecordCount = deriveAllRecordCountByLastPage(selectedList, pagingBean);
} else {
allRecordCount = executeCount(handler); // count later
}
checkSafetyResultIfNeeds(safetyMaxResultSize, allRecordCount);
} else { // faster when zero record selected
// basically main here because it has been used for a long time
allRecordCount = executeCount(handler);
checkSafetyResultIfNeeds(safetyMaxResultSize, allRecordCount);
if (allRecordCount == 0) {
selectedList = builder.buildEmptyListOfPaging(pagingBean);
} else {
selectedList = executePaging(handler);
}
}
final InvocationResultResource resource = new InvocationResultResource();
resource.setAllRecordCount(allRecordCount);
resource.setSelectedList(selectedList);
return resource;
}
protected static class InvocationResultResource {
protected int _allRecordCount;
protected List _selectedList;
public int getAllRecordCount() {
return _allRecordCount;
}
public void setAllRecordCount(int allRecordCount) {
_allRecordCount = allRecordCount;
}
public List getSelectedList() {
return _selectedList;
}
public void setSelectedList(List selectedList) {
_selectedList = selectedList;
}
}
/**
* Create the builder of result bean.
* @return The builder of result bean. (NotNull)
*/
protected ResultBeanBuilder createResultBeanBuilder() {
return new ResultBeanBuilder(_tableDbName);
}
protected int executeCount(PagingHandler handler) {
return handler.count();
}
protected List executePaging(PagingHandler handler) {
return handler.paging();
}
protected PagingResultBean reselect(PagingHandler handler, PagingBean pagingBean, ResultBeanBuilder builder,
PagingResultBean rb) {
pagingBean.xfetchPage(rb.getAllPageCount());
final InvocationResultResource resource = doPaging(handler, pagingBean, builder);
final int allRecordCount = resource.getAllRecordCount();
final List selectedList = resource.getSelectedList();
return builder.buildPagingOfPaging(pagingBean, allRecordCount, selectedList);
}
/**
* Is the current page is last page?
* @param selectedList The selected list. (NotNull)
* @param pagingBean The bean of paging. (NotNull)
* @return The determination, true or false.
*/
protected boolean isCurrentLastPage(List selectedList, PagingBean pagingBean) {
if (selectedList.size() == 0 && pagingBean.getFetchPageNumber() > 1) {
return false; // because of unknown all record count (re-selected later)
}
// /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// It returns true if the size of list is under fetch size(page size).
// (contains the size is zero and first page is target)
//
// {For example}
// If the fetch size is 20 and the size of selected list is 19 or less,
// the current page must be last page(contains when only one page exists).
// it is NOT necessary to read count because the 19 is the hint to derive all record count.
//
// If the fetch size is 20 and the size of selected list is 20,
// it is necessary to read count because we cannot know whether the next pages exist or not.
// - - - - - - - - - -/
return selectedList.size() <= (pagingBean.getFetchSize() - 1);
}
/**
* Derive all record count by last page.
* @param selectedList The selected list. (NotNull)
* @param pagingBean The bean of paging. (NotNull)
* @return Derived all record count.
*/
protected int deriveAllRecordCountByLastPage(List selectedList, PagingBean pagingBean) {
int baseSize = (pagingBean.getFetchPageNumber() - 1) * pagingBean.getFetchSize();
return baseSize + selectedList.size();
}
/**
* Is it necessary to read page again?
* @param rb The result bean of paging. (NotNull)
* @return The determination, true or false.
*/
protected boolean isNecessaryToReadPageAgain(PagingResultBean rb) {
return rb.getAllRecordCount() > 0 && rb.getSelectedList().isEmpty();
}
/**
* Check whether the count of all records is safety or not if it needs.
* @param safetyMaxResultSize The max size of safety result.
* @param allRecordCount The count of all records.
* @throws DangerousResultSizeException When the count of all records is dangerous.
*/
protected void checkSafetyResultIfNeeds(int safetyMaxResultSize, int allRecordCount) {
if (safetyMaxResultSize > 0 && allRecordCount > safetyMaxResultSize) {
throwPagingOverSafetySizeException(safetyMaxResultSize, allRecordCount);
}
}
protected boolean isUseManualThreadDataSource() {
return true; // basically for MySQL's found_rows() when no transaction
}
protected void throwPagingStatusInvalidException(PagingBean pagingBean) {
final boolean cbean = pagingBean instanceof ConditionBean;
final String name = cbean ? "condition-bean" : "parameter-bean";
final ExceptionMessageBuilder br = new ExceptionMessageBuilder();
br.addNotice("The status of paging was INVALID. (paging parameters was not found)");
br.addItem("Advice");
br.addElement("Confirm your logic for paging of " + name + ".");
br.addElement("Paging execution needs paging parameters 'pageSize' and 'pageNumber'.");
br.addElement("For example:");
br.addElement(" (x):");
if (cbean) {
br.addElement(" MemberCB cb = new MemberCB();");
br.addElement(" cb.query().set...;");
br.addElement(" ... = memberBhv.selectPage(cb);");
} else {
br.addElement(" SimpleMemberPmb pmb = new SimpleMemberPmb();");
br.addElement(" pmb.set...;");
br.addElement(" ... = memberBhv.outsideSql().manualPaging().selectPage(...);");
}
br.addElement(" (o):");
if (cbean) {
br.addElement(" MemberCB cb = new MemberCB();");
br.addElement(" cb.query().set...;");
br.addElement(" cb.paging(20, 2); // *Point!");
br.addElement(" ... = memberBhv.selectPage(cb);");
} else {
br.addElement(" SimpleMemberPmb pmb = new SimpleMemberPmb();");
br.addElement(" pmb.set...;");
br.addElement(" pmb.paging(20, 2); // *Point!");
br.addElement(" ... = memberBhv.outsideSql().manualPaging().selectPage(...);");
}
br.addItem("PagingBean");
br.addElement(pagingBean);
final String msg = br.buildExceptionMessage();
throw new PagingStatusInvalidException(msg);
}
protected void throwPagingOverSafetySizeException(int safetyMaxResultSize, int allRecordCount) {
// here simple message because an entry method catches this
String msg = "The paging was over the specified safety size:";
msg = msg + " " + allRecordCount + " > " + safetyMaxResultSize;
throw new PagingOverSafetySizeException(msg, safetyMaxResultSize, allRecordCount);
}
// ===================================================================================
// General Helper
// ==============
protected String ln() {
return DBFluteSystem.ln();
}
// ===================================================================================
// Assert Helper
// =============
/**
* Assert that the object is not null.
* @param variableName The check name of variable for message. (NotNull)
* @param value The checked value. (NotNull)
* @throws IllegalArgumentException When the argument is null.
*/
protected void assertObjectNotNull(String variableName, Object value) {
if (variableName == null) {
String msg = "The value should not be null: variableName=null value=" + value;
throw new IllegalArgumentException(msg);
}
if (value == null) {
String msg = "The value should not be null: variableName=" + variableName;
throw new IllegalArgumentException(msg);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy