Skip to content

Commit 302dfd4

Browse files
mp911dechristophstrobl
authored andcommitted
DATAKV-137 - Fix cached query execution.
We now make sure that cached SpelCriteria's use the appropriate EvaluationContext containing parameters from the request instead of using the EvaluationContext of the cached query. Original Pull Request: #20
1 parent 66534d2 commit 302dfd4

File tree

4 files changed

+173
-6
lines changed

4 files changed

+173
-6
lines changed

src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
*
4545
* @author Christoph Strobl
4646
* @author Oliver Gierke
47+
* @author Mark Paluch
4748
*/
4849
public class KeyValuePartTreeQuery implements RepositoryQuery {
4950

@@ -134,15 +135,17 @@ protected KeyValueQuery<?> prepareQuery(KeyValueQuery<?> instance, Object[] para
134135
ParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(),
135136
parameters);
136137

137-
KeyValueQuery<?> query = new KeyValueQuery(instance.getCritieria());
138+
Object criteria = instance.getCritieria();
138139

139-
if (instance.getCritieria() instanceof SpelExpression) {
140+
if (criteria instanceof SpelCriteria || criteria instanceof SpelExpression) {
140141

142+
SpelExpression spelExpression = getSpelExpression(criteria);
141143
EvaluationContext context = this.evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(),
142144
parameters);
143-
query = new KeyValueQuery(new SpelCriteria((SpelExpression) instance.getCritieria(), context));
145+
criteria = new SpelCriteria(spelExpression, context);
144146
}
145-
147+
148+
KeyValueQuery<?> query = new KeyValueQuery(criteria);
146149
Pageable pageable = accessor.getPageable();
147150
Sort sort = accessor.getSort();
148151

@@ -153,6 +156,19 @@ protected KeyValueQuery<?> prepareQuery(KeyValueQuery<?> instance, Object[] para
153156
return query;
154157
}
155158

159+
private SpelExpression getSpelExpression(Object criteria) {
160+
161+
if(criteria instanceof SpelExpression){
162+
return (SpelExpression) criteria;
163+
}
164+
165+
if(criteria instanceof SpelCriteria){
166+
return getSpelExpression(((SpelCriteria) criteria).getExpression());
167+
}
168+
169+
throw new IllegalStateException(String.format("Cannot retrieve SpelExpression from %s", criteria));
170+
}
171+
156172
public KeyValueQuery<?> createQuery(ParameterAccessor accessor) {
157173

158174
PartTree tree = new PartTree(getQueryMethod().getName(), getQueryMethod().getEntityInformation().getJavaType());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2016 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.keyvalue.repository.query;
17+
18+
import static org.hamcrest.core.IsNot.*;
19+
import static org.hamcrest.core.IsSame.*;
20+
import static org.junit.Assert.*;
21+
import static org.mockito.Matchers.any;
22+
import static org.mockito.Mockito.*;
23+
24+
import java.lang.reflect.Method;
25+
import java.util.List;
26+
27+
import org.junit.Before;
28+
import org.junit.Test;
29+
import org.junit.runner.RunWith;
30+
import org.mockito.Mock;
31+
import org.mockito.runners.MockitoJUnitRunner;
32+
import org.springframework.data.keyvalue.Person;
33+
import org.springframework.data.keyvalue.core.KeyValueOperations;
34+
import org.springframework.data.keyvalue.core.SpelCriteria;
35+
import org.springframework.data.projection.ProjectionFactory;
36+
import org.springframework.data.repository.core.RepositoryMetadata;
37+
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
38+
import org.springframework.data.repository.query.QueryMethod;
39+
40+
/**
41+
* Unit tests for {@link CachingKeyValuePartTreeQuery}.
42+
*
43+
* @author Mark Paluch
44+
*/
45+
@RunWith(MockitoJUnitRunner.class)
46+
public class CachingKeyValuePartTreeQueryUnitTests {
47+
48+
@Mock KeyValueOperations kvOpsMock;
49+
@Mock RepositoryMetadata metadataMock;
50+
@Mock ProjectionFactory projectionFactoryMock;
51+
52+
@Before
53+
public void setUp() throws Exception {
54+
55+
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
56+
when(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);
57+
}
58+
59+
/**
60+
* @see DATAKV-137
61+
*/
62+
@Test
63+
@SuppressWarnings({ "unchecked", "rawtypes" })
64+
public void cachedSpelExpressionShouldBeReusedWithNewContext() throws NoSuchMethodException, SecurityException {
65+
66+
QueryMethod qm = new QueryMethod(Repo.class.getMethod("findByFirstname", String.class), metadataMock,
67+
projectionFactoryMock);
68+
69+
KeyValuePartTreeQuery query = new CachingKeyValuePartTreeQuery(qm, DefaultEvaluationContextProvider.INSTANCE,
70+
kvOpsMock, SpelQueryCreator.class);
71+
72+
Object[] args = new Object[] { "foo" };
73+
74+
SpelCriteria first = (SpelCriteria) query.prepareQuery(args).getCritieria();
75+
SpelCriteria second = (SpelCriteria) query.prepareQuery(args).getCritieria();
76+
77+
assertThat(first.getExpression(), sameInstance(second.getExpression()));
78+
assertThat(first.getContext(), not(sameInstance(second.getContext())));
79+
}
80+
81+
static interface Repo {
82+
83+
List<Person> findByFirstname(String firstname);
84+
}
85+
}

src/test/java/org/springframework/data/map/AbstractRepositoryUnitTests.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2015 the original author or authors.
2+
* Copyright 2014-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
4343
* @author Christoph Strobl
4444
* @author Oliver Gierke
4545
* @author Thomas Darimont
46+
* @author Mark Paluch
4647
*/
4748
public abstract class AbstractRepositoryUnitTests<T extends AbstractRepositoryUnitTests.PersonRepository> {
4849

@@ -61,7 +62,7 @@ public abstract class AbstractRepositoryUnitTests<T extends AbstractRepositoryUn
6162
public void setup() {
6263

6364
KeyValueOperations operations = new KeyValueTemplate(new MapKeyValueAdapter());
64-
KeyValueRepositoryFactory keyValueRepositoryFactory = new KeyValueRepositoryFactory(operations);
65+
KeyValueRepositoryFactory keyValueRepositoryFactory = createKeyValueRepositoryFactory(operations);
6566

6667
this.repository = getRepository(keyValueRepositoryFactory);
6768
}
@@ -77,15 +78,29 @@ public void findBy() {
7778
assertThat(repository.findByAge(19), hasItems(CERSEI, JAIME));
7879
}
7980

81+
/**
82+
* @see DATAKV-137
83+
*/
84+
@Test
85+
public void findByFirstname() {
86+
87+
repository.save(LENNISTERS);
88+
89+
assertThat(repository.findByFirstname(CERSEI.getFirstname()), hasItems(CERSEI));
90+
assertThat(repository.findByFirstname(JAIME.getFirstname()), hasItems(JAIME));
91+
}
92+
8093
/**
8194
* @see DATACMNS-525
95+
* @see DATAKV-137
8296
*/
8397
@Test
8498
public void combindedFindUsingAnd() {
8599

86100
repository.save(LENNISTERS);
87101

88102
assertThat(repository.findByFirstnameAndAge(JAIME.getFirstname(), 19), hasItem(JAIME));
103+
assertThat(repository.findByFirstnameAndAge(TYRION.getFirstname(), 17), hasItem(TYRION));
89104
}
90105

91106
/**
@@ -120,13 +135,15 @@ public void findByConnectingOr() {
120135

121136
/**
122137
* @see DATACMNS-525
138+
* @see DATAKV-137
123139
*/
124140
@Test
125141
public void singleEntityExecution() {
126142

127143
repository.save(LENNISTERS);
128144

129145
assertThat(repository.findByAgeAndFirstname(TYRION.getAge(), TYRION.getFirstname()), is(TYRION));
146+
assertThat(repository.findByAgeAndFirstname(CERSEI.getAge(), CERSEI.getFirstname()), is(CERSEI));
130147
}
131148

132149
/**
@@ -183,6 +200,10 @@ public void projectsResultToDynamicInterface() {
183200
assertThat(result.get(0).getFirstname(), is(CERSEI.getFirstname()));
184201
}
185202

203+
protected KeyValueRepositoryFactory createKeyValueRepositoryFactory(KeyValueOperations operations) {
204+
return new KeyValueRepositoryFactory(operations);
205+
}
206+
186207
protected abstract T getRepository(KeyValueRepositoryFactory factory);
187208

188209
public static interface PersonRepository extends CrudRepository<Person, String>, KeyValueRepository<Person, String> {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2016 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.map;
17+
18+
import static org.hamcrest.Matchers.hasItems;
19+
import static org.junit.Assert.assertThat;
20+
21+
import org.junit.Test;
22+
import org.springframework.data.keyvalue.core.KeyValueOperations;
23+
import org.springframework.data.keyvalue.repository.query.CachingKeyValuePartTreeQuery;
24+
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
25+
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;
26+
import org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository;
27+
import org.springframework.data.map.AbstractRepositoryUnitTests.PersonRepository;
28+
29+
/**
30+
* Unit tests for {@link SimpleKeyValueRepository} using {@link CachingKeyValuePartTreeQuery} and {@link SpelQueryCreator}.
31+
*
32+
* @author Mark Paluch
33+
*/
34+
public class CachingQuerySimpleKeyValueRepositoryUnitTests extends AbstractRepositoryUnitTests<PersonRepository> {
35+
36+
@Override
37+
protected KeyValueRepositoryFactory createKeyValueRepositoryFactory(KeyValueOperations operations) {
38+
return new KeyValueRepositoryFactory(operations, SpelQueryCreator.class, CachingKeyValuePartTreeQuery.class);
39+
}
40+
41+
@Override
42+
protected PersonRepository getRepository(KeyValueRepositoryFactory factory) {
43+
return factory.getRepository(PersonRepository.class);
44+
}
45+
}

0 commit comments

Comments
 (0)