From 9840acd2c6ecfe66cf907a1667df2de54e4c7fec Mon Sep 17 00:00:00 2001 From: Subhashni Balakrishnan Date: Tue, 18 Sep 2018 21:57:19 -0700 Subject: [PATCH] DATACOUCH-384 - Apply converters to string based query parameters Apply the converters registered to serialize the positional and named parameters to match the database representation Original Pull Request: #172. --- .../N1qlCouchbaseRepositoryTests.java | 18 +++++++++++-- .../couchbase/repository/PartyRepository.java | 3 +++ .../ReactiveN1qlCouchbaseRepositoryTests.java | 21 +++++++++++++-- .../repository/ReactivePartyRepository.java | 2 ++ .../query/ReactiveStringN1qlBasedQuery.java | 2 +- .../query/StringBasedN1qlQueryParser.java | 9 ++++--- .../query/StringN1qlBasedQuery.java | 2 +- .../query/StringN1QlBasedQueryTest.java | 27 +++++++++++++------ 8 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java b/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java index 988a21fb..7c73b587 100644 --- a/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java +++ b/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java @@ -40,6 +40,8 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; +import java.util.Calendar; +import java.util.Date; import java.util.List; /** @@ -127,7 +129,7 @@ public class N1qlCouchbaseRepositoryTests { Pageable pageable = new PageRequest(0, 8); Page page1 = repository.findAll(pageable); - assertEquals(16, page1.getTotalElements()); //12 generated parties + 4 specifically crafted party + assertEquals(17, page1.getTotalElements()); //12 generated parties + 5 specifically crafted party assertEquals(8, page1.getNumberOfElements()); } @@ -136,7 +138,7 @@ public class N1qlCouchbaseRepositoryTests { Pageable pageable = new PageRequest(0, 8, Sort.Direction.DESC, "attendees"); Page page1 = repository.findAll(pageable); - assertEquals(16, page1.getTotalElements()); //12 generated parties + 4 specifically crafted party + assertEquals(17, page1.getTotalElements()); //12 generated parties + 5 specifically crafted party assertEquals(8, page1.getNumberOfElements()); List parties = page1.getContent(); @@ -195,4 +197,16 @@ public class N1qlCouchbaseRepositoryTests { assertTrue(partyList.size() == 1); } + @Test + public void testSpelDateConvertion() { + final String key = "testSpelDateConvertion"; + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2018, Calendar.SEPTEMBER, 10); + Date date = cal.getTime(); + partyRepository.save(new Party(key, "", "", date, 0, null)); + List partyList = partyRepository.getByEventDate(date); + assertTrue(partyList.size() == 1); + assertEquals("Key mismatch", partyList.get(0).getKey(), key); + } } diff --git a/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java b/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java index 8c0d9beb..355346d1 100644 --- a/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java +++ b/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java @@ -89,4 +89,7 @@ public interface PartyRepository extends CouchbaseRepository { List findByDescriptionOrName(String description, String name); List removeByDescriptionOrName(String description, String name); + + @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and eventDate = $1") + List getByEventDate(Date eventDate); } diff --git a/src/integration/java/org/springframework/data/couchbase/repository/ReactiveN1qlCouchbaseRepositoryTests.java b/src/integration/java/org/springframework/data/couchbase/repository/ReactiveN1qlCouchbaseRepositoryTests.java index 05459472..8f7c10f0 100644 --- a/src/integration/java/org/springframework/data/couchbase/repository/ReactiveN1qlCouchbaseRepositoryTests.java +++ b/src/integration/java/org/springframework/data/couchbase/repository/ReactiveN1qlCouchbaseRepositoryTests.java @@ -18,6 +18,10 @@ package org.springframework.data.couchbase.repository; import static org.junit.Assert.*; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -115,12 +119,25 @@ public class ReactiveN1qlCouchbaseRepositoryTests { @Test public void testCustomSpelCountQuery() { long count = partyRepository.countCustom().block(); - assertEquals("Test N1QL Spel based query", 15, count); + assertEquals("Test N1QL Spel based query", 17, count); } @Test public void testPartTreeQuery() { long count = partyRepository.countAllByDescriptionNotNull().block(); - assertEquals("Test N1QL part tree based query", 15, count); + assertEquals("Test N1QL part tree based query", 17, count); } + + @Test + public void testSpelDateConvertion() { + final String key = "testReactiveSpelDateConvertion"; + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2018, Calendar.SEPTEMBER, 15); + Date date = cal.getTime(); + partyRepository.save(new Party(key, "", "", date, 0, null)).block(); + List partyList = partyRepository.getByEventDate(date).collectList().block(); + assertTrue(partyList.size() == 1); + assertEquals("Key mismatch", partyList.get(0).getKey(), key); + } } diff --git a/src/integration/java/org/springframework/data/couchbase/repository/ReactivePartyRepository.java b/src/integration/java/org/springframework/data/couchbase/repository/ReactivePartyRepository.java index 4a560506..7138e075 100644 --- a/src/integration/java/org/springframework/data/couchbase/repository/ReactivePartyRepository.java +++ b/src/integration/java/org/springframework/data/couchbase/repository/ReactivePartyRepository.java @@ -57,4 +57,6 @@ public interface ReactivePartyRepository extends ReactiveCouchbaseRepository findByDescriptionOrName(String description, String name); + @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and eventDate = $1") + Flux getByEventDate(Date eventDate); } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringN1qlBasedQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringN1qlBasedQuery.java index d3b269f4..98b5ab57 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringN1qlBasedQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringN1qlBasedQuery.java @@ -62,7 +62,7 @@ public class ReactiveStringN1qlBasedQuery extends ReactiveAbstractN1qlBasedQuery super(queryMethod, couchbaseOperations); this.queryParser = new StringBasedN1qlQueryParser(statement, queryMethod, - getCouchbaseOperations().getCouchbaseBucket().name(), getTypeField(), getTypeValue()); + getCouchbaseOperations().getCouchbaseBucket().name(), getCouchbaseOperations().getConverter(), getTypeField(), getTypeValue()); this.parser = spelParser; this.evaluationContextProvider = evaluationContextProvider; } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java index bcb4a816..206dbed3 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java @@ -29,6 +29,7 @@ import com.couchbase.client.java.document.json.JsonArray; import com.couchbase.client.java.document.json.JsonObject; import com.couchbase.client.java.document.json.JsonValue; import org.slf4j.LoggerFactory; +import org.springframework.data.couchbase.core.convert.CouchbaseConverter; import org.springframework.data.couchbase.repository.query.support.N1qlUtils; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.ParameterAccessor; @@ -110,10 +111,12 @@ public class StringBasedN1qlQueryParser { private final PlaceholderType placeHolderType; private final N1qlSpelValues statementContext; private final N1qlSpelValues countContext; + private final CouchbaseConverter couchbaseConverter; public StringBasedN1qlQueryParser(String statement, QueryMethod queryMethod, String bucketName, + CouchbaseConverter couchbaseConverter, String typeField, Class typeValue) { this.statement = statement; @@ -121,7 +124,7 @@ public class StringBasedN1qlQueryParser { this.placeHolderType = checkPlaceholders(statement); this.statementContext = createN1qlSpelValues(bucketName, typeField, typeValue, false); this.countContext = createN1qlSpelValues(bucketName, typeField, typeValue, true); - + this.couchbaseConverter = couchbaseConverter; } public static N1qlSpelValues createN1qlSpelValues(String bucketName, String typeField, Class typeValue, boolean isCount) { @@ -211,7 +214,7 @@ public class StringBasedN1qlQueryParser { private JsonValue getPositionalPlaceholderValues(ParameterAccessor accessor) { JsonArray posValues = JsonArray.create(); for (Parameter parameter : this.queryMethod.getParameters().getBindableParameters()) { - posValues.add(accessor.getBindableValue(parameter.getIndex())); + posValues.add(this.couchbaseConverter.convertForWriteIfNeeded(accessor.getBindableValue(parameter.getIndex()))); } return posValues; } @@ -221,7 +224,7 @@ public class StringBasedN1qlQueryParser { for (Parameter parameter : this.queryMethod.getParameters().getBindableParameters()) { String placeholder = parameter.getPlaceholder(); - Object value = accessor.getBindableValue(parameter.getIndex()); + Object value = this.couchbaseConverter.convertForWriteIfNeeded(accessor.getBindableValue(parameter.getIndex())); if (placeholder != null && placeholder.charAt(0) == ':') { placeholder = placeholder.replaceFirst(":", ""); diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java index c0971187..019651bd 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java @@ -68,7 +68,7 @@ public class StringN1qlBasedQuery extends AbstractN1qlBasedQuery { SpelExpressionParser spelParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { super(queryMethod, couchbaseOperations); this.queryParser = new StringBasedN1qlQueryParser(statement, queryMethod, - getCouchbaseOperations().getCouchbaseBucket().name(), getTypeField(), getTypeValue()); + getCouchbaseOperations().getCouchbaseBucket().name(), getCouchbaseOperations().getConverter(), getTypeField(), getTypeValue()); this.parser = spelParser; this.evaluationContextProvider = evaluationContextProvider; } diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java b/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java index ab6a9eb5..eeadfd97 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java +++ b/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java @@ -1,9 +1,13 @@ package org.springframework.data.couchbase.repository.query; import static org.junit.Assert.*; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.eq; import static org.springframework.data.couchbase.repository.query.StringBasedN1qlQueryParser.*; - +import org.junit.Before; import org.junit.Test; +import org.springframework.data.couchbase.core.convert.CouchbaseConverter; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -15,11 +19,18 @@ public class StringN1QlBasedQueryTest { private static String spel(String expression) { return "#{" + expression + "}"; } + CouchbaseConverter couchbaseConverter; + + @Before + public void setup() { + this.couchbaseConverter = mock(CouchbaseConverter.class); + when(couchbaseConverter.convertForWriteIfNeeded(eq("value"))).thenReturn("value"); + } @Test public void testReplaceAllFullSelectPlaceholder() throws Exception { String statement = spel(SPEL_SELECT_FROM_CLAUSE) + " where " + spel(SPEL_SELECT_FROM_CLAUSE); - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "_class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, false); assertEquals("SELECT META(`B`).id AS _ID, META(`B`).cas AS _CAS, `B`.* FROM `B` where " @@ -29,7 +40,7 @@ public class StringN1QlBasedQueryTest { @Test public void testReplaceAllBucketPlaceholder() throws Exception { String statement = "SELECT * FROM " + spel(SPEL_BUCKET) + " WHERE " + spel(SPEL_BUCKET) + ".test = 1"; - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "_class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, false); assertEquals("SELECT * FROM `B` WHERE `B`.test = 1", parsed); @@ -38,7 +49,7 @@ public class StringN1QlBasedQueryTest { @Test public void testReplaceAllEntityPlaceholder() throws Exception { String statement = "SELECT " + spel(SPEL_ENTITY) + " FROM a where a.test = 1 and " + spel(SPEL_ENTITY); - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "_class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, false); assertEquals("SELECT META(`B`).id AS _ID, META(`B`).cas AS _CAS FROM a where a.test = 1 and " @@ -48,7 +59,7 @@ public class StringN1QlBasedQueryTest { @Test public void testReplaceTypePlaceholder() throws Exception { String statement = "SELECT " + spel(SPEL_ENTITY) + " FROM a WHERE a.test = 1 AND " + spel(SPEL_FILTER); - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "@class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "@class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, false); assertEquals("SELECT META(`B`).id AS _ID, META(`B`).cas AS _CAS FROM a WHERE a.test = 1 AND `@class` = " @@ -58,7 +69,7 @@ public class StringN1QlBasedQueryTest { @Test public void testReplaceSelectFromPlaceholderWithCountIfCountTrue() { String statement = spel(SPEL_SELECT_FROM_CLAUSE) + " WHERE true"; - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "_class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, true); assertEquals("SELECT COUNT(*) AS " + CountFragment.COUNT_ALIAS + " FROM `B` WHERE true", parsed); @@ -67,7 +78,7 @@ public class StringN1QlBasedQueryTest { @Test public void testDeletePlaceholder() throws Exception { String statement = spel(SPEL_DELETE) + " WHERE test = 1 AND " + spel(SPEL_FILTER); - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "_class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, true); assertEquals("DELETE FROM `B` WHERE test = 1 AND `_class` = " @@ -77,7 +88,7 @@ public class StringN1QlBasedQueryTest { @Test public void testReturningPlaceholder() throws Exception { String statement = spel(SPEL_DELETE) + " WHERE test = 1 AND " + spel(SPEL_FILTER) + spel(SPEL_RETURNING) ; - String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class) + String parsed = new StringBasedN1qlQueryParser(statement, null, "B", this.couchbaseConverter, "_class", String.class) .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, true); assertEquals("DELETE FROM `B` WHERE test = 1 AND `_class` = "