Skip to content

Commit 1605110

Browse files
mp911dechristophstrobl
authored andcommitted
DATAMONGO-2182 - Add Querydsl support for reactive repositories.
We now support execution of Querydsl Predicates using reactive MongoDB repositories. Reactive repositories are only required to add ReactiveQuerydslPredicateExecutor to their declaration to enable Querydsl. public interface PersonRepository extends ReactiveMongoRepository<Person, String>, ReactiveQuerydslPredicateExecutor<Person> { // additional query methods go here } PersonRepository repository = …; QPerson person = new QPerson("person"); Flux<Person> result = repository.findAll(person.address.zipCode.eq("C0123")); Original Pull Request: #635
1 parent 2f60d08 commit 1605110

File tree

7 files changed

+899
-4
lines changed

7 files changed

+899
-4
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.mongodb.repository.support;
1717

18+
import static org.springframework.data.querydsl.QuerydslUtils.*;
19+
1820
import lombok.AccessLevel;
1921
import lombok.RequiredArgsConstructor;
2022

@@ -32,10 +34,13 @@
3234
import org.springframework.data.mongodb.repository.query.ReactivePartTreeMongoQuery;
3335
import org.springframework.data.mongodb.repository.query.ReactiveStringBasedMongoQuery;
3436
import org.springframework.data.projection.ProjectionFactory;
37+
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
3538
import org.springframework.data.repository.core.NamedQueries;
3639
import org.springframework.data.repository.core.RepositoryInformation;
3740
import org.springframework.data.repository.core.RepositoryMetadata;
3841
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
42+
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
43+
import org.springframework.data.repository.core.support.RepositoryFragment;
3944
import org.springframework.data.repository.query.QueryLookupStrategy;
4045
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
4146
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
@@ -81,6 +86,30 @@ protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
8186
return SimpleReactiveMongoRepository.class;
8287
}
8388

89+
/*
90+
* (non-Javadoc)
91+
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryFragments(org.springframework.data.repository.core.RepositoryMetadata)
92+
*/
93+
@Override
94+
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
95+
96+
RepositoryFragments fragments = RepositoryFragments.empty();
97+
98+
boolean isQueryDslRepository = QUERY_DSL_PRESENT
99+
&& ReactiveQuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface());
100+
101+
if (isQueryDslRepository) {
102+
103+
MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType(),
104+
metadata);
105+
106+
fragments = fragments.append(RepositoryFragment.implemented(getTargetRepositoryViaReflection(
107+
ReactiveQuerydslMongoPredicateExecutor.class, entityInformation, operations)));
108+
}
109+
110+
return fragments;
111+
}
112+
84113
/*
85114
* (non-Javadoc)
86115
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryInformation)
@@ -113,12 +142,12 @@ public <T, ID> MongoEntityInformation<T, ID> getEntityInformation(Class<T> domai
113142

114143
@SuppressWarnings("unchecked")
115144
private <T, ID> MongoEntityInformation<T, ID> getEntityInformation(Class<T> domainClass,
116-
@Nullable RepositoryInformation information) {
145+
@Nullable RepositoryMetadata metadata) {
117146

118147
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(domainClass);
119148

120-
return new MappingMongoEntityInformation<T, ID>((MongoPersistentEntity<T>) entity,
121-
information != null ? (Class<ID>) information.getIdType() : null);
149+
return new MappingMongoEntityInformation<>((MongoPersistentEntity<T>) entity,
150+
metadata != null ? (Class<ID>) metadata.getIdType() : null);
122151
}
123152

124153
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/*
2+
* Copyright 2019 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.mongodb.repository.support;
17+
18+
import reactor.core.publisher.Flux;
19+
import reactor.core.publisher.Mono;
20+
21+
import java.util.List;
22+
23+
import org.springframework.data.domain.Sort;
24+
import org.springframework.data.domain.Sort.Order;
25+
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
26+
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
27+
import org.springframework.data.querydsl.EntityPathResolver;
28+
import org.springframework.data.querydsl.QSort;
29+
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
30+
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
31+
import org.springframework.data.querydsl.SimpleEntityPathResolver;
32+
import org.springframework.data.repository.core.EntityInformation;
33+
import org.springframework.util.Assert;
34+
35+
import com.querydsl.core.types.EntityPath;
36+
import com.querydsl.core.types.Expression;
37+
import com.querydsl.core.types.OrderSpecifier;
38+
import com.querydsl.core.types.Predicate;
39+
import com.querydsl.core.types.dsl.PathBuilder;
40+
41+
/**
42+
* MongoDB-specific {@link QuerydslPredicateExecutor} that allows execution {@link Predicate}s in various forms.
43+
*
44+
* @author Mark Paluch
45+
* @since 2.2
46+
*/
47+
public class ReactiveQuerydslMongoPredicateExecutor<T> implements ReactiveQuerydslPredicateExecutor<T> {
48+
49+
private final PathBuilder<T> builder;
50+
private final EntityInformation<T, ?> entityInformation;
51+
private final ReactiveMongoOperations mongoOperations;
52+
53+
/**
54+
* Creates a new {@link ReactiveQuerydslMongoPredicateExecutor} for the given {@link MongoEntityInformation} and
55+
* {@link ReactiveMongoOperations}. Uses the {@link SimpleEntityPathResolver} to create an {@link EntityPath} for the
56+
* given domain class.
57+
*
58+
* @param entityInformation must not be {@literal null}.
59+
* @param mongoOperations must not be {@literal null}.
60+
*/
61+
public ReactiveQuerydslMongoPredicateExecutor(MongoEntityInformation<T, ?> entityInformation,
62+
ReactiveMongoOperations mongoOperations) {
63+
this(entityInformation, mongoOperations, SimpleEntityPathResolver.INSTANCE);
64+
}
65+
66+
/**
67+
* Creates a new {@link ReactiveQuerydslMongoPredicateExecutor} for the given {@link MongoEntityInformation},
68+
* {@link ReactiveMongoOperations} and {@link EntityPathResolver}.
69+
*
70+
* @param entityInformation must not be {@literal null}.
71+
* @param mongoOperations must not be {@literal null}.
72+
* @param resolver must not be {@literal null}.
73+
*/
74+
public ReactiveQuerydslMongoPredicateExecutor(MongoEntityInformation<T, ?> entityInformation,
75+
ReactiveMongoOperations mongoOperations, EntityPathResolver resolver) {
76+
77+
Assert.notNull(resolver, "EntityPathResolver must not be null!");
78+
79+
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
80+
81+
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
82+
this.entityInformation = entityInformation;
83+
this.mongoOperations = mongoOperations;
84+
}
85+
86+
/*
87+
* (non-Javadoc)
88+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#findOne(com.querydsl.core.types.Predicate)
89+
*/
90+
@Override
91+
public Mono<T> findOne(Predicate predicate) {
92+
93+
Assert.notNull(predicate, "Predicate must not be null!");
94+
95+
return createQueryFor(predicate).fetchOne();
96+
}
97+
98+
/*
99+
* (non-Javadoc)
100+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate)
101+
*/
102+
@Override
103+
public Flux<T> findAll(Predicate predicate) {
104+
105+
Assert.notNull(predicate, "Predicate must not be null!");
106+
107+
return createQueryFor(predicate).fetch();
108+
}
109+
110+
/*
111+
* (non-Javadoc)
112+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[])
113+
*/
114+
@Override
115+
public Flux<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
116+
117+
Assert.notNull(predicate, "Predicate must not be null!");
118+
Assert.notNull(orders, "Order specifiers must not be null!");
119+
120+
return createQueryFor(predicate).orderBy(orders).fetch();
121+
}
122+
123+
/*
124+
* (non-Javadoc)
125+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort)
126+
*/
127+
@Override
128+
public Flux<T> findAll(Predicate predicate, Sort sort) {
129+
130+
Assert.notNull(predicate, "Predicate must not be null!");
131+
Assert.notNull(sort, "Sort must not be null!");
132+
133+
return applySorting(createQueryFor(predicate), sort).fetch();
134+
}
135+
136+
/*
137+
* (non-Javadoc)
138+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[])
139+
*/
140+
@Override
141+
public Flux<T> findAll(OrderSpecifier<?>... orders) {
142+
143+
Assert.notNull(orders, "Order specifiers must not be null!");
144+
145+
return createQuery().orderBy(orders).fetch();
146+
}
147+
148+
/*
149+
* (non-Javadoc)
150+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#count(com.querydsl.core.types.Predicate)
151+
*/
152+
@Override
153+
public Mono<Long> count(Predicate predicate) {
154+
155+
Assert.notNull(predicate, "Predicate must not be null!");
156+
157+
return createQueryFor(predicate).fetchCount();
158+
}
159+
160+
/*
161+
* (non-Javadoc)
162+
* @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#exists(com.querydsl.core.types.Predicate)
163+
*/
164+
@Override
165+
public Mono<Boolean> exists(Predicate predicate) {
166+
167+
Assert.notNull(predicate, "Predicate must not be null!");
168+
169+
return createQueryFor(predicate).fetchCount().map(it -> it != 0);
170+
}
171+
172+
/**
173+
* Creates a {@link ReactiveSpringDataMongodbQuery} for the given {@link Predicate}.
174+
*
175+
* @param predicate
176+
* @return
177+
*/
178+
private ReactiveSpringDataMongodbQuery<T> createQueryFor(Predicate predicate) {
179+
return createQuery().where(predicate);
180+
}
181+
182+
/**
183+
* Creates a {@link ReactiveSpringDataMongodbQuery}.
184+
*
185+
* @return
186+
*/
187+
private ReactiveSpringDataMongodbQuery<T> createQuery() {
188+
SpringDataMongodbSerializer serializer = new SpringDataMongodbSerializer(mongoOperations.getConverter());
189+
190+
Class<T> javaType = entityInformation.getJavaType();
191+
return new ReactiveSpringDataMongodbQuery<>(serializer, mongoOperations, javaType,
192+
mongoOperations.getCollectionName(javaType));
193+
}
194+
195+
/**
196+
* Applies the given {@link Sort} to the given {@link ReactiveSpringDataMongodbQuery}.
197+
*
198+
* @param query
199+
* @param sort
200+
* @return
201+
*/
202+
private ReactiveSpringDataMongodbQuery<T> applySorting(ReactiveSpringDataMongodbQuery<T> query, Sort sort) {
203+
204+
// TODO: find better solution than instanceof check
205+
if (sort instanceof QSort) {
206+
207+
List<OrderSpecifier<?>> orderSpecifiers = ((QSort) sort).getOrderSpecifiers();
208+
query.orderBy(orderSpecifiers.toArray(new OrderSpecifier<?>[orderSpecifiers.size()]));
209+
210+
return query;
211+
}
212+
213+
sort.stream().map(this::toOrder).forEach(query::orderBy);
214+
215+
return query;
216+
}
217+
218+
/**
219+
* Transforms a plain {@link Order} into a Querydsl specific {@link OrderSpecifier}.
220+
*
221+
* @param order
222+
* @return
223+
*/
224+
@SuppressWarnings({ "rawtypes", "unchecked" })
225+
private OrderSpecifier<?> toOrder(Order order) {
226+
227+
Expression<Object> property = builder.get(order.getProperty());
228+
229+
return new OrderSpecifier(
230+
order.isAscending() ? com.querydsl.core.types.Order.ASC : com.querydsl.core.types.Order.DESC, property);
231+
}
232+
}

0 commit comments

Comments
 (0)