Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,35 @@
package org.apache.fineract.infrastructure.core.jpa;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class CriteriaQueryFactory {

public List<Order> fromPageable(Pageable pageable, CriteriaBuilder cb, Root<?> root) {
return fromPageable(pageable, cb, root, () -> null);
private final EntityManager entityManager;

public List<Order> ordersFromPageable(Pageable pageable, CriteriaBuilder cb, Root<?> root) {
return ordersFromPageable(pageable, cb, root, () -> null);
}

public List<Order> fromPageable(Pageable pageable, CriteriaBuilder cb, Root<?> root, Supplier<Order> defaultOrderSupplier) {
public List<Order> ordersFromPageable(Pageable pageable, CriteriaBuilder cb, Root<?> root, Supplier<Order> defaultOrderSupplier) {
List<Order> orders = new ArrayList<>();
Sort sort = pageable.getSort();
if (sort.isSorted()) {
Expand All @@ -54,4 +66,56 @@ public List<Order> fromPageable(Pageable pageable, CriteriaBuilder cb, Root<?> r
}
return orders;
}

public <S, U> Page<S> readPage(TypedQuery<S> query, Class<U> domainClass, Pageable pageable, Specification<U> spec) {

if (pageable.isPaged()) {
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
}

return PageableExecutionUtils.getPage(query.getResultList(), pageable, () -> executeCountQuery(getCountQuery(spec, domainClass)));
}

private static long executeCountQuery(TypedQuery<Long> query) {
List<Long> totals = query.getResultList();
long total = 0L;

for (Long element : totals) {
total += element == null ? 0 : element;
}
return total;
}

private <S> TypedQuery<Long> getCountQuery(Specification<S> spec, Class<S> domainClass) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);

Root<S> root = query.from(domainClass);

applySpecificationToCriteria(root, spec, query);

if (query.isDistinct()) {
query.select(builder.countDistinct(root));
} else {
query.select(builder.count(root));
}

// Remove all Orders the Specifications might have applied
query.orderBy(Collections.emptyList());

return entityManager.createQuery(query);
}

public <S, U> Root<U> applySpecificationToCriteria(Root<U> root, Specification<U> spec, CriteriaQuery<S> query) {

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
Predicate predicate = spec.toPredicate(root, query, builder);

if (predicate != null) {
query.where(predicate);
}

return root;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class SearchedClient {
private final Long id;
private final String displayName;
private final ExternalId externalId;
private final String accountNo;
private final String accountNumber;
private final Long officeId;
private final String officeName;
private final String mobileNo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
Expand All @@ -32,8 +33,8 @@
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.client.domain.Client;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;

@Repository
Expand All @@ -55,28 +56,33 @@ public Page<SearchedClient> searchByText(String searchText, Pageable pageable, S

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SearchedClient> query = cb.createQuery(SearchedClient.class);

Root<Client> root = query.from(Client.class);
Path<Office> office = root.get("office");

query.select(cb.construct(SearchedClient.class, root.get("id"), root.get("displayName"), root.get("externalId"),
root.get("accountNumber"), office.get("id"), office.get("name"), root.get("mobileNo"), root.get("status"),
root.get("activationDate"), root.get("createdDate")));
Specification<Client> spec = (r, q, builder) -> {
Path<Office> o = r.get("office");

List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.like(office.get("hierarchy"), hierarchyLikeValue));
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.like(o.get("hierarchy"), hierarchyLikeValue));

String searchLikeValue = "%" + searchText + "%";
predicates.add(cb.or(cb.like(root.get("accountNumber"), searchLikeValue), cb.like(root.get("displayName"), searchLikeValue),
cb.like(root.get("externalId"), searchLikeValue), cb.like(root.get("mobileNo"), searchLikeValue)));
String searchLikeValue = "%" + searchText + "%";
predicates.add(cb.or(cb.like(r.get("accountNumber"), searchLikeValue), cb.like(r.get("displayName"), searchLikeValue),
cb.like(r.get("externalId"), searchLikeValue), cb.like(r.get("mobileNo"), searchLikeValue)));

query.where(cb.and(predicates.toArray(new Predicate[0])));
return cb.and(predicates.toArray(new Predicate[0]));
};
criteriaQueryFactory.applySpecificationToCriteria(root, spec, query);

List<Order> orders = criteriaQueryFactory.fromPageable(pageable, cb, root, () -> cb.desc(root.get("id")));
List<Order> orders = criteriaQueryFactory.ordersFromPageable(pageable, cb, root, () -> cb.desc(root.get("id")));
query.orderBy(orders);

List<SearchedClient> result = entityManager.createQuery(query).setFirstResult(pageable.getPageNumber())
.setMaxResults(pageable.getPageSize()).getResultList();
query.select(cb.construct(SearchedClient.class, root.get("id"), root.get("displayName"), root.get("externalId"),
root.get("accountNumber"), office.get("id"), office.get("name"), root.get("mobileNo"), root.get("status"),
root.get("activationDate"), root.get("createdDate")));

TypedQuery<SearchedClient> queryToExecute = entityManager.createQuery(query);

return new PageImpl<>(result, pageable, result.size());
return criteriaQueryFactory.readPage(queryToExecute, Client.class, pageable, spec);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ClientSearchData {
private Long id;
private String displayName;
private ExternalId externalId;
private String accountNo;
private String accountNumber;
private Long officeId;
private String officeName;
private String mobileNo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ public void setup() {
clientHelper = new ClientHelper(requestSpec, responseSpec);
}

@Test
public void testClientSearchWorks_WithLastnameText_WithPaging() {
// given
String lastname = Utils.randomStringGenerator("Client_LastName_", 5);
PostClientsRequest request1 = ClientHelper.defaultClientCreationRequest();
request1.setLastname(lastname);
clientHelper.createClient(request1);

PostClientsRequest request2 = ClientHelper.defaultClientCreationRequest();
request2.setLastname(lastname);
clientHelper.createClient(request2);

PostClientsRequest request3 = ClientHelper.defaultClientCreationRequest();
request3.setLastname(lastname);
clientHelper.createClient(request3);
// when
PageClientSearchData result = clientHelper.searchClients(lastname, 0, 1);
// then
assertThat(result.getTotalElements()).isEqualTo(3);
assertThat(result.getNumberOfElements()).isEqualTo(1);
assertThat(result.getTotalPages()).isEqualTo(3);
}

@Test
public void testClientSearchWorks_WithLastnameTextOnDefaultOrdering() {
// given
Expand Down Expand Up @@ -135,7 +158,7 @@ public void testClientSearchWorks_ByAccountNumber() {
PageClientSearchData result = clientHelper.searchClients(client2Data.getAccountNo());
// then
assertThat(result.getTotalElements()).isEqualTo(1);
assertThat(result.getContent().get(0).getAccountNo()).isEqualTo(client2Data.getAccountNo());
assertThat(result.getContent().get(0).getAccountNumber()).isEqualTo(client2Data.getAccountNo());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ public PageClientSearchData searchClients(String text) {
return searchClients(request);
}

public PageClientSearchData searchClients(String text, int page, int pageSize) {
ClientTextSearch clientTextSearch = new ClientTextSearch();
clientTextSearch.setText(text);
PagedRequestClientTextSearch request = new PagedRequestClientTextSearch();
request.setRequest(clientTextSearch);
request.setPage(page);
request.setSize(pageSize);
return searchClients(request);
}

public PageClientSearchData searchClients(String text, SortOrder sortOrder) {
ClientTextSearch clientTextSearch = new ClientTextSearch();
clientTextSearch.setText(text);
Expand Down