From 415d8a2d2386f4387df95f54ff4a92467d57f7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Basl=C3=A9?= Date: Thu, 18 Aug 2016 12:55:05 +0200 Subject: [PATCH] DATACOUCH-241 - Document mixing SpEL and N1QL placeholders --- .../repository/spel/SpelRepository.java | 6 ++++++ .../repository/spel/SpelRepositoryTests.java | 14 ++++++++++++++ src/main/asciidoc/repository.adoc | 19 +++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepository.java b/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepository.java index cbc9f307..eb2aeeab 100644 --- a/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepository.java +++ b/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepository.java @@ -13,4 +13,10 @@ public interface SpelRepository extends CrudRepository { @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND username = \"#{oneCustomer}\"") List findCustomUsers(); + + //notice how the SpEL syntax #{[0]} considers the first method argument, + //and N1QL placeholder resolution will still consider every method argument as a placeholder value. + //thus N1QL placeholder used is $2, to match criteriaValue + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND #{[0]} = $2") + List findUserWithDynamicCriteria(String criteriaField, Object criteriaValue); } diff --git a/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepositoryTests.java b/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepositoryTests.java index 87e2e3a8..e06075e8 100644 --- a/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepositoryTests.java +++ b/src/integration/java/org/springframework/data/couchbase/repository/spel/SpelRepositoryTests.java @@ -16,11 +16,14 @@ package org.springframework.data.couchbase.repository.spel; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.*; import java.util.List; import com.couchbase.client.java.Bucket; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,4 +69,15 @@ public class SpelRepositoryTests { assertEquals("uname-3", users.get(0).getUsername()); } + @Test + public void testSpelArgumentResolution() { + List usersByName = repository.findUserWithDynamicCriteria("username", "uname-5"); + List usersByAge = repository.findUserWithDynamicCriteria("age", 4); + + assertThat(usersByName, hasSize(1)); + assertThat(usersByAge, hasSize(1)); + assertThat(usersByName.get(0).getKey(), is("testuser-5")); + assertThat(usersByAge.get(0).getKey(), is("testuser-4")); + } + } diff --git a/src/main/asciidoc/repository.adoc b/src/main/asciidoc/repository.adoc index 1c7a4c45..f210d934 100644 --- a/src/main/asciidoc/repository.adoc +++ b/src/main/asciidoc/repository.adoc @@ -147,10 +147,25 @@ IMPORTANT: We recommend that you always use the `selectEntity` SpEL and a WHERE String-based queries support parametrized queries. You can either use positional placeholders like "`$1`", in which case each of the method parameters will map, in order, to `$1`, `$2`, `$3`... Alternatively, you can use named placeholders using the "`$someString`" syntax. Method parameters will be matched with their corresponding placeholder using the parameter's name, which can be overridden by annotating each parameter (except a `Pageable` or `Sort`) with `@Param` (eg. `@Param("someString")`). You cannot mix the two approaches in your query and will get an `IllegalArgumentException` if you do. +Note that you can mix N1QL placeholders and SpEL. N1QL placeholders will still consider all method parameters, so be sure to use the correct index like in the example below: + +.An inline query that mixes SpEL and N1QL placeholders +==== +[source,java] +---- +@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND #{[0]} = $2") +public List findUsersByDynamicCriteria(String criteriaField, Object criteriaValue) +---- +==== + +This allows you to generate queries that would work similarly to eg. `AND name = "someName"` or `AND age = 3`, with a single method declaration. + You can also do single projections in your N1QL queries (provided it selects only one field and returns only one result, usually an aggregation like `COUNT`, `AVG`, `MAX`...). Such projection would have a simple return type like `long`, `boolean` or `String`. This is *NOT* intended for projections to DTOs. -Another example: "`#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND test = $1`", which is equivalent to -`SELECT #{#n1ql.fields} FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} AND test = $1`". +Another example: + +`#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND test = $1` + +is equivalent to + +`SELECT #{#n1ql.fields} FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} AND test = $1` .A practical application of SpEL with Spring Security ****