Skip to content

Commit 895493c

Browse files
committed
DATAJPA-300 - Specifications are now more lenient against null values.
Static factory methods on Specifications are now more lenient regarding null values eventually evaluating to null predicates on concatenation attempts.
1 parent a4c5fef commit 895493c

File tree

2 files changed

+150
-22
lines changed

2 files changed

+150
-22
lines changed

src/main/java/org/springframework/data/jpa/domain/Specifications.java

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,19 @@
2020
import javax.persistence.criteria.Predicate;
2121
import javax.persistence.criteria.Root;
2222

23-
import org.springframework.util.Assert;
24-
2523
/**
2624
* Helper class to easily combine {@link Specification} instances.
2725
*
2826
* @author Oliver Gierke
2927
*/
3028
public class Specifications<T> implements Specification<T> {
3129

32-
private static final String NOT_NULL = "Specification given to %s(…) must not be null!";
33-
3430
private final Specification<T> spec;
3531

3632
/**
3733
* Creates a new {@link Specifications} wrapper for the given {@link Specification}.
3834
*
39-
* @param spec must not be {@literal null}.
35+
* @param spec can be {@literal null}.
4036
*/
4137
private Specifications(Specification<T> spec) {
4238
this.spec = spec;
@@ -46,29 +42,30 @@ private Specifications(Specification<T> spec) {
4642
* Simple static factory method to add some syntactic sugar around a {@link Specification}.
4743
*
4844
* @param <T>
49-
* @param spec must not be {@literal null}.
45+
* @param spec can be {@literal null}.
5046
* @return
5147
*/
5248
public static <T> Specifications<T> where(Specification<T> spec) {
53-
54-
Assert.notNull(spec, String.format(NOT_NULL, "where"));
5549
return new Specifications<T>(spec);
5650
}
5751

5852
/**
5953
* ANDs the given {@link Specification} to the current one.
6054
*
6155
* @param <T>
62-
* @param other must not be {@literal null}.
56+
* @param other can be {@literal null}.
6357
* @return
6458
*/
6559
public Specifications<T> and(final Specification<T> other) {
6660

67-
Assert.notNull(spec, String.format(NOT_NULL, "and"));
68-
6961
return new Specifications<T>(new Specification<T>() {
7062
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
71-
return builder.and(spec.toPredicate(root, query, builder), other.toPredicate(root, query, builder));
63+
64+
Predicate otherPredicate = other == null ? null : other.toPredicate(root, query, builder);
65+
Predicate thisPredicate = spec == null ? null : spec.toPredicate(root, query, builder);
66+
67+
return thisPredicate == null ? otherPredicate : otherPredicate == null ? thisPredicate : builder.and(
68+
thisPredicate, otherPredicate);
7269
}
7370
});
7471
}
@@ -77,16 +74,19 @@ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuild
7774
* ORs the given specification to the current one.
7875
*
7976
* @param <T>
80-
* @param other must not be {@literal null}.
77+
* @param other can be {@literal null}.
8178
* @return
8279
*/
8380
public Specifications<T> or(final Specification<T> other) {
8481

85-
Assert.notNull(spec, String.format(NOT_NULL, "or"));
86-
8782
return new Specifications<T>(new Specification<T>() {
8883
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
89-
return builder.or(spec.toPredicate(root, query, builder), other.toPredicate(root, query, builder));
84+
85+
Predicate otherPredicate = other == null ? null : other.toPredicate(root, query, builder);
86+
Predicate thisPredicate = spec == null ? null : spec.toPredicate(root, query, builder);
87+
88+
return thisPredicate == null ? otherPredicate : otherPredicate == null ? thisPredicate : builder.or(
89+
thisPredicate, otherPredicate);
9090
}
9191
});
9292
}
@@ -95,16 +95,13 @@ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuild
9595
* Negates the given {@link Specification}.
9696
*
9797
* @param <T>
98-
* @param spec must not be {@literal null}.
98+
* @param spec can be {@literal null}.
9999
* @return
100100
*/
101101
public static <T> Specifications<T> not(final Specification<T> spec) {
102-
103-
Assert.notNull(spec, String.format(NOT_NULL, "not"));
104-
105102
return new Specifications<T>(new Specification<T>() {
106103
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
107-
return builder.not(spec.toPredicate(root, query, builder));
104+
return spec == null ? null : builder.not(spec.toPredicate(root, query, builder));
108105
}
109106
});
110107
}
@@ -114,6 +111,6 @@ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuild
114111
* @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder)
115112
*/
116113
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
117-
return spec.toPredicate(root, query, builder);
114+
return spec == null ? null : spec.toPredicate(root, query, builder);
118115
}
119116
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.domain;
17+
18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.junit.Assert.*;
20+
import static org.mockito.Mockito.*;
21+
import static org.springframework.data.jpa.domain.Specifications.*;
22+
23+
import javax.persistence.criteria.CriteriaBuilder;
24+
import javax.persistence.criteria.CriteriaQuery;
25+
import javax.persistence.criteria.Predicate;
26+
import javax.persistence.criteria.Root;
27+
28+
import org.junit.Before;
29+
import org.junit.Test;
30+
import org.junit.runner.RunWith;
31+
import org.mockito.Mock;
32+
import org.mockito.runners.MockitoJUnitRunner;
33+
34+
/**
35+
* @author Oliver Gierke
36+
*/
37+
@RunWith(MockitoJUnitRunner.class)
38+
public class SpecificationsUnitTests {
39+
40+
@Mock
41+
Specification<Object> mockSpec;
42+
@Mock
43+
Root<Object> root;
44+
@Mock
45+
CriteriaQuery<?> query;
46+
@Mock
47+
CriteriaBuilder builder;
48+
49+
@Mock
50+
Predicate predicate;
51+
52+
@Before
53+
public void setUp() {
54+
when(mockSpec.toPredicate(root, query, builder)).thenReturn(predicate);
55+
}
56+
57+
/**
58+
* @see DATAJPA-300
59+
*/
60+
@Test
61+
public void createsSpecificationsFromNull() {
62+
63+
Specifications<Object> specification = where(null);
64+
assertThat(specification, is(notNullValue()));
65+
assertThat(specification.toPredicate(root, query, builder), is(nullValue()));
66+
}
67+
68+
/**
69+
* @see DATAJPA-300
70+
*/
71+
@Test
72+
public void negatesNullSpecToNull() {
73+
74+
Specifications<Object> specification = not((Specification<Object>) null);
75+
76+
assertThat(specification, is(notNullValue()));
77+
assertThat(specification.toPredicate(root, query, builder), is(nullValue()));
78+
}
79+
80+
/**
81+
* @see DATAJPA-300
82+
*/
83+
@Test
84+
public void andConcatenatesSpecToNullSpec() {
85+
86+
Specifications<Object> specification = where(null);
87+
specification = specification.and(mockSpec);
88+
89+
assertThat(specification, is(notNullValue()));
90+
assertThat(specification.toPredicate(root, query, builder), is(predicate));
91+
}
92+
93+
/**
94+
* @see DATAJPA-300
95+
*/
96+
@Test
97+
public void andConcatenatesNullSpecToSpec() {
98+
99+
Specifications<Object> specification = where(mockSpec);
100+
specification = specification.and(null);
101+
102+
assertThat(specification, is(notNullValue()));
103+
assertThat(specification.toPredicate(root, query, builder), is(predicate));
104+
}
105+
106+
/**
107+
* @see DATAJPA-300
108+
*/
109+
@Test
110+
public void orConcatenatesSpecToNullSpec() {
111+
112+
Specifications<Object> specification = where(null);
113+
specification = specification.or(mockSpec);
114+
115+
assertThat(specification, is(notNullValue()));
116+
assertThat(specification.toPredicate(root, query, builder), is(predicate));
117+
}
118+
119+
/**
120+
* @see DATAJPA-300
121+
*/
122+
@Test
123+
public void orConcatenatesNullSpecToSpec() {
124+
125+
Specifications<Object> specification = where(mockSpec);
126+
specification = specification.or(null);
127+
128+
assertThat(specification, is(notNullValue()));
129+
assertThat(specification.toPredicate(root, query, builder), is(predicate));
130+
}
131+
}

0 commit comments

Comments
 (0)