Skip to content

Commit a6a8a0d

Browse files
fix: fix integration with Spring Boot 3
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
1 parent e53eb8c commit a6a8a0d

File tree

2 files changed

+39
-29
lines changed

2 files changed

+39
-29
lines changed

src/main/java/org/springframework/data/jpa/datatables/SpecificationBuilder.java

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,35 @@ public Specification<T> build() {
2020
}
2121

2222
private class DataTablesSpecification<S> implements Specification<S> {
23-
protected List<Predicate> columnPredicates = new ArrayList<>();
24-
protected List<Predicate> globalPredicates = new ArrayList<>();
25-
2623
@Override
2724
public Predicate toPredicate(@NonNull Root<S> root, @NonNull CriteriaQuery<?> query, @NonNull CriteriaBuilder criteriaBuilder) {
28-
initPredicatesRecursively(query, tree, root, root, criteriaBuilder);
25+
Predicates predicates = new Predicates();
26+
initPredicatesRecursively(predicates, query, tree, root, root, criteriaBuilder);
2927

3028
if (input.getSearchPanes() != null) {
3129
input.getSearchPanes().forEach((attribute, values) -> {
3230
if (!values.isEmpty()) {
33-
columnPredicates.add(root.get(attribute).in(values));
31+
predicates.columns.add(root.get(attribute).in(values));
3432
}
3533
});
3634
}
3735

38-
final Predicate predicate = createFinalPredicate(criteriaBuilder);
39-
columnPredicates.clear();
40-
return predicate;
36+
return predicates.toPredicate(criteriaBuilder);
4137
}
4238

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

47-
protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> node, From<S, S> from, FetchParent<S, S> fetch, CriteriaBuilder criteriaBuilder) {
43+
protected void initPredicatesRecursively(Predicates predicates, CriteriaQuery<?> query, Node<Filter> node, From<S, S> from, FetchParent<S, S> fetch, CriteriaBuilder criteriaBuilder) {
4844
if (node.isLeaf()) {
4945
boolean hasColumnFilter = node.getData() != null;
5046
if (hasColumnFilter) {
5147
Filter columnFilter = node.getData();
52-
columnPredicates.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
48+
predicates.columns.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
5349
} else if (hasGlobalFilter) {
5450
Filter globalFilter = tree.getData();
55-
globalPredicates.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
51+
predicates.global.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
5652
}
5753
}
5854
for (Node<Filter> child : node.getChildren()) {
@@ -62,44 +58,34 @@ protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> no
6258
continue;
6359
}
6460
if (child.isLeaf()) {
65-
initPredicatesRecursively(query, child, from, fetch, criteriaBuilder);
61+
initPredicatesRecursively(predicates, query, child, from, fetch, criteriaBuilder);
6662
} else {
6763
Join<S, S> join = from.join(child.getName(), JoinType.LEFT);
6864

6965
if (isCountQuery(query)) {
70-
initPredicatesRecursively(query, child, join, join, criteriaBuilder);
66+
initPredicatesRecursively(predicates, query, child, join, join, criteriaBuilder);
7167
} else {
7268
Fetch<S, S> childFetch = fetch.fetch(child.getName(), JoinType.LEFT);
73-
initPredicatesRecursively(query, child, join, childFetch, criteriaBuilder);
69+
initPredicatesRecursively(predicates, query, child, join, childFetch, criteriaBuilder);
7470
}
7571
}
7672
}
7773
}
78-
79-
protected Predicate createFinalPredicate(CriteriaBuilder criteriaBuilder) {
80-
List<Predicate> allPredicates = new ArrayList<>(columnPredicates);
81-
82-
if (!globalPredicates.isEmpty()) {
83-
allPredicates.add(criteriaBuilder.or(globalPredicates.toArray(new Predicate[0])));
84-
}
85-
86-
return allPredicates.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(allPredicates.toArray(new Predicate[0]));
87-
}
8874
}
8975

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

9278
@Override
93-
protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> node, From<S, S> from,
79+
protected void initPredicatesRecursively(Predicates predicates, CriteriaQuery<?> query, Node<Filter> node, From<S, S> from,
9480
FetchParent<S, S> fetch, CriteriaBuilder criteriaBuilder) {
9581
if (node.isLeaf()) {
9682
boolean hasColumnFilter = node.getData() != null;
9783
if (hasColumnFilter) {
9884
Filter columnFilter = node.getData();
99-
columnPredicates.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
85+
predicates.columns.add(columnFilter.createPredicate(from, criteriaBuilder, node.getName()));
10086
} else if (hasGlobalFilter) {
10187
Filter globalFilter = tree.getData();
102-
globalPredicates.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
88+
predicates.global.add(globalFilter.createPredicate(from, criteriaBuilder, node.getName()));
10389
}
10490
}
10591
for (Node<Filter> child : node.getChildren()) {
@@ -109,10 +95,10 @@ protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> no
10995
continue;
11096
}
11197
if (child.isLeaf()) {
112-
initPredicatesRecursively(query, child, from, fetch, criteriaBuilder);
98+
initPredicatesRecursively(predicates, query, child, from, fetch, criteriaBuilder);
11399
} else {
114100
Join<S, S> join = from.join(child.getName(), JoinType.LEFT);
115-
initPredicatesRecursively(query, child, join, fetch, criteriaBuilder);
101+
initPredicatesRecursively(predicates, query, child, join, fetch, criteriaBuilder);
116102
}
117103
}
118104
}
@@ -121,4 +107,17 @@ protected void initPredicatesRecursively(CriteriaQuery<?> query, Node<Filter> no
121107
public Specification<T> buildSearchPane() {
122108
return new DataTablesSearchPaneSpecification<>();
123109
}
110+
111+
private static class Predicates {
112+
public List<Predicate> columns = new ArrayList<>();
113+
public List<Predicate> global = new ArrayList<>();
114+
115+
Predicate toPredicate(CriteriaBuilder criteriaBuilder) {
116+
if (!global.isEmpty()) {
117+
columns.add(criteriaBuilder.or(global.toArray(new Predicate[0])));
118+
}
119+
120+
return columns.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(columns.toArray(new Predicate[0]));
121+
}
122+
}
124123
}

src/test/java/org/springframework/data/jpa/datatables/repository/EmployeeRepositoryTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ void globalFilter() {
9797
assertThat(output.getData()).containsOnly(Employee.BRIELLE_WILLIAMSON);
9898
}
9999

100+
@Test
101+
void globalFilterWithMultiplePages() {
102+
input.getSearch().setValue("e");
103+
input.setLength(1);
104+
105+
DataTablesOutput<Employee> output = getOutput(input);
106+
assertThat(output.getError()).isNull();
107+
assertThat(output.getRecordsFiltered()).isEqualTo(6);
108+
assertThat(output.getRecordsTotal()).isEqualTo(6);
109+
}
110+
100111
@Test
101112
void globalFilterIgnoreCaseIgnoreSpace() {
102113
input.getSearch().setValue(" aMoS ");

0 commit comments

Comments
 (0)