MIFOSX-321: tidy up some of backend implementation for client pagination and sorting example.

This commit is contained in:
Keith Woodlock
2013-05-02 01:56:32 +01:00
parent c73ac95466
commit f107d185e8
6 changed files with 102 additions and 77 deletions
@@ -3,36 +3,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. * You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
package org.mifosplatform.portfolio.pagination; package org.mifosplatform.infrastructure.core.service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.google.gson.Gson;
public class Page<E> { public class Page<E> {
private int totalFilteredRecords; private final int totalFilteredRecords;
private List<E> pageItems = new ArrayList<E>(); private final List<E> pageItems;
public void setPageItems(List<E> pageItems) { public Page(final List<E> pageItems, final int totalFilteredRecords) {
this.pageItems = pageItems; this.pageItems = pageItems;
this.totalFilteredRecords = totalFilteredRecords;
} }
public int getTotalFilteredRecords() { public int getTotalFilteredRecords() {
return this.totalFilteredRecords; return this.totalFilteredRecords;
} }
public void setTotalFilteredRecords(int totalFilteredRecords) {
this.totalFilteredRecords = totalFilteredRecords;
}
public List<E> getPageItems() { public List<E> getPageItems() {
return pageItems; return pageItems;
} }
}
@Override
public String toString() {
return new Gson().toJson(this);
}
}
@@ -3,7 +3,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. * You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
package org.mifosplatform.portfolio.pagination; package org.mifosplatform.infrastructure.core.service;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
@@ -13,16 +15,11 @@ public class PaginationHelper<E> {
public Page<E> fetchPage(final JdbcTemplate jt, final String sqlCountRows, final String sqlFetchRows, final Object args[], public Page<E> fetchPage(final JdbcTemplate jt, final String sqlCountRows, final String sqlFetchRows, final Object args[],
final RowMapper<E> rowMapper) { final RowMapper<E> rowMapper) {
final Page<E> page = new Page<E>(); final List<E> items = jt.query(sqlFetchRows, args, rowMapper);
page.getPageItems().addAll(jt.query(sqlFetchRows, args, rowMapper));
// determine how many rows are available // determine how many rows are available
final int totalFilteredRecords = jt.queryForInt(sqlCountRows); final int totalFilteredRecords = jt.queryForInt(sqlCountRows);
page.setTotalFilteredRecords(totalFilteredRecords); return new Page<E>(items, totalFilteredRecords);
return page;
} }
} }
@@ -33,6 +33,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
@@ -40,7 +41,6 @@ import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionDat
import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -90,18 +90,19 @@ public class ClientsApiResource {
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllPaginatedAndSorted(@QueryParam("sqlSearch") final String sqlSearch, public String retrieveAll(@QueryParam("sqlSearch") final String sqlSearch, @QueryParam("officeId") final Long officeId,
@QueryParam("officeId") final Long officeId, @QueryParam("externalId") final String externalId, @QueryParam("externalId") final String externalId, @QueryParam("displayName") final String displayName,
@QueryParam("displayName") final String displayName, @QueryParam("firstName") final String firstname, @QueryParam("firstName") final String firstname, @QueryParam("lastName") final String lastname,
@QueryParam("lastName") final String lastname, @QueryParam("underHierarchy") final String hierarchy, @QueryParam("underHierarchy") final String hierarchy, @QueryParam("offset") final Integer offset,
@QueryParam("offset") final int offset, @QueryParam("limit") final int limit, @QueryParam("orderBy") final String orderBy, @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy,
@QueryParam("sortOrder") final String sortOrder) { @QueryParam("sortOrder") final String sortOrder) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME); context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname, final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
lastname, hierarchy); lastname, hierarchy, offset, limit, orderBy, sortOrder);
Page<ClientData> clientData = this.clientReadPlatformService.retrieveAllPaginatedAndSorted(searchParameters, offset, limit,
orderBy, sortOrder); final Page<ClientData> clientData = this.clientReadPlatformService.retrieveAll(searchParameters);
return this.toApiJsonSerializer.serialize(clientData); return this.toApiJsonSerializer.serialize(clientData);
} }
@@ -123,7 +124,7 @@ public class ClientsApiResource {
return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
} }
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@@ -7,19 +7,17 @@ package org.mifosplatform.portfolio.client.service;
import java.util.Collection; import java.util.Collection;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData;
import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.pagination.Page;
public interface ClientReadPlatformService { public interface ClientReadPlatformService {
ClientData retrieveTemplate(); ClientData retrieveTemplate();
Collection<ClientData> retrieveAll(SearchParameters searchParameters); Page<ClientData> retrieveAll(SearchParameters searchParameters);
Page<ClientData> retrieveAllPaginatedAndSorted(SearchParameters searchParameters, int offset, int limit, String sortBy, String sortOrder);
ClientData retrieveOne(Long clientId); ClientData retrieveOne(Long clientId);
@@ -16,6 +16,8 @@ import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.core.service.PaginationHelper;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.data.OfficeData;
@@ -29,8 +31,6 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.pagination.Page;
import org.mifosplatform.portfolio.pagination.PaginationHelper;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
@@ -46,6 +46,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private final OfficeReadPlatformService officeReadPlatformService; private final OfficeReadPlatformService officeReadPlatformService;
// data mappers // data mappers
private final PaginationHelper<ClientData> paginationHelper = new PaginationHelper<ClientData>();
private final ClientMapper clientMapper = new ClientMapper(); private final ClientMapper clientMapper = new ClientMapper();
private final ClientLookupMapper lookupMapper = new ClientLookupMapper(); private final ClientLookupMapper lookupMapper = new ClientLookupMapper();
private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper(); private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper();
@@ -72,49 +73,41 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
} }
@Override @Override
public Collection<ClientData> retrieveAll(final SearchParameters searchParameters) { public Page<ClientData> retrieveAll(final SearchParameters searchParameters) {
final AppUser currentUser = context.authenticatedUser(); final AppUser currentUser = context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy(); final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%"; final String hierarchySearchString = hierarchy + "%";
String sql = "select " + this.clientMapper.schema() + " where o.hierarchy like ?"; StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
sqlBuilder.append(this.clientMapper.schema());
sqlBuilder.append(" where o.hierarchy like ?");
final String extraCriteria = buildSqlStringFromClientCriteria(searchParameters); final String extraCriteria = buildSqlStringFromClientCriteria(searchParameters);
if (StringUtils.isNotBlank(extraCriteria)) sql += " and (" + extraCriteria + ")"; if (StringUtils.isNotBlank(extraCriteria)) {
sqlBuilder.append(" and (").append(extraCriteria).append(")");
}
sql += " order by c.display_name ASC, c.account_no ASC"; // DONT order by default - just use database natural ordering so doesnt
// have to scan entire database table.
// sql += " order by c.display_name ASC, c.account_no ASC";
if (searchParameters.isOrderByRequested()) {
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()).append(' ').append(searchParameters.getSortOrder());
}
return this.jdbcTemplate.query(sql, clientMapper, new Object[] { hierarchySearchString }); if (searchParameters.isLimited()) {
} sqlBuilder.append(" limit ").append(searchParameters.getLimit());
}
@Override if (searchParameters.isOffset()) {
public Page<ClientData> retrieveAllPaginatedAndSorted(final SearchParameters searchParameters, final int offset, final int limit, sqlBuilder.append(" offset ").append(searchParameters.getOffset());
final String orderby, final String sortOrder) { }
final AppUser currentUser = context.authenticatedUser(); final String sqlCountRows = "SELECT FOUND_ROWS()";
final String hierarchy = currentUser.getOffice().getHierarchy(); return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(),
final String hierarchySearchString = hierarchy + "%"; new Object[] { hierarchySearchString }, this.clientMapper);
String sql = "select SQL_CALC_FOUND_ROWS " + this.clientMapper.schema() + " where o.hierarchy like ?";
final String extraCriteria = buildSqlStringFromClientCriteria(searchParameters);
if (StringUtils.isNotBlank(extraCriteria)) sql += " and (" + extraCriteria + ")";
if (orderby != null && sortOrder != null) {
if (!orderby.equalsIgnoreCase("") && !sortOrder.equalsIgnoreCase("")) {
sql += " order by " + orderby + " " + sortOrder;
}
} else
sql += " order by c.display_name ASC, c.account_no ASC";
PaginationHelper<ClientData> ph = new PaginationHelper<ClientData>();
String sqlCountRows = "SELECT FOUND_ROWS()";
String sqlFetchRows = sql;
if (limit != 0) sqlFetchRows = sql + " LIMIT " + limit + " OFFSET " + offset;
return ph.fetchPage(jdbcTemplate, sqlCountRows, sqlFetchRows, new Object[] { hierarchySearchString }, clientMapper);
} }
private String buildSqlStringFromClientCriteria(final SearchParameters searchParameters) { private String buildSqlStringFromClientCriteria(final SearchParameters searchParameters) {
@@ -1,6 +1,8 @@
package org.mifosplatform.portfolio.group.service; package org.mifosplatform.portfolio.group.service;
public class SearchParameters { import org.apache.commons.lang.StringUtils;
public final class SearchParameters {
private final String sqlSearch; private final String sqlSearch;
private final Long officeId; private final Long officeId;
@@ -9,19 +11,32 @@ public class SearchParameters {
private final String hierarchy; private final String hierarchy;
private final String firstname; private final String firstname;
private final String lastname; private final String lastname;
private final Integer offset;
private final Integer limit;
private final String orderBy;
private final String sortOrder;
public static SearchParameters from(final String sqlSearch, final Long officeId, final String externalId, final String name, public static SearchParameters from(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy) { final String hierarchy) {
return new SearchParameters(sqlSearch, officeId, externalId, name, hierarchy, null, null); return new SearchParameters(sqlSearch, officeId, externalId, name, hierarchy, null, null, null, null, null, null);
} }
public static SearchParameters forClients(final String sqlSearch, final Long officeId, final String externalId, public static SearchParameters forClients(final String sqlSearch, final Long officeId, final String externalId,
final String displayName, final String firstname, final String lastname, final String hierarchy) { final String displayName, final String firstname, final String lastname, final String hierarchy, final Integer offset,
return new SearchParameters(sqlSearch, officeId, externalId, displayName, hierarchy, firstname, lastname); final Integer limit, final String orderBy, final String sortOrder) {
Integer maxLimitAllowed = 200;
if (limit != null && limit < maxLimitAllowed && limit > 0) {
maxLimitAllowed = limit;
}
return new SearchParameters(sqlSearch, officeId, externalId, displayName, hierarchy, firstname, lastname, offset, maxLimitAllowed,
orderBy, sortOrder);
} }
private SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name, private SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy, final String firstname, final String lastname) { final String hierarchy, final String firstname, final String lastname, final Integer offset, final Integer limit,
final String orderBy, final String sortOrder) {
this.sqlSearch = sqlSearch; this.sqlSearch = sqlSearch;
this.officeId = officeId; this.officeId = officeId;
this.externalId = externalId; this.externalId = externalId;
@@ -29,6 +44,14 @@ public class SearchParameters {
this.hierarchy = hierarchy; this.hierarchy = hierarchy;
this.firstname = firstname; this.firstname = firstname;
this.lastname = lastname; this.lastname = lastname;
this.offset = offset;
this.limit = limit;
this.orderBy = orderBy;
this.sortOrder = sortOrder;
}
public boolean isOrderByRequested() {
return StringUtils.isNotBlank(this.orderBy) && StringUtils.isNotBlank(this.sortOrder);
} }
public String getSqlSearch() { public String getSqlSearch() {
@@ -58,4 +81,28 @@ public class SearchParameters {
public String getLastname() { public String getLastname() {
return this.lastname; return this.lastname;
} }
public Integer getOffset() {
return this.offset;
}
public Integer getLimit() {
return this.limit;
}
public String getOrderBy() {
return this.orderBy;
}
public String getSortOrder() {
return this.sortOrder;
}
public boolean isLimited() {
return this.limit != null && this.limit.intValue() > 0;
}
public boolean isOffset() {
return this.offset != null;
}
} }