Skip to content

Commit 6f26cb0

Browse files
raoua-enggnodet
authored andcommitted
CXF-9174: Make doBuildPredicate and doBuildCollectionPredicate protected for extensibility (#2798)
Allow subclasses of AbstractJPATypedQueryVisitor to customize JPA predicate building by changing doBuildPredicate() and doBuildCollectionPredicate() from private to protected visibility. This enables the Template Method pattern for custom FIQL-to-JPA predicate generation without modifying the core visitor. Includes tests demonstrating a custom visitor with case-insensitive LIKE matching and collection predicate override. Co-authored-by: Guillaume Nodet <gnodet@gmail.com> (cherry picked from commit 95a5856)
1 parent 070a691 commit 6f26cb0

File tree

4 files changed

+141
-3
lines changed

4 files changed

+141
-3
lines changed

rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/jpa/AbstractJPATypedQueryVisitor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private Predicate buildPredicate(PrimitiveStatement ps) {
184184
}
185185

186186
@SuppressWarnings({ "unchecked", "rawtypes" })
187-
private Predicate doBuildPredicate(ConditionType ct, Path<?> path, Class<?> valueClazz, Object value) {
187+
protected Predicate doBuildPredicate(ConditionType ct, Path<?> path, Class<?> valueClazz, Object value) {
188188

189189
Class<? extends Comparable> clazz = (Class<? extends Comparable>)valueClazz;
190190
Expression<? extends Comparable> exp = path.as(clazz);
@@ -246,7 +246,7 @@ private Predicate doBuildPredicate(ConditionType ct, Path<?> path, Class<?> valu
246246
}
247247

248248
@SuppressWarnings({ "unchecked", "rawtypes" })
249-
private Predicate doBuildCollectionPredicate(ConditionType ct, Path<?> path, CollectionCheckInfo collInfo) {
249+
protected Predicate doBuildCollectionPredicate(ConditionType ct, Path<?> path, CollectionCheckInfo collInfo) {
250250
Predicate pred = null;
251251

252252
Expression<Integer> exp = builder.size((Expression<? extends Collection>)path);

rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/jpa/AbstractJPATypedQueryVisitorTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ public void tearDown() throws Exception {
173173
}
174174
}
175175

176-
176+
protected EntityManager getEntityManager() {
177+
return em;
178+
}
177179

178180
protected List<Book> queryBooks(String expression) throws Exception {
179181
return queryBooks(expression, null, null, null);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.cxf.jaxrs.ext.search.jpa;
20+
21+
import jakarta.persistence.EntityManager;
22+
import jakarta.persistence.criteria.Path;
23+
import jakarta.persistence.criteria.Predicate;
24+
import jakarta.persistence.metamodel.Attribute;
25+
import jakarta.persistence.metamodel.Bindable;
26+
import org.apache.cxf.jaxrs.ext.search.ConditionType;
27+
import org.apache.cxf.jaxrs.ext.search.collections.CollectionCheckInfo;
28+
29+
public class CustomJPACriteriaVisitor extends JPACriteriaQueryVisitor<Book, Book> {
30+
31+
CustomJPACriteriaVisitor(EntityManager em) {
32+
super(em, Book.class, Book.class);
33+
}
34+
35+
@Override
36+
protected Predicate doBuildPredicate(ConditionType ct, Path<?> path, Class<?> valueClazz, Object value) {
37+
String name = getAttributeName(path);
38+
if ("bookTitle".equals(name)) {
39+
return getCriteriaBuilder().like(
40+
getCriteriaBuilder().lower(path.as(String.class)),
41+
"%" + value.toString().toLowerCase() + "%");
42+
}
43+
44+
return super.doBuildPredicate(ct, path, valueClazz, value);
45+
}
46+
47+
@Override
48+
protected Predicate doBuildCollectionPredicate(ConditionType ct, Path<?> path, CollectionCheckInfo collInfo) {
49+
return getCriteriaBuilder().disjunction();
50+
}
51+
52+
private String getAttributeName(Path<?> path) {
53+
Bindable<?> model = path.getModel();
54+
if (model instanceof Attribute) {
55+
return ((Attribute<?, ?>)model).getName();
56+
}
57+
return null;
58+
}
59+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.cxf.jaxrs.ext.search.jpa;
20+
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import org.apache.cxf.jaxrs.ext.search.SearchCondition;
25+
import org.apache.cxf.jaxrs.ext.search.SearchConditionParser;
26+
import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser;
27+
28+
import org.junit.Test;
29+
30+
import static org.junit.Assert.assertEquals;
31+
import static org.junit.Assert.assertTrue;
32+
33+
public class JPACriteriaQueryVisitorExtensionTest extends AbstractJPATypedQueryVisitorTest {
34+
35+
@Test
36+
public void testCustomPredicateExtensionIsUsed() throws Exception {
37+
SearchCondition<Book> filter = getParser().parse("bookTitle==NUM9");
38+
39+
JPACriteriaQueryVisitor<Book, Book> visitor = new CustomJPACriteriaVisitor(getEntityManager());
40+
41+
filter.accept(visitor);
42+
43+
List<Book> result = getEntityManager().createQuery(visitor.getQuery()).getResultList();
44+
45+
// The custom visitor uses a case-insensitive LIKE query for bookTitle,
46+
// so "NUM9" matches "num9"
47+
assertEquals(1, result.size());
48+
}
49+
50+
@Test
51+
public void testCollectionPredicateOverrideIsUsed() throws Exception {
52+
// count(authors) triggers the collection predicate path
53+
SearchCondition<Book> filter = getParser().parse("count(authors)==1");
54+
55+
JPACriteriaQueryVisitor<Book, Book> visitor = new CustomJPACriteriaVisitor(getEntityManager());
56+
57+
filter.accept(visitor);
58+
59+
List<Book> results = getEntityManager().createQuery(visitor.getQuery()).getResultList();
60+
61+
// Without override -> books with exactly 1 author would be returned
62+
// With override (disjunction) -> 0 results
63+
assertTrue(results.isEmpty());
64+
}
65+
66+
@Override
67+
protected SearchConditionParser<Book> getParser() {
68+
return new FiqlParser<>(Book.class);
69+
}
70+
71+
@Override
72+
protected SearchConditionParser<Book> getParser(Map<String, String> visitorProps,
73+
Map<String, String> parserBinProps) {
74+
return new FiqlParser<>(Book.class, parserBinProps);
75+
}
76+
77+
}

0 commit comments

Comments
 (0)