Skip to content

Commit df8bc9f

Browse files
Native query support for reactive repositories (#42)
o. Added support for running native queries on reactive repos. o. Added new unit tests for reactive native queries.
1 parent 9614e0c commit df8bc9f

File tree

7 files changed

+150
-2
lines changed

7 files changed

+150
-2
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*-
2+
* Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* Licensed under the Universal Permissive License v 1.0 as shown at
5+
* https://oss.oracle.com/licenses/upl/
6+
*/
7+
8+
package com.oracle.nosql.spring.data.repository.query;
9+
10+
import com.oracle.nosql.spring.data.core.ReactiveNosqlOperations;
11+
import com.oracle.nosql.spring.data.core.query.NosqlQuery;
12+
import com.oracle.nosql.spring.data.core.query.StringQuery;
13+
import com.oracle.nosql.spring.data.repository.Query;
14+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
15+
16+
import static com.oracle.nosql.spring.data.repository.query.StringBasedNosqlQuery.hasAmbiguousProjectionFlags;
17+
18+
public class ReactiveStringBasedNosqlQuery extends AbstractReactiveNosqlQuery {
19+
private final String query;
20+
21+
private final boolean isCountQuery;
22+
private final boolean isExistsQuery;
23+
private final boolean isDeleteQuery;
24+
25+
public ReactiveStringBasedNosqlQuery(NosqlQueryMethod method,
26+
ReactiveNosqlOperations operations,
27+
QueryMethodEvaluationContextProvider evaluationContextProvider) {
28+
this(method.getAnnotatedQuery(), method, operations,
29+
evaluationContextProvider);
30+
}
31+
32+
public ReactiveStringBasedNosqlQuery(String query,
33+
NosqlQueryMethod method,
34+
ReactiveNosqlOperations operations,
35+
QueryMethodEvaluationContextProvider evaluationContextProvider) {
36+
super(method, operations);
37+
this.query = query;
38+
if (method.hasAnnotatedQuery()) {
39+
Query queryAnnotation = method.getQueryAnnotation();
40+
isCountQuery = queryAnnotation.count();
41+
isExistsQuery = queryAnnotation.exists();
42+
isDeleteQuery = queryAnnotation.delete();
43+
if (hasAmbiguousProjectionFlags(isCountQuery, isExistsQuery,
44+
isDeleteQuery)) {
45+
throw new IllegalArgumentException(
46+
String.format("Manually defined query for %s cannot be a " +
47+
"count and exists or delete query at the same time!",
48+
method));
49+
}
50+
} else {
51+
isCountQuery = false;
52+
isExistsQuery = false;
53+
isDeleteQuery = false;
54+
}
55+
}
56+
57+
@Override
58+
protected NosqlQuery createQuery(NosqlParameterAccessor accessor) {
59+
return new StringQuery(getQueryMethod(), query, accessor);
60+
}
61+
62+
@Override
63+
protected boolean isDeleteQuery() {
64+
return isDeleteQuery;
65+
}
66+
67+
@Override
68+
protected boolean isExistsQuery() {
69+
return isExistsQuery;
70+
}
71+
72+
@Override
73+
protected boolean isCountQuery() {
74+
return isCountQuery;
75+
}
76+
}

src/main/java/com/oracle/nosql/spring/data/repository/query/StringBasedNosqlQuery.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ protected boolean isCountQuery() {
7979
return isCountQuery;
8080
}
8181

82-
private static boolean hasAmbiguousProjectionFlags(boolean isCountQuery, boolean isExistsQuery,
82+
static boolean hasAmbiguousProjectionFlags(boolean isCountQuery, boolean isExistsQuery,
8383
boolean isDeleteQuery) {
8484
return countBooleanTrueValues(isCountQuery, isExistsQuery, isDeleteQuery) > 1;
8585
}

src/main/java/com/oracle/nosql/spring/data/repository/support/ReactiveNosqlRepositoryFactory.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.oracle.nosql.spring.data.repository.query.NosqlQueryMethod;
1515
import com.oracle.nosql.spring.data.repository.query.PartTreeReactiveNosqlQuery;
1616

17+
import com.oracle.nosql.spring.data.repository.query.ReactiveStringBasedNosqlQuery;
1718
import org.springframework.context.ApplicationContext;
1819
import org.springframework.data.projection.ProjectionFactory;
1920
import org.springframework.data.repository.core.EntityInformation;
@@ -69,13 +70,16 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(
6970
private static class ReactiveNosqlQueryLookupStrategy implements QueryLookupStrategy {
7071
private final ApplicationContext applicationContext;
7172
private final ReactiveNosqlOperations nosqlOperations;
73+
private final QueryMethodEvaluationContextProvider
74+
evaluationContextProvider;
7275

7376
public ReactiveNosqlQueryLookupStrategy(
7477
ApplicationContext applicationContext,
7578
ReactiveNosqlOperations operations,
7679
QueryMethodEvaluationContextProvider provider) {
7780
this.applicationContext = applicationContext;
7881
this.nosqlOperations = operations;
82+
this.evaluationContextProvider = provider;
7983
}
8084

8185
@Override
@@ -87,7 +91,18 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata,
8791

8892
Assert.notNull(queryMethod, "queryMethod must not be null!");
8993
Assert.notNull(nosqlOperations, "dbOperations must not be null!");
90-
return new PartTreeReactiveNosqlQuery(queryMethod, nosqlOperations);
94+
95+
String namedQueryName = queryMethod.getNamedQueryName();
96+
if (namedQueries.hasQuery(namedQueryName)) {
97+
String namedQuery = namedQueries.getQuery(namedQueryName);
98+
return new ReactiveStringBasedNosqlQuery(namedQuery, queryMethod,
99+
nosqlOperations, evaluationContextProvider);
100+
} else if (queryMethod.hasAnnotatedQuery()) {
101+
return new ReactiveStringBasedNosqlQuery(queryMethod, nosqlOperations,
102+
evaluationContextProvider);
103+
} else {
104+
return new PartTreeReactiveNosqlQuery(queryMethod, nosqlOperations);
105+
}
91106
}
92107
}
93108
}

src/test/java/com/oracle/nosql/spring/data/test/composite/ReactiveMachineApp.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,20 @@ public void testIgnoreCase() {
197197
machines.forEach(m -> TestCase.assertEquals(machineCache.get(m.getMachineId())
198198
, m));
199199
}
200+
201+
@Test
202+
public void testNative() {
203+
List<Machine> machines = repo.
204+
findAllByLocationNative().collectList().block();
205+
TestCase.assertEquals(8, machines.size());
206+
machines.forEach(m -> {
207+
TestCase.assertNotNull(m.getMachineId());
208+
TestCase.assertNotNull(m.getMachineId().getName());
209+
TestCase.assertNotNull(m.getMachineId().getVersion());
210+
});
211+
212+
machines = repo.findByMachineIdNameNative("name3").collectList().block();
213+
TestCase.assertEquals(4, machines.size());
214+
machines.forEach(m -> TestCase.assertEquals(machineCache.get(m.getMachineId()), m));
215+
}
200216
}

src/test/java/com/oracle/nosql/spring/data/test/composite/ReactiveMachineRepository.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import com.oracle.nosql.spring.data.repository.Query;
1111
import com.oracle.nosql.spring.data.repository.ReactiveNosqlRepository;
12+
import org.springframework.data.repository.query.Param;
1213
import reactor.core.publisher.Flux;
1314

1415
public interface ReactiveMachineRepository extends ReactiveNosqlRepository<Machine, MachineId> {
@@ -33,4 +34,13 @@ Flux<Machine> findByMachineIdNameOrMachineIdVersion(String name,
3334

3435
//Ignore case
3536
Flux<Machine> findByMachineIdNameIgnoreCase(String name);
37+
38+
//native
39+
@Query("SELECT * FROM Machine m WHERE m" +
40+
".kv_json_.location='newyork'")
41+
Flux<Machine> findAllByLocationNative();
42+
43+
@Query(value = "DECLARE $name STRING; SELECT * FROM Machine AS m " +
44+
"WHERE m.name = $name")
45+
Flux<Machine> findByMachineIdNameNative(@Param("$name") String name);
3646
}

src/test/java/com/oracle/nosql/spring/data/test/reactive/CustomerReactiveRepository.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
*/
77
package com.oracle.nosql.spring.data.test.reactive;
88

9+
import com.oracle.nosql.spring.data.repository.Query;
910
import com.oracle.nosql.spring.data.repository.ReactiveNosqlRepository;
1011
import com.oracle.nosql.spring.data.test.app.Customer;
1112

13+
import org.springframework.data.repository.query.Param;
1214
import reactor.core.publisher.Flux;
1315
import reactor.core.publisher.Mono;
1416

@@ -24,4 +26,20 @@ public interface CustomerReactiveRepository
2426
Mono<Long> countByLastName(String last);
2527

2628
Flux<Customer> deleteByLastName(String last);
29+
30+
@Query("SELECT * FROM Customer AS c WHERE c.kv_json_.firstName = 'John'")
31+
Flux<Customer> findCustomersByFirstNameJohn();
32+
33+
@Query(value = "DECLARE $firstName STRING; SELECT * FROM Customer AS c " +
34+
"WHERE c.kv_json_.firstName = $firstName")
35+
Flux<Customer> findCustomersByFirstName(@Param("$firstName") String firstName);
36+
37+
@Query("DECLARE $firstName STRING; $last STRING; " +
38+
"SELECT * FROM Customer AS c " +
39+
"WHERE c.kv_json_.firstName = $firstName AND " +
40+
"c.kv_json_.lastName = $last")
41+
Flux<Customer> findCustomersWithLastAndFirstNames(
42+
@Param("$last") String paramLast,
43+
@Param("$firstName") String firstName
44+
);
2745
}

src/test/java/com/oracle/nosql/spring/data/test/reactive/TestReactiveApp.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,19 @@ public void testRepo() {
169169
Mono<Long> count = repo.count();
170170
StepVerifier.create(count).expectNext(7L).verifyComplete();
171171

172+
//native queries
173+
List<Customer> johns =
174+
repo.findCustomersByFirstNameJohn().collectList().block();
175+
Assert.assertTrue(johns.contains(c3) && johns.contains(c4));
176+
177+
johns = repo.findCustomersByFirstName("John").collectList().block();
178+
Assert.assertTrue(johns.size() == 2 &&
179+
johns.contains(c3) && johns.contains(c4));
180+
181+
johns = repo.findCustomersWithLastAndFirstNames("Doe", "John").
182+
collectList().block();
183+
Assert.assertTrue(johns.size() == 1 && johns.contains(c4));
184+
172185
// deleteById
173186
repo.deleteById(c7.customerId).subscribe();
174187
exists = repo.existsById(c7.customerId);

0 commit comments

Comments
 (0)