Skip to content

Commit

Permalink
fix: fix integration with Spring Boot 3
Browse files Browse the repository at this point in the history
The library creates the following queries:

- 1 count query (unfiltered, to compute the "recordsTotal" field)
- 1 data query (filtered)
- 1 optional count query (filtered), if the data query returns a complete slice, in order to compute the "recordsFiltered" field

Before this commit, when the optional count query ran,
DataTablesSpecification.toPredicate() was called a second time and
reused the globalPredicates array, triggering one of the following
error:

> java.lang.IllegalArgumentException: Already registered a copy

> jakarta.persistence.PersistenceException: Converting org.hibernate.sql.ast.SqlTreeCreationException to JPA PersistenceException : Could not locate TableGroup

Related: #150
  • Loading branch information
darrachequesne committed Feb 12, 2023
1 parent e53eb8c commit a6a8a0d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,35 @@ public Specification<T> build() {
}

private class DataTablesSpecification<S> implements Specification<S> {
protected List<Predicate> columnPredicates = new ArrayList<>();
protected List<Predicate> globalPredicates = new ArrayList<>();

@Override
public Predicate toPredicate(@NonNull Root<S> root, @NonNull CriteriaQuery<?> query, @NonNull CriteriaBuilder criteriaBuilder) {
initPredicatesRecursively(query, tree, root, root, criteriaBuilder);
Predicates predicates = new Predicates();
initPredicatesRecursively(predicates, query, tree, root, root, criteriaBuilder);

if (input.getSearchPanes() != null) {
input.getSearchPanes().forEach((attribute, values) -> {
if (!values.isEmpty()) {
columnPredicates.add(root.get(attribute).in(values));
predicates.columns.add(root.get(attribute).in(values));
}
});
}

final Predicate predicate = createFinalPredicate(criteriaBuilder);
columnPredicates.clear();
return predicate;
return predicates.toPredicate(criteriaBuilder);
}

private boolean isCountQuery(CriteriaQuery<?> query) {
return query.getResultType() == Long.class;
}

protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> node, From<S, S> from, FetchParent<S, S> fetch, CriteriaBuilder criteriaBuilder) {
protected void initPredicatesRecursively(Predicates predicates, CriteriaQuery<?> query, Node<Filter> node, From<S, S> from, FetchParent<S, S> fetch, CriteriaBuilder criteriaBuilder) {
if (node.isLeaf()) {
boolean hasColumnFilter = node.getData() != null;
if (hasColumnFilter) {
Filter columnFilter = node.getData();
columnPredicates.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
predicates.columns.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
} else if (hasGlobalFilter) {
Filter globalFilter = tree.getData();
globalPredicates.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
predicates.global.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
}
}
for (Node<Filter> child : node.getChildren()) {
Expand All @@ -62,44 +58,34 @@ protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> no
continue;
}
if (child.isLeaf()) {
initPredicatesRecursively(query, child, from, fetch, criteriaBuilder);
initPredicatesRecursively(predicates, query, child, from, fetch, criteriaBuilder);
} else {
Join<S, S> join = from.join(child.getName(), JoinType.LEFT);

if (isCountQuery(query)) {
initPredicatesRecursively(query, child, join, join, criteriaBuilder);
initPredicatesRecursively(predicates, query, child, join, join, criteriaBuilder);
} else {
Fetch<S, S> childFetch = fetch.fetch(child.getName(), JoinType.LEFT);
initPredicatesRecursively(query, child, join, childFetch, criteriaBuilder);
initPredicatesRecursively(predicates, query, child, join, childFetch, criteriaBuilder);
}
}
}
}

protected Predicate createFinalPredicate(CriteriaBuilder criteriaBuilder) {
List<Predicate> allPredicates = new ArrayList<>(columnPredicates);

if (!globalPredicates.isEmpty()) {
allPredicates.add(criteriaBuilder.or(globalPredicates.toArray(new Predicate[0])));
}

return allPredicates.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(allPredicates.toArray(new Predicate[0]));
}
}

private class DataTablesSearchPaneSpecification<S> extends DataTablesSpecification<S> {

@Override
protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> node, From<S, S> from,
protected void initPredicatesRecursively(Predicates predicates, CriteriaQuery<?> query, Node<Filter> node, From<S, S> from,
FetchParent<S, S> fetch, CriteriaBuilder criteriaBuilder) {
if (node.isLeaf()) {
boolean hasColumnFilter = node.getData() != null;
if (hasColumnFilter) {
Filter columnFilter = node.getData();
columnPredicates.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
predicates.columns.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
} else if (hasGlobalFilter) {
Filter globalFilter = tree.getData();
globalPredicates.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
predicates.global.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
}
}
for (Node<Filter> child : node.getChildren()) {
Expand All @@ -109,10 +95,10 @@ protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> no
continue;
}
if (child.isLeaf()) {
initPredicatesRecursively(query, child, from, fetch, criteriaBuilder);
initPredicatesRecursively(predicates, query, child, from, fetch, criteriaBuilder);
} else {
Join<S, S> join = from.join(child.getName(), JoinType.LEFT);
initPredicatesRecursively(query, child, join, fetch, criteriaBuilder);
initPredicatesRecursively(predicates, query, child, join, fetch, criteriaBuilder);
}
}
}
Expand All @@ -121,4 +107,17 @@ protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> no
public Specification<T> buildSearchPane() {
return new DataTablesSearchPaneSpecification<>();
}

private static class Predicates {
public List<Predicate> columns = new ArrayList<>();
public List<Predicate> global = new ArrayList<>();

Predicate toPredicate(CriteriaBuilder criteriaBuilder) {
if (!global.isEmpty()) {
columns.add(criteriaBuilder.or(global.toArray(new Predicate[0])));
}

return columns.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(columns.toArray(new Predicate[0]));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ void globalFilter() {
assertThat(output.getData()).containsOnly(Employee.BRIELLE_WILLIAMSON);
}

@Test
void globalFilterWithMultiplePages() {
input.getSearch().setValue("e");
input.setLength(1);

DataTablesOutput<Employee> output = getOutput(input);
assertThat(output.getError()).isNull();
assertThat(output.getRecordsFiltered()).isEqualTo(6);
assertThat(output.getRecordsTotal()).isEqualTo(6);
}

@Test
void globalFilterIgnoreCaseIgnoreSpace() {
input.getSearch().setValue(" aMoS ");
Expand Down

0 comments on commit a6a8a0d

Please sign in to comment.