Skip to content

Commit 786842c

Browse files
committed
DATAJPA-218 - Adopt QBE API refactoring.
Adopt changes from query by example API refactoring. Related ticket: DATACMNS-810.
1 parent 4af84b0 commit 786842c

File tree

6 files changed

+241
-142
lines changed

6 files changed

+241
-142
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
target/
2+
.idea/
23
.settings/
4+
*.iml
35
.project
46
.classpath
57
.springBeans

src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434

3535
import org.springframework.dao.InvalidDataAccessApiUsageException;
3636
import org.springframework.data.domain.Example;
37-
import org.springframework.data.domain.Example.NullHandler;
37+
import org.springframework.data.domain.ExampleSpec;
38+
import org.springframework.data.domain.ExampleSpecAccessor;
3839
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
3940
import org.springframework.orm.jpa.JpaSystemException;
4041
import org.springframework.util.Assert;
@@ -48,8 +49,9 @@
4849
* The builder includes any {@link SingularAttribute} of the {@link Example#getProbe()} applying {@link String} and
4950
* {@literal null} matching strategies configured on the {@link Example}. Ignored paths are no matter of their actual
5051
* value not considered. <br />
51-
*
52+
*
5253
* @author Christoph Strobl
54+
* @author Mark Paluch
5355
* @since 1.10
5456
*/
5557
public class QueryByExamplePredicateBuilder {
@@ -63,7 +65,7 @@ public class QueryByExamplePredicateBuilder {
6365

6466
/**
6567
* Extract the {@link Predicate} representing the {@link Example}.
66-
*
68+
*
6769
* @param root must not be {@literal null}.
6870
* @param cb must not be {@literal null}.
6971
* @param example must not be {@literal null}.
@@ -73,10 +75,10 @@ public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Examp
7375

7476
Assert.notNull(root, "Root must not be null!");
7577
Assert.notNull(cb, "CriteriaBuilder must not be null!");
76-
Assert.notNull(example, "Root must not be null!");
78+
Assert.notNull(example, "Example must not be null!");
7779

78-
List<Predicate> predicates = getPredicates("", cb, root, root.getModel(), example.getSampleObject(), example,
79-
new PathNode("root", null, example.getSampleObject()));
80+
List<Predicate> predicates = getPredicates("", cb, root, root.getModel(), example.getProbe(),
81+
example.getProbeType(), ExampleSpecAccessor.of(example), new PathNode("root", null, example.getProbe()));
8082

8183
if (predicates.isEmpty()) {
8284
return cb.isTrue(cb.literal(false));
@@ -90,8 +92,8 @@ public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Examp
9092
}
9193

9294
@SuppressWarnings({ "rawtypes", "unchecked" })
93-
static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> from, ManagedType<?> type,
94-
Object value, Example<?> example, PathNode currentNode) {
95+
static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> from, ManagedType<?> type, Object value,
96+
Class<?> probeType, ExampleSpecAccessor exampleAccessor, PathNode currentNode) {
9597

9698
List<Predicate> predicates = new ArrayList<Predicate>();
9799
DirectFieldAccessFallbackBeanWrapper beanWrapper = new DirectFieldAccessFallbackBeanWrapper(value);
@@ -100,16 +102,16 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr
100102

101103
String currentPath = !StringUtils.hasText(path) ? attribute.getName() : path + "." + attribute.getName();
102104

103-
if (example.isIgnoredPath(currentPath)) {
105+
if (exampleAccessor.isIgnoredPath(currentPath)) {
104106
continue;
105107
}
106108

107-
Object attributeValue = example.getValueTransformerForPath(currentPath).convert(
108-
beanWrapper.getPropertyValue(attribute.getName()));
109+
Object attributeValue = exampleAccessor.getValueTransformerForPath(currentPath)
110+
.convert(beanWrapper.getPropertyValue(attribute.getName()));
109111

110112
if (attributeValue == null) {
111113

112-
if (example.getNullHandler().equals(NullHandler.INCLUDE)) {
114+
if (exampleAccessor.getNullHandler().equals(ExampleSpec.NullHandler.INCLUDE)) {
113115
predicates.add(cb.isNull(from.get(attribute)));
114116
}
115117
continue;
@@ -118,39 +120,39 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr
118120
if (attribute.getPersistentAttributeType().equals(PersistentAttributeType.EMBEDDED)) {
119121

120122
predicates.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()),
121-
(ManagedType<?>) attribute.getType(), attributeValue, example, currentNode));
123+
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, currentNode));
122124
continue;
123125
}
124126

125127
if (isAssociation(attribute)) {
126128

127129
if (!(from instanceof From)) {
128-
throw new JpaSystemException(new IllegalArgumentException(String.format(
129-
"Unexpected path type for %s. Found % where From.class was expected.", currentPath, from)));
130+
throw new JpaSystemException(new IllegalArgumentException(
131+
String.format("Unexpected path type for %s. Found % where From.class was expected.", currentPath, from)));
130132
}
131133

132134
PathNode node = currentNode.add(attribute.getName(), attributeValue);
133135
if (node.spansCycle()) {
134-
throw new InvalidDataAccessApiUsageException(String.format(
135-
"Path '%s' from root %s must not span a cyclic property reference!\r\n%s", currentPath,
136-
ClassUtils.getShortName(example.getSampleType()), node));
136+
throw new InvalidDataAccessApiUsageException(
137+
String.format("Path '%s' from root %s must not span a cyclic property reference!\r\n%s", currentPath,
138+
ClassUtils.getShortName(probeType), node));
137139
}
138140

139141
predicates.addAll(getPredicates(currentPath, cb, ((From<?, ?>) from).join(attribute.getName()),
140-
(ManagedType<?>) attribute.getType(), attributeValue, example, node));
142+
(ManagedType<?>) attribute.getType(), attributeValue, probeType, exampleAccessor, node));
141143

142144
continue;
143145
}
144146

145147
if (attribute.getJavaType().equals(String.class)) {
146148

147149
Expression<String> expression = from.get(attribute);
148-
if (example.isIgnoreCaseForPath(currentPath)) {
150+
if (exampleAccessor.isIgnoreCaseForPath(currentPath)) {
149151
expression = cb.lower(expression);
150152
attributeValue = attributeValue.toString().toLowerCase();
151153
}
152154

153-
switch (example.getStringMatcherForPath(currentPath)) {
155+
switch (exampleAccessor.getStringMatcherForPath(currentPath)) {
154156

155157
case DEFAULT:
156158
case EXACT:
@@ -166,8 +168,8 @@ static List<Predicate> getPredicates(String path, CriteriaBuilder cb, Path<?> fr
166168
predicates.add(cb.like(expression, "%" + attributeValue));
167169
break;
168170
default:
169-
throw new IllegalArgumentException("Unsupported StringMatcher "
170-
+ example.getStringMatcherForPath(currentPath));
171+
throw new IllegalArgumentException(
172+
"Unsupported StringMatcher " + exampleAccessor.getStringMatcherForPath(currentPath));
171173
}
172174
} else {
173175
predicates.add(cb.equal(from.get(attribute), attributeValue));
@@ -184,7 +186,7 @@ private static boolean isAssociation(Attribute<?, ?> attribute) {
184186
/**
185187
* {@link PathNode} is used to dynamically grow a directed graph structure that allows to detect cycles within its
186188
* direct predecessor nodes by comparing parent node values using {@link System#identityHashCode(Object)}.
187-
*
189+
*
188190
* @author Christoph Strobl
189191
*/
190192
private static class PathNode {

src/main/java/org/springframework/data/jpa/repository/JpaRepository.java

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,43 +21,48 @@
2121
import javax.persistence.EntityManager;
2222

2323
import org.springframework.data.domain.Example;
24-
import org.springframework.data.domain.Page;
25-
import org.springframework.data.domain.Pageable;
2624
import org.springframework.data.domain.Sort;
2725
import org.springframework.data.repository.NoRepositoryBean;
2826
import org.springframework.data.repository.PagingAndSortingRepository;
27+
import org.springframework.data.repository.query.QueryByExampleExecutor;
2928

3029
/**
3130
* JPA specific extension of {@link org.springframework.data.repository.Repository}.
32-
*
31+
*
3332
* @author Oliver Gierke
3433
* @author Christoph Strobl
34+
* @author Mark Paluch
3535
*/
3636
@NoRepositoryBean
37-
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
37+
public interface JpaRepository<T, ID extends Serializable>
38+
extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
3839

3940
/*
4041
* (non-Javadoc)
4142
* @see org.springframework.data.repository.CrudRepository#findAll()
4243
*/
44+
@Override
4345
List<T> findAll();
4446

4547
/*
4648
* (non-Javadoc)
4749
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
4850
*/
51+
@Override
4952
List<T> findAll(Sort sort);
5053

5154
/*
5255
* (non-Javadoc)
5356
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
5457
*/
58+
@Override
5559
List<T> findAll(Iterable<ID> ids);
5660

5761
/*
5862
* (non-Javadoc)
5963
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
6064
*/
65+
@Override
6166
<S extends T> List<S> save(Iterable<S> entities);
6267

6368
/**
@@ -67,7 +72,7 @@ public interface JpaRepository<T, ID extends Serializable> extends PagingAndSort
6772

6873
/**
6974
* Saves an entity and flushes changes instantly.
70-
*
75+
*
7176
* @param entity
7277
* @return the saved entity
7378
*/
@@ -76,7 +81,7 @@ public interface JpaRepository<T, ID extends Serializable> extends PagingAndSort
7681
/**
7782
* Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear
7883
* the {@link javax.persistence.EntityManager} after the call.
79-
*
84+
*
8085
* @param entities
8186
*/
8287
void deleteInBatch(Iterable<T> entities);
@@ -88,40 +93,23 @@ public interface JpaRepository<T, ID extends Serializable> extends PagingAndSort
8893

8994
/**
9095
* Returns a reference to the entity with the given identifier.
91-
*
96+
*
9297
* @param id must not be {@literal null}.
9398
* @return a reference to the entity with the given identifier.
9499
* @see EntityManager#getReference(Class, Object)
95100
*/
96101
T getOne(ID id);
97102

98-
/**
99-
* Returns all instances of the type specified by the given {@link Example}.
100-
*
101-
* @param example must not be {@literal null}.
102-
* @return
103-
* @since 1.10
103+
/* (non-Javadoc)
104+
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
104105
*/
105-
List<T> findAllByExample(Example<T> example);
106+
@Override
107+
<S extends T> List<T> findAll(Example<S> example);
106108

107-
/**
108-
* Returns all instances of the type specified by the given {@link Example}.
109-
*
110-
* @param example must not be {@literal null}.
111-
* @param sort can be {@literal null}.
112-
* @return all entities sorted by the given options
113-
* @since 1.10
109+
/* (non-Javadoc)
110+
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
114111
*/
115-
List<T> findAllByExample(Example<T> example, Sort sort);
112+
@Override
113+
<S extends T> List<T> findAll(Example<S> example, Sort sort);
116114

117-
/**
118-
* Returns a {@link Page} of entities meeting the paging restriction specified by the given {@link Example} limited to
119-
* criteria provided in the {@code Pageable} object.
120-
*
121-
* @param example must not be {@literal null}.
122-
* @param pageable can be {@literal null}.
123-
* @return a {@link Page} of entities
124-
* @since 1.10
125-
*/
126-
Page<T> findAllByExample(Example<T> example, Pageable pageable);
127115
}

0 commit comments

Comments
 (0)