DATACOUCH-148 - Allow to configure repository queries consistency globally.

The consistency to apply on generated view queries (Stale) and N1QL queries (ScanConsistency) can now be chosen via the configuration, through a more abstract Consistency enumeration.

It is accessed from the CouchbaseOperations interface but is used in the repository only. In xml, the consistency attribute is on the couchbase:template element (string value of the enum to be passed in).

Documentation has been amended to describe this feature.
This commit is contained in:
Simon Baslé
2015-07-27 17:17:21 +02:00
parent 508f190270
commit f543e2b037
15 changed files with 343 additions and 100 deletions

View File

@@ -13,61 +13,67 @@ import org.springframework.core.env.Environment;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.WriteResultChecking;
import org.springframework.data.couchbase.core.view.Consistency;
@Configuration
public class IntegrationTestApplicationConfig extends AbstractCouchbaseConfiguration {
@Autowired
private Environment springEnv;
@Autowired
private Environment springEnv;
@Bean
public String couchbaseAdminUser() {
return springEnv.getProperty("couchbase.adminUser", "Administrator");
}
@Bean
public String couchbaseAdminUser() {
return springEnv.getProperty("couchbase.adminUser", "Administrator");
}
@Bean
public String couchbaseAdminPassword() {
return springEnv.getProperty("couchbase.adminUser", "password");
}
@Bean
public String couchbaseAdminPassword() {
return springEnv.getProperty("couchbase.adminUser", "password");
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList(springEnv.getProperty("couchbase.host", "127.0.0.1"));
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList(springEnv.getProperty("couchbase.host", "127.0.0.1"));
}
@Override
protected String getBucketName() {
return springEnv.getProperty("couchbase.bucket", "default");
}
@Override
protected String getBucketName() {
return springEnv.getProperty("couchbase.bucket", "default");
}
@Override
protected String getBucketPassword() {
return springEnv.getProperty("couchbase.password", "");
}
@Override
protected String getBucketPassword() {
return springEnv.getProperty("couchbase.password", "");
}
//TODO maybe create the bucket if doesn't exist
//TODO maybe create the bucket if doesn't exist
@Override
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.builder()
.connectTimeout(10000)
.kvTimeout(10000)
.queryTimeout(10000)
.viewTimeout(10000)
.build();
}
@Override
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.builder()
.connectTimeout(10000)
.kvTimeout(10000)
.queryTimeout(10000)
.viewTimeout(10000)
.build();
}
@Override
public CouchbaseTemplate couchbaseTemplate() throws Exception {
CouchbaseTemplate template = super.couchbaseTemplate();
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
@Override
public CouchbaseTemplate couchbaseTemplate() throws Exception {
CouchbaseTemplate template = super.couchbaseTemplate();
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
//change the name of the field that will hold type information
@Override
public String typeKey() {
return "javaClass";
}
@Override
public String typeKey() {
return "javaClass";
}
@Override
protected Consistency getDefaultConsistency() {
return Consistency.READ_YOUR_OWN_WRITES;
}
}

View File

@@ -30,65 +30,67 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.view.Consistency;
import org.springframework.data.couchbase.repository.User;
/**
* @author Michael Nitschinger
* @author Simon Baslé
*/
public class CouchbaseTemplateParserIntegrationTests {
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
reader = new XmlBeanDefinitionReader(factory);
}
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
reader = new XmlBeanDefinitionReader(factory);
}
@Test
public void readsCouchbaseTemplateAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-bean.xml"));
@Test
public void readsCouchbaseTemplateAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition("couchbaseTemplate");
assertEquals(2, definition.getConstructorArgumentValues().getArgumentCount());
BeanDefinition definition = factory.getBeanDefinition("couchbaseTemplate");
assertEquals(2, definition.getConstructorArgumentValues().getArgumentCount());
factory.getBean("couchbaseTemplate");
}
factory.getBean("couchbaseTemplate");
}
@Test
public void readsCouchbaseTemplateWithTranslationServiceAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-with-translation-service-bean.xml"));
@Test
public void readsCouchbaseTemplateWithTranslationServiceAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-with-translation-service-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition("couchbaseTemplate");
assertEquals(3, definition.getConstructorArgumentValues().getArgumentCount());
BeanDefinition definition = factory.getBeanDefinition("couchbaseTemplate");
assertEquals(3, definition.getConstructorArgumentValues().getArgumentCount());
factory.getBean("couchbaseTemplate");
}
factory.getBean("couchbaseTemplate");
}
/**
* Test case for DATACOUCH-47.
*/
@Test
public void allowsMultipleBuckets() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-multi-bucket-bean.xml"));
/**
* Test case for DATACOUCH-47.
*/
@Test
public void allowsMultipleBuckets() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-multi-bucket-bean.xml"));
factory.getBean("cb-template-first");
factory.getBean("cb-template-second");
}
factory.getBean("cb-template-first");
factory.getBean("cb-template-second");
}
/**
* Test case for DATACOUCH-134 in xml: field for storing type information can be renamed.
*/
@Test
public void testTypeFieldCanBeChosen() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-typekey.xml"));
CouchbaseTemplate template = factory.getBean("couchbaseTemplate", CouchbaseTemplate.class);
/**
* Test case for DATACOUCH-134 in xml: field for storing type information can be renamed.
*/
@Test
public void testTypeFieldCanBeChosen() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-typekey.xml"));
CouchbaseTemplate template = factory.getBean("couchbaseTemplate", CouchbaseTemplate.class);
assertTrue(template.getConverter() instanceof MappingCouchbaseConverter);
MappingCouchbaseConverter converter = ((MappingCouchbaseConverter) template.getConverter());
assertTrue(template.getConverter() instanceof MappingCouchbaseConverter);
MappingCouchbaseConverter converter = ((MappingCouchbaseConverter) template.getConverter());
assertEquals("javaXmlClass", converter.getTypeKey());
assertEquals("javaXmlClass", converter.getTypeKey());
User u = new User("specialSaveUser", "John Locke");
template.save(u);
@@ -99,5 +101,37 @@ public class CouchbaseTemplateParserIntegrationTests {
assertNull(uJson.get(MappingCouchbaseConverter.TYPEKEY_DEFAULT));
assertEquals("org.springframework.data.couchbase.repository.User", uJson.getString("javaXmlClass"));
assertEquals("John Locke", uJson.getString("username"));
}
}
/**
* Test case for DATACOUCH-148, choosing an alternative default for view/N1QL staleness.
*/
@Test
public void shouldParseCustomStaleness() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-consistency.xml"));
CouchbaseTemplate template = factory.getBean("template", CouchbaseTemplate.class);
assertEquals(Consistency.READ_YOUR_OWN_WRITES, template.getDefaultConsistency());
assertNotEquals(Consistency.DEFAULT_CONSISTENCY, template.getDefaultConsistency());
}
/**
* Test case for DATACOUCH-148, choosing an unknown value for view/N1QL staleness.
*/
@Test
public void shouldIgnoreBadCustomStaleness() {
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-consistency.xml"));
CouchbaseTemplate template = factory.getBean("templateBad", CouchbaseTemplate.class);
assertEquals(Consistency.DEFAULT_CONSISTENCY, template.getDefaultConsistency());
}
@Test
public void shouldHaveDefaultsForStaleness() {
//use another resource where staleness isn't customized
reader.loadBeanDefinitions(new ClassPathResource("configurations/couchbase-template-with-translation-service-bean.xml"));
CouchbaseTemplate template = factory.getBean("couchbaseTemplate", CouchbaseTemplate.class);
assertEquals(Consistency.DEFAULT_CONSISTENCY, template.getDefaultConsistency());
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:couchbase="http://www.springframework.org/schema/data/couchbase"
xsi:schemaLocation="http://www.springframework.org/schema/data/couchbase http://www.springframework.org/schema/data/couchbase/spring-couchbase.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<couchbase:env/>
<couchbase:cluster/>
<couchbase:clusterInfo/>
<couchbase:bucket/>
<couchbase:template id="template" consistency="READ_YOUR_OWN_WRITES"/>
<couchbase:template id="templateBad" consistency="BadConsistency"/>
</beans>

View File

@@ -85,10 +85,12 @@ Depending on how your environment is set up, the configuration will be automatic
Additionally, the SDK environment can be tuned by overriding the `getEnvironment()` method to return a properly tuned `CouchbaseEnvironment`.
While not immediately obvious, much more things can be customized and overridden as custom beans from this configuration (for example repositories, validation and custom converters).
While not immediately obvious, much more things can be customized and overridden as custom beans from this configuration (for example repositories, query consistency, validation and custom converters).
TIP: If you use `SyncGateway` and `CouchbaseMobile`, you may run into problem with fields prefixed by `_`. Since Spring Data Couchbase by default stores the type information as a `_class` attribute this can be problematic. Override `typeKey()` (for example to return `MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE`) to change the name of said attribute.
TIP: For generated queries, if you want strong consistency (at the expense of performance), you can override `getDefaultConsistency()` and return `Consistency.READ_YOUR_OWN_WRITES`.
[[configuration-xml]]
== XML-based Configuration
@@ -121,5 +123,7 @@ The library provides a custom namespace that you can use in your XML configurati
====
This code is equivalent to the java configuration approach shown above. You can customize the SDK `CouchbaseEnvironment` via the `<couchbase:env/>` tag, that supports most tuning parameters as attributes. It is also possible to configure templates and repositories, which is shown in the appropriate sections.
IMPORTANT: The XML configuration **must** include the `clusterInfo` credentials, in order to be able to detect N1QL feature.
If you start your application, you should see Couchbase INFO level logging in the logs, indicating that the underlying Couchbase Java SDK is connecting to the database. If any errors are reported, make sure that the given credentials and host information are correct.

View File

@@ -264,7 +264,7 @@ List<User> users = repo.findByFirstnameStartingWith("Mich");
On all these derived custom finder methods, you have to use the `@View` annotation with at least the view name specified (and you can also override the design document name, otherwise determined by convention).
IMPORTANT: For any other usage and customization of the `ViewQuery` that goes beyond that, recommended approach is to provide an implementation that uses the underlying template, like described in <<repositories.single-repository-behaviour>>.
Please keep in mind that one typical parameter that you cannot tune using both view-based approaches is the `Stale` mechanism. You would need to do the implementation yourself in order to tune the behavior through the `setStale()` method on the `ViewQuery` object. For more details on behavior, please consult the Couchbase Server and Java SDK documentation directly.
For more details on behavior, please consult the Couchbase Server and Java SDK documentation directly.
For view-based query derivation, here are the supported keywords (A and B are method parameters in this table):
@@ -282,4 +282,28 @@ For view-based query derivation, here are the supported keywords (A and B are me
TIP: Note that the `reduce function` (not always a count) will be activated by prefixing with `count` and that <<repositories.limit-query-result>> is also supported.
WARNING: Compound keys are not supported, and neither are Or composition, Ignore Case and Order By. You have to include a valid entity property in the naming of your method.
WARNING: Compound keys are not supported, and neither are Or composition, Ignore Case and Order By. You have to include a valid entity property in the naming of your method.
[[couchbase.repository.consistency]]
== Querying with consistency
One aspect that is often needed and doesn't have a direct equivalent in the Spring Data query derivation mechanism is
`query consistency`. In both view-based queries and N1QL, you have this concept that the secondary index can return stale
data, because the latest version hasn't been indexed yet. This gives the best performance at the expense of consistency.
If one wants to have stronger consistency, there are two possibilities described in the next sections.
=== Configure it on a global level
The global consistency used by generated queries (views and N1QL) is defined at the template level,
using `Consistency` enumeration (like `Consistency.READ_YOUR_OWN_WRITE`):
- in xml, this is done via the `consistency` attribute on `<couchbase:template>`.
- in javaConfig, this is done by overriding the `getDefaultConsistency()` method.
=== Provide an implementation
Provide the implementation and directly use `queryView` and `queryN1QL` methods on the template with a specific consistency
(see <<repositories.single-repository-behaviour>>).
- one can specify the consistency on those via their respective query classes, according to the Couchbase Java SDK documentation.
- for example for views `ViewQuery.stale(Stale.FALSE)`
- for example for N1QL `Query.simple("SELECT * FROM default", QueryParams.build().consistency(ScanConsistency.REQUEST_PLUS));`

View File

@@ -27,6 +27,8 @@ import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.view.ViewQuery;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
@@ -41,6 +43,7 @@ import org.springframework.data.couchbase.core.convert.translation.JacksonTransl
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.view.Consistency;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
@@ -136,11 +139,16 @@ public abstract class AbstractCouchbaseConfiguration {
/**
* Creates a {@link CouchbaseTemplate}.
*
* This uses {@link #couchbaseClusterInfo()}, {@link #couchbaseClient()}, {@link #mappingCouchbaseConverter()},
* , {@link #translationService()} and {@link #getDefaultConsistency()} for construction.
*
* @throws Exception on Bean construction failure.
*/
@Bean(name = BeanNames.COUCHBASE_TEMPLATE)
public CouchbaseTemplate couchbaseTemplate() throws Exception {
return new CouchbaseTemplate(couchbaseClusterInfo(), couchbaseClient(), mappingCouchbaseConverter(), translationService());
CouchbaseTemplate template = new CouchbaseTemplate(couchbaseClusterInfo(), couchbaseClient(), mappingCouchbaseConverter(), translationService());
template.setDefaultConsistency(getDefaultConsistency());
return template;
}
/**
@@ -257,4 +265,14 @@ public abstract class AbstractCouchbaseConfiguration {
protected FieldNamingStrategy fieldNamingStrategy() {
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy() : PropertyNameFieldNamingStrategy.INSTANCE;
}
}
/**
* Configures the default consistency for generated {@link ViewQuery view queries}
* and {@link Query N1QL queries} in repositories.
*
* @return the {@link Consistency consistency} to apply by default on generated queries.
*/
protected Consistency getDefaultConsistency() {
return Consistency.DEFAULT_CONSISTENCY;
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.data.couchbase.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -23,6 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.view.Consistency;
import org.springframework.util.StringUtils;
/**
@@ -34,6 +37,8 @@ import org.springframework.util.StringUtils;
*/
public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser {
private static final Logger LOGGER = LoggerFactory.getLogger(CouchbaseTemplateParser.class);
/**
* Resolve the bean ID and assign a default if not set.
*
@@ -82,6 +87,17 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
if (StringUtils.hasText(translationServiceRef)) {
bean.addConstructorArgReference(translationServiceRef);
}
String consistencyValue = element.getAttribute("consistency");
if (consistencyValue != null) {
try {
Consistency consistency = Consistency.valueOf(consistencyValue);
bean.addPropertyValue("defaultConsistency", consistency);
} catch (IllegalArgumentException e) {
//bad consistency, leave default and log
LOGGER.warn("Parsed bad consistency value " + consistencyValue + " in xml template configuration, using default");
}
}
}
}

View File

@@ -33,6 +33,7 @@ import com.couchbase.client.java.view.ViewResult;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.view.Consistency;
/**
* Defines common operations on the Couchbase data source, most commonly implemented by {@link CouchbaseTemplate}.
@@ -341,4 +342,12 @@ public interface CouchbaseOperations {
*/
CouchbaseConverter getConverter();
/**
* Returns the {@link Consistency consistency} parameter to be used by default for generated queries (views and N1QL)
* in repositories. Defaults to {@link Consistency#DEFAULT_CONSISTENCY}.
*
* @return the consistency to use for generated repository queries.
*/
Consistency getDefaultConsistency();
}

View File

@@ -36,8 +36,10 @@ import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.error.CASMismatchException;
import com.couchbase.client.java.error.TranscodingException;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.query.QueryParams;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.client.java.query.QueryRow;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import com.couchbase.client.java.util.features.CouchbaseFeature;
import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
@@ -65,6 +67,7 @@ import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeDeleteEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
import org.springframework.data.couchbase.core.view.Consistency;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
@@ -100,6 +103,9 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
//default value is in case the template isn't constructed through configuration mechanisms that use the setter.
private Consistency configuredConsistency = Consistency.DEFAULT_CONSISTENCY;
public CouchbaseTemplate(final ClusterInfo clusterInfo, final Bucket client) {
this(clusterInfo, client, null, null);
}
@@ -576,4 +582,13 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
public CouchbaseConverter getConverter() {
return this.converter;
}
@Override
public Consistency getDefaultConsistency() {
return configuredConsistency;
}
public void setDefaultConsistency(Consistency consistency) {
this.configuredConsistency = consistency;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2012-2015 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.view;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import com.couchbase.client.java.view.Stale;
/**
* Enumeration of different consistency configurations to be used by the queries generated by the
* framework.
*
* Each consistency can be translated to a {@link Stale} (for the {@link #viewConsistency() views})
* and {@link ScanConsistency} (for the {@link #n1qlConsistency() N1QL queries}).
*
* @author Simon Baslé
*/
public enum Consistency {
/** READ_YOUR_OWN_WRITES is {@link Stale#FALSE} and {@link ScanConsistency#STATEMENT_PLUS} */
READ_YOUR_OWN_WRITES(Stale.FALSE, ScanConsistency.STATEMENT_PLUS),
/** STRONGLY_CONSISTENT is {@link Stale#FALSE} and {@link ScanConsistency#REQUEST_PLUS} */
STRONGLY_CONSISTENT(Stale.FALSE, ScanConsistency.REQUEST_PLUS),
/** UPDATE_AFTER is {@link Stale#UPDATE_AFTER} and {@link ScanConsistency#NOT_BOUNDED} */
UPDATE_AFTER(Stale.UPDATE_AFTER, ScanConsistency.NOT_BOUNDED),
/** EVENTUALLY_CONSISTENT is {@link Stale#TRUE} and {@link ScanConsistency#NOT_BOUNDED} */
EVENTUALLY_CONSISTENT(Stale.TRUE, ScanConsistency.NOT_BOUNDED);
/**
* The static default Consistency ({@link #UPDATE_AFTER}).
*/
public static final Consistency DEFAULT_CONSISTENCY = UPDATE_AFTER;
private final Stale viewConsistency;
private final ScanConsistency n1qlConsistency;
Consistency(Stale viewConsistency, ScanConsistency n1qlConsistency) {
this.viewConsistency = viewConsistency;
this.n1qlConsistency = n1qlConsistency;
}
/**
* Returns the {@link Stale view consistency} corresponding to this {@link Consistency}.
*
* @return the view consistency.
*/
public Stale viewConsistency() {
return viewConsistency;
}
/**
* Returns the {@link ScanConsistency N1QL consistency} corresponding to this {@link Consistency}.
*
* @return the N1QL consistency.
*/
public ScanConsistency n1qlConsistency() {
return n1qlConsistency;
}
}

View File

@@ -20,7 +20,9 @@ import java.util.List;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.query.QueryParams;
import com.couchbase.client.java.query.Statement;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,18 +59,20 @@ public abstract class AbstractN1qlBasedQuery implements RepositoryQuery {
Statement statement = getStatement(accessor);
JsonArray queryPlaceholderValues = getPlaceholderValues(accessor);
Query query = buildQuery(statement, queryPlaceholderValues);
Query query = buildQuery(statement, queryPlaceholderValues,
getCouchbaseOperations().getDefaultConsistency().n1qlConsistency());
return executeDependingOnType(query, queryMethod, queryMethod.isPageQuery(), queryMethod.isModifyingQuery(),
queryMethod.isSliceQuery());
}
protected static Query buildQuery(Statement statement, JsonArray queryPlaceholderValues) {
protected static Query buildQuery(Statement statement, JsonArray queryPlaceholderValues, ScanConsistency scanConsistency) {
QueryParams queryParams = QueryParams.build().consistency(scanConsistency);
Query query;
if (!queryPlaceholderValues.isEmpty()) {
query = Query.parameterized(statement, queryPlaceholderValues);
query = Query.parameterized(statement, queryPlaceholderValues, queryParams);
}
else {
query = Query.simple(statement);
query = Query.simple(statement, queryParams);
}
if (LOG.isDebugEnabled()) {

View File

@@ -68,7 +68,8 @@ public class ViewBasedCouchbaseQuery implements RepositoryQuery {
boolean isReduce = methodName.startsWith("count");
String viewName = StringUtils.uncapitalize(methodName.replaceFirst("find|count", ""));
ViewQuery simpleQuery = ViewQuery.from(designDoc, viewName);
ViewQuery simpleQuery = ViewQuery.from(designDoc, viewName)
.stale(operations.getDefaultConsistency().viewConsistency());
if (isReduce) {
simpleQuery.reduce(isReduce);
return executeReduce(simpleQuery, designDoc, viewName);
@@ -80,7 +81,8 @@ public class ViewBasedCouchbaseQuery implements RepositoryQuery {
protected Object deriveAndExecute(Object[] runtimeParams) {
String designDoc = designDocName(method);
String viewName = method.getViewAnnotation().viewName();
ViewQuery baseQuery = ViewQuery.from(designDoc, viewName);
ViewQuery baseQuery = ViewQuery.from(designDoc, viewName)
.stale(operations.getDefaultConsistency().viewConsistency());
try {
PartTree tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());

View File

@@ -112,6 +112,18 @@ The reference to a CouchbaseConverter instance.
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="consistency" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The default Consistency value for generated queries.]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="direct">
<tool:expected-type type="org.springframework.data.couchbase.core.view.Consistency"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@@ -20,6 +20,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.WriteResultChecking;
import org.springframework.data.couchbase.core.view.Consistency;
@Configuration
public class UnitTestApplicationConfig extends AbstractCouchbaseConfiguration {
@@ -73,4 +74,9 @@ public class UnitTestApplicationConfig extends AbstractCouchbaseConfiguration {
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
@Override
protected Consistency getDefaultConsistency() {
return Consistency.READ_YOUR_OWN_WRITES;
}
}

View File

@@ -11,11 +11,16 @@ import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.query.ParameterizedQuery;
import com.couchbase.client.java.query.Query;
import com.couchbase.client.java.query.QueryParams;
import com.couchbase.client.java.query.SimpleQuery;
import com.couchbase.client.java.query.Statement;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
public class AbstractN1qlBasedQueryTest {
@@ -23,12 +28,12 @@ public class AbstractN1qlBasedQueryTest {
@Test
public void testEmptyArgumentsShouldProduceSimpleQuery() throws Exception {
Statement st = select("*");
Query query = AbstractN1qlBasedQuery.buildQuery(st, JsonArray.empty());
Query query = AbstractN1qlBasedQuery.buildQuery(st, JsonArray.empty(), ScanConsistency.NOT_BOUNDED);
JsonObject queryObject = query.n1ql();
assertTrue(query instanceof SimpleQuery);
assertEquals(st.toString(), query.statement().toString());
assertNull(query.params());
assertEquals(QueryParams.build().consistency(ScanConsistency.NOT_BOUNDED), query.params());
assertFalse(queryObject.containsKey("args"));
}
@@ -38,12 +43,12 @@ public class AbstractN1qlBasedQueryTest {
List<Object> params = new ArrayList<Object>(2);
params.add("test");
JsonArray placeholderValues = JsonArray.from(params);
Query query = AbstractN1qlBasedQuery.buildQuery(st, placeholderValues);
Query query = AbstractN1qlBasedQuery.buildQuery(st, placeholderValues, ScanConsistency.NOT_BOUNDED);
JsonObject queryObject = query.n1ql();
assertTrue(query instanceof ParameterizedQuery);
assertEquals(st.toString(), query.statement().toString());
assertNull(query.params());
assertEquals(QueryParams.build().consistency(ScanConsistency.NOT_BOUNDED), query.params());
assertTrue(queryObject.containsKey("args"));
JsonArray args = queryObject.getArray("args");
assertEquals(1, args.size());
@@ -57,12 +62,12 @@ public class AbstractN1qlBasedQueryTest {
params.add(123L);
params.add("test");
JsonArray placeholderValues = JsonArray.from(params);
Query query = AbstractN1qlBasedQuery.buildQuery(st, placeholderValues);
Query query = AbstractN1qlBasedQuery.buildQuery(st, placeholderValues, ScanConsistency.NOT_BOUNDED);
JsonObject queryObject = query.n1ql();
assertTrue(query instanceof ParameterizedQuery);
assertEquals(st.toString(), query.statement().toString());
assertNull(query.params());
assertEquals(QueryParams.build().consistency(ScanConsistency.NOT_BOUNDED), query.params());
assertTrue(queryObject.containsKey("args"));
JsonArray args = queryObject.getArray("args");
assertEquals(2, args.size());