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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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));`
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user