Skip to content

Commit 323cb32

Browse files
DATAREDIS-605 - Polishing
Some minor code and documentation changes. More interestingly obtain the RedisConverter instead of the KeyValueAdapter for IndexResolver resolution in QueryByExampleRedisExecutor and stick to findOne contract by throwing IncorrectResultSizeDataAccessException for queries returning non unique results. Original Pull Request: #301
1 parent a3f876f commit 323cb32

File tree

5 files changed

+51
-34
lines changed

5 files changed

+51
-34
lines changed

src/main/asciidoc/new-features.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ New and noteworthy in the latest releases.
88

99
* Unix domain socket connections using <<redis:connectors:lettuce,Lettuce>>.
1010
* <<redis:write-to-master-read-from-slave, Write to Master read from Slave>> support using Lettuce.
11-
* <<query-by-example,Query by Example>> integration
11+
* <<query-by-example,Query by Example>> integration.
1212

1313
[[new-in-2.0.0]]
1414
== New in Spring Data Redis 2.0

src/main/java/org/springframework/data/redis/core/RedisKeyValueTemplate.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.data.keyvalue.core.KeyValueAdapter;
2323
import org.springframework.data.keyvalue.core.KeyValueCallback;
2424
import org.springframework.data.keyvalue.core.KeyValueTemplate;
25+
import org.springframework.data.redis.core.convert.RedisConverter;
2526
import org.springframework.data.redis.core.mapping.RedisMappingContext;
2627
import org.springframework.util.Assert;
2728
import org.springframework.util.ClassUtils;
@@ -49,11 +50,13 @@ public RedisKeyValueTemplate(RedisKeyValueAdapter adapter, RedisMappingContext m
4950
}
5051

5152
/**
52-
* @return the {@link RedisKeyValueAdapter}.
53+
* Obtain the underlying redis specific {@link org.springframework.data.convert.EntityConverter}.
54+
*
55+
* @return never {@literal null}.
5356
* @since 2.1
5457
*/
55-
public RedisKeyValueAdapter getAdapter() {
56-
return adapter;
58+
public RedisConverter getConverter() {
59+
return adapter.getConverter();
5760
}
5861

5962
/*
@@ -65,7 +68,6 @@ public RedisMappingContext getMappingContext() {
6568
return (RedisMappingContext) super.getMappingContext();
6669
}
6770

68-
6971
/**
7072
* Retrieve entities by resolving their {@literal id}s and converting them into required type. <br />
7173
* The callback provides either a single {@literal id} or an {@link Iterable} of {@literal id}s, used for retrieving
@@ -104,15 +106,13 @@ public List<T> doInRedis(RedisKeyValueAdapter adapter) {
104106
}
105107

106108
Iterable<?> ids = ClassUtils.isAssignable(Iterable.class, callbackResult.getClass())
107-
? (Iterable<?>) callbackResult
108-
: Collections.singleton(callbackResult);
109+
? (Iterable<?>) callbackResult : Collections.singleton(callbackResult);
109110

110111
List<T> result = new ArrayList<>();
111112
for (Object id : ids) {
112113

113114
String idToUse = adapter.getConverter().getConversionService().canConvert(id.getClass(), String.class)
114-
? adapter.getConverter().getConversionService().convert(id, String.class)
115-
: id.toString();
115+
? adapter.getConverter().getConversionService().convert(id, String.class) : id.toString();
116116

117117
findById(idToUse, type).ifPresent(result::add);
118118
}

src/main/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutor.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121
import java.util.Optional;
2222

23+
import org.springframework.dao.IncorrectResultSizeDataAccessException;
2324
import org.springframework.data.domain.Example;
2425
import org.springframework.data.domain.Page;
2526
import org.springframework.data.domain.PageImpl;
@@ -29,7 +30,6 @@
2930
import org.springframework.data.redis.core.RedisKeyValueTemplate;
3031
import org.springframework.data.redis.core.convert.IndexResolver;
3132
import org.springframework.data.redis.core.convert.PathIndexResolver;
32-
import org.springframework.data.redis.core.convert.RedisConverter;
3333
import org.springframework.data.redis.repository.query.ExampleQueryMapper;
3434
import org.springframework.data.redis.repository.query.RedisOperationChain;
3535
import org.springframework.data.repository.core.EntityInformation;
@@ -43,6 +43,7 @@
4343
* methods.
4444
*
4545
* @author Mark Paluch
46+
* @author Christoph Strobl
4647
* @since 2.1
4748
*/
4849
@SuppressWarnings("unchecked")
@@ -62,17 +63,9 @@ public class QueryByExampleRedisExecutor<T> implements QueryByExampleExecutor<T>
6263
public QueryByExampleRedisExecutor(EntityInformation<T, ?> entityInformation,
6364
RedisKeyValueTemplate keyValueTemplate) {
6465

65-
Assert.notNull(entityInformation, "EntityInformation must not be null!");
66-
Assert.notNull(keyValueTemplate, "RedisKeyValueTemplate must not be null!");
67-
68-
this.entityInformation = entityInformation;
69-
this.keyValueTemplate = keyValueTemplate;
70-
71-
RedisConverter converter = keyValueTemplate.getAdapter().getConverter();
72-
IndexResolver indexResolver = converter.getIndexResolver();
73-
74-
this.mapper = new ExampleQueryMapper(keyValueTemplate.getMappingContext(),
75-
indexResolver != null ? indexResolver : new PathIndexResolver(converter.getMappingContext()));
66+
this(entityInformation, keyValueTemplate,
67+
keyValueTemplate.getConverter().getIndexResolver() != null ? keyValueTemplate.getConverter().getIndexResolver()
68+
: new PathIndexResolver(keyValueTemplate.getMappingContext()));
7669
}
7770

7871
/**
@@ -91,8 +84,7 @@ public QueryByExampleRedisExecutor(EntityInformation<T, ?> entityInformation, Re
9184
this.entityInformation = entityInformation;
9285
this.keyValueTemplate = keyValueTemplate;
9386

94-
this.mapper = new ExampleQueryMapper(keyValueTemplate.getMappingContext(),
95-
new PathIndexResolver(keyValueTemplate.getMappingContext()));
87+
this.mapper = new ExampleQueryMapper(keyValueTemplate.getMappingContext(), indexResolver);
9688
}
9789

9890
/*
@@ -102,13 +94,21 @@ public QueryByExampleRedisExecutor(EntityInformation<T, ?> entityInformation, Re
10294
@Override
10395
public <S extends T> Optional<S> findOne(Example<S> example) {
10496

105-
RedisOperationChain operationChain = getQuery(example);
97+
RedisOperationChain operationChain = createQuery(example);
10698

10799
KeyValueQuery<RedisOperationChain> query = new KeyValueQuery<>(operationChain);
108-
Iterable<T> result = keyValueTemplate.find(query.limit(1), entityInformation.getJavaType());
109-
Iterator<T> iterator = result.iterator();
100+
Iterator<T> iterator = keyValueTemplate.find(query.limit(2), entityInformation.getJavaType()).iterator();
101+
102+
Optional result = Optional.empty();
103+
104+
if (iterator.hasNext()) {
105+
result = Optional.of((S) iterator.next());
106+
if (iterator.hasNext()) {
107+
throw new IncorrectResultSizeDataAccessException(1);
108+
}
109+
}
110110

111-
return iterator.hasNext() ? Optional.of((S) iterator.next()) : Optional.empty();
111+
return result;
112112
}
113113

114114
/*
@@ -118,7 +118,7 @@ public <S extends T> Optional<S> findOne(Example<S> example) {
118118
@Override
119119
public <S extends T> Iterable<S> findAll(Example<S> example) {
120120

121-
RedisOperationChain operationChain = getQuery(example);
121+
RedisOperationChain operationChain = createQuery(example);
122122

123123
return (Iterable<S>) keyValueTemplate.find(new KeyValueQuery<>(operationChain), entityInformation.getJavaType());
124124
}
@@ -141,7 +141,7 @@ public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
141141

142142
Assert.notNull(pageable, "Pageable must not be null!");
143143

144-
RedisOperationChain operationChain = getQuery(example);
144+
RedisOperationChain operationChain = createQuery(example);
145145

146146
KeyValueQuery<RedisOperationChain> query = new KeyValueQuery<>(operationChain);
147147
Iterable<T> result = keyValueTemplate.find(
@@ -166,7 +166,7 @@ public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
166166
@Override
167167
public <S extends T> long count(Example<S> example) {
168168

169-
RedisOperationChain operationChain = getQuery(example);
169+
RedisOperationChain operationChain = createQuery(example);
170170

171171
return keyValueTemplate.count(new KeyValueQuery<>(operationChain), entityInformation.getJavaType());
172172
}
@@ -180,7 +180,7 @@ public <S extends T> boolean exists(Example<S> example) {
180180
return count(example) > 0;
181181
}
182182

183-
private <S extends T> RedisOperationChain getQuery(Example<S> example) {
183+
private <S extends T> RedisOperationChain createQuery(Example<S> example) {
184184

185185
Assert.notNull(example, "Example must not be null!");
186186

src/test/java/org/springframework/data/redis/repository/query/ExampleQueryMapperUnitTests.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* Unit tests for {@link ExampleQueryMapper}.
4141
*
4242
* @author Mark Paluch
43+
* @author Christoph Strobl
4344
*/
4445
public class ExampleQueryMapperUnitTests {
4546

@@ -186,7 +187,7 @@ public void shouldApplyPropertyTransformation() {
186187
}
187188

188189
@Data
189-
public static class Person {
190+
static class Person {
190191

191192
@Id String id;
192193

@@ -204,7 +205,8 @@ public static class Person {
204205
Species species;
205206
}
206207

207-
public enum Gender {
208+
enum Gender {
209+
208210
MALE, FEMALE {
209211

210212
@Override
@@ -214,7 +216,7 @@ public String toString() {
214216
}
215217
}
216218

217-
public static class Species {
219+
static class Species {
218220

219221
@Indexed String name;
220222
}

src/test/java/org/springframework/data/redis/repository/support/QueryByExampleRedisExecutorTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.junit.After;
2828
import org.junit.Before;
2929
import org.junit.Test;
30+
import org.springframework.dao.IncorrectResultSizeDataAccessException;
3031
import org.springframework.data.annotation.Id;
3132
import org.springframework.data.domain.Example;
3233
import org.springframework.data.domain.Page;
@@ -48,6 +49,7 @@
4849
* Integration tests for {@link QueryByExampleRedisExecutor}.
4950
*
5051
* @author Mark Paluch
52+
* @author Christoph Strobl
5153
*/
5254
public class QueryByExampleRedisExecutorTests {
5355

@@ -101,6 +103,19 @@ public void shouldFindOneByExample() {
101103
assertThat(result).contains(walt);
102104
}
103105

106+
@Test // DATAREDIS-605
107+
public void shouldThrowExceptionWhenFindOneByExampleReturnsNonUniqueResult() {
108+
109+
QueryByExampleRedisExecutor<Person> executor = new QueryByExampleRedisExecutor<>(getEntityInformation(Person.class),
110+
kvTemplate);
111+
112+
Person person = new Person();
113+
person.setHometown(walt.getHometown());
114+
115+
assertThatThrownBy(() -> executor.findOne(Example.of(person)))
116+
.isInstanceOf(IncorrectResultSizeDataAccessException.class);
117+
}
118+
104119
@Test // DATAREDIS-605
105120
public void shouldNotFindOneByExample() {
106121

0 commit comments

Comments
 (0)