DATACOUCH-275 Key auto generation using attributes, uuid

Original pull request: #142.
This commit is contained in:
Subhashni Balakrishnan
2017-03-21 18:47:03 -07:00
parent 298fe3ea90
commit 58ef2dbd0b
14 changed files with 786 additions and 17 deletions

View File

@@ -0,0 +1,7 @@
package org.springframework.data.couchbase;
import org.springframework.context.annotation.Configuration;
@Configuration
public class IntegrationTestCustomKeySettings extends IntegrationTestApplicationConfig {
}

View File

@@ -0,0 +1,147 @@
package org.springframework.data.couchbase.core;
import static org.junit.Assert.*;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.*;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.IdAttribute;
import org.springframework.data.couchbase.core.mapping.id.IdPrefix;
import org.springframework.data.couchbase.core.mapping.id.IdSuffix;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Subhashni Balakrishnan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
public class CouchbaseTemplateIdGenerationTests {
@Rule
public TestName testName = new TestName();
@Autowired
private Bucket client;
@Autowired
private CouchbaseTemplate template;
private void removeIfExist(String key) {
try {
client.remove(key);
}
catch (DocumentDoesNotExistException e) {
//ignore
}
}
@Test
public void shouldGenerateIdUsingAtrributes() throws Exception {
SimpleClassWithGeneratedIdValueUsingAttributes simpleClass = new SimpleClassWithGeneratedIdValueUsingAttributes();
String generatedId = template.getGeneratedId(simpleClass);
removeIfExist(generatedId);
assertEquals("Id generation should be correct", generatedId,
"prefix1::prefix2::0::1::2.0::3.0::4::Simple::Nested{value:simple}::suffix1::suffix2");
template.insert(simpleClass);
assertEquals("Exists after insert", true, template.exists(generatedId));
simpleClass.value = "modified";
template.save(simpleClass);
SimpleClassWithGeneratedIdValueUsingAttributes modifiedClass = template.findById(generatedId,
SimpleClassWithGeneratedIdValueUsingAttributes.class);
assertEquals("Get after save id should be correct", generatedId, modifiedClass.id);
template.update(simpleClass);
SimpleClassWithGeneratedIdValueUsingAttributes updatedClass = template.findById(generatedId,
SimpleClassWithGeneratedIdValueUsingAttributes.class);
assertEquals("Get after update id should be correct", generatedId, updatedClass.id);
template.remove(generatedId);
assertEquals("Exists after remove", false, template.exists(generatedId));
}
@Test
public void shouldGenerateIdUsingUUID() throws Exception {
SimpleClassWithGeneratedIdValueUsingUUID simpleClass = new SimpleClassWithGeneratedIdValueUsingUUID();
String generatedId = template.getGeneratedId(simpleClass);
simpleClass.id = generatedId;
template.insert(simpleClass);
assertEquals("Should not regenerate id", generatedId, simpleClass.id);
template.remove(generatedId);
assertEquals("Exists after remove", false, template.exists(generatedId));
}
@Document
static class SimpleClassWithGeneratedIdValueUsingAttributes {
@Id @GeneratedValue(strategy = USE_ATTRIBUTES, delimiter = "::")
public String id;
@IdAttribute(order = 6)
public Nested nested = new Nested("simple");
@IdAttribute(order = 5)
public String type = "Simple";
@IdAttribute(order = 4)
public int intNum = 4;
@IdAttribute(order = 2)
public float floatNum = 2F;
@IdAttribute(order = 3)
public double doubleNum = 3;
@IdAttribute
public long longNum = 0L;
@IdAttribute(order = 1)
public short shortNum = 1;
@IdPrefix(order = 1)
public String prefix2 = "prefix2";
@IdPrefix
public String prefix1 = "prefix1";
@IdSuffix(order = 1)
public String suffix2 = "suffix2";
@IdSuffix
public String suffix1 = "suffix1";
public String value = "new";
}
static class Nested {
private String value;
public Nested(String value) {
this.value = value;
}
@Override
public String toString() {
return "Nested{value:" + value + "}";
}
}
@Document
static class SimpleClassWithGeneratedIdValueUsingUUID {
@Id @GeneratedValue(strategy = UNIQUE)
public String id;
public String value = "new";
}
}

View File

@@ -0,0 +1,63 @@
package org.springframework.data.couchbase.core;
import static org.junit.Assert.*;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.error.DocumentDoesNotExistException;
import com.couchbase.client.java.repository.annotation.Id;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.IntegrationTestCustomKeySettings;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.KeySettings;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Subhashni Balakrishnan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = IntegrationTestCustomKeySettings.class)
public class CouchbaseTemplateKeySettingsTests {
@Rule
public TestName testName = new TestName();
@Autowired
private CouchbaseTemplate template;
@Before
public void setup() {
if (template.keySettings() == null) {
template.keySettings(KeySettings.build().prefix("MyAppPrefix").suffix("MyAppSuffix").delimiter("::"));
}
}
@Test
public void shouldAddCustomKeySettings() throws Exception {
SimpleClass simpleClass = new SimpleClass();
String generatedId = template.getGeneratedId(simpleClass);
assertEquals("Id generated should include custom key settings", "MyAppPrefix::myId::MyAppSuffix", generatedId);
}
@Test
public void shouldNotAllowKeySettingsToBeChanged() {
try {
template.keySettings(KeySettings.build().prefix("MyAppPrefix").suffix("MyAppSuffix").delimiter("::"));
fail("excepted unsupportedOperationException");
} catch(Exception ex) {
}
}
@Document
static class SimpleClass {
@Id
public String id = "myId";
}
}

View File

@@ -0,0 +1,106 @@
[[couchbase.autokeygeneration]]
= Auto generating keys
This chapter describes how couchbase document keys can be auto-generated using builtin mechanisms.
There are two types of auto-generation strategies supported.
- <<couchbase.autokeygeneration.usingattributes>>
- <<couchbase.autokeygeneration.unique>>
NOTE: The maximum key length supported by couchbase is 250 bytes.
[[couchbase.autokeygeneration.configuration]]
== Configuration
Keys to be auto-generated should be annotated with `@GeneratedValue`. The default strategy is `USE_ATTRIBUTES`. Prefix
and suffix for the key can be provided as part of the entity itself, these values are not persisted, they are only
used for key generation. The prefixes and suffixes are ordered using the `order` value. The default order is `0`, multiple
prefixes without order will overwrite the previous. If a value for id is already available, auto-generation will be skipped.
The delimiter for concatenation can be provided using `delimiter`, the default delimiter is `.`.
.Annotation for GeneratedValue
====
[source,java]
----
@Document
public class User {
@Id @GeneratedValue(strategy = USE_ATTRIBUTES, delimiter = ".")
private String id;
@IdPrefix(order=0)
private String userPrefix;
@IdSuffix(order=0)
private String userSuffix;
...
}
----
====
Common prefix and suffix for all entities keys can be added to `CouchbaseTemplate` directly. Once added to the `CouchbaseTemplate`,
they become immutable. These settings are always applied irrespective of the `GeneratedValue` annotation.
.Common key settings in CouchbaseTemplate
====
[source,java]
----
@Autowired
CouchbaseTemplate couchbaseTemplate;
...
couchbaseTemplate.keySettings(KeySettings.build().prefix("ApplicationA").suffix("Server1").delimiter("::"));
----
====
Key will be auto-generated only for operations with direct entity input like insert, update, save, delete using entity.
For other operations requiring just the key, it can be generated using `CouchbaseTemplate`.
.Standalone key generation in CouchbaseTemplate
====
[source,java]
----
@Autowired
CouchbaseTemplate couchbaseTemplate;
...
String id = couchbaseTemplate.getGeneratedId(entity);
...
repo.exists(id);
----
====
[[couchbase.autokeygeneration.usingattributes]]
== Key generation using attributes
It is a common practice to generate keys using a combination of the document attributes. Key generation using attributes
concatenates all the attribute values annotated with `IdAttribute`, based on the ordering provided similar to prefixes and suffixes.
.Annotation for IdAttribute
====
[source,java]
----
@Document
public class User {
@Id @GeneratedValue(strategy = USE_ATTRIBUTES)
private String id;
@IdAttribute
private String userid;
...
}
----
====
[[couchbase.autokeygeneration.unique]]
== Key generation using uuid
This auto-generation uses UUID random generator to generate document keys consuming 16 bytes of key space. This mechanism
is only recommended for test scaffolding.
.Annotation for Unique key generation
====
[source,java]
----
@Document
public class User {
@Id @GeneratedValue(strategy = UNIQUE)
private String id;
...
}
----
====

View File

@@ -20,6 +20,7 @@ include::preface.adoc[]
:leveloffset: +1
include::configuration.adoc[]
include::entity.adoc[]
include::autokeygeneration.adoc[]
include::{spring-data-commons-docs}/repositories.adoc[]
include::repository.adoc[]
include::template.adoc[]

View File

@@ -35,6 +35,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.mapping.KeySettings;
import org.springframework.data.couchbase.core.query.Consistency;
/**
@@ -389,4 +390,31 @@ public interface CouchbaseOperations {
* @return the consistency to use for generated repository queries.
*/
Consistency getDefaultConsistency();
/**
* Add common key settings
*
* Throws {@link UnsupportedOperationException} if KeySettings is already set. It becomes immutable.
*
* @param settings {@link KeySettings}
*/
void keySettings(final KeySettings settings);
/**
* Get key settings associated with the template
*
* @return {@link KeySettings}
*/
KeySettings keySettings();
/**
* Get generated id - applies both using prefix and suffix through entity as well as template {@link KeySettings}
*
* ** NOTE: UNIQUE strategy will generate different ids each time ***
*
* @param entity the entity object.
* @return id the couchbase document key.
*/
String getGeneratedId(Object entity);
}

View File

@@ -49,6 +49,12 @@ import com.couchbase.client.java.view.ViewQuery;
import com.couchbase.client.java.view.ViewResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
import org.springframework.data.couchbase.core.mapping.KeySettings;
import rx.Observable;
import rx.functions.Func1;
@@ -61,11 +67,6 @@ import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
import org.springframework.data.couchbase.core.mapping.event.AfterDeleteEvent;
import org.springframework.data.couchbase.core.mapping.event.AfterSaveEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent;
@@ -101,6 +102,7 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
private final CouchbaseConverter converter;
private final TranslationService translationService;
private final ClusterInfo clusterInfo;
private KeySettings keySettings;
private ApplicationEventPublisher eventPublisher;
@@ -120,7 +122,8 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
this(clusterInfo, client, null, translationService);
}
public CouchbaseTemplate(final ClusterInfo clusterInfo, final Bucket client,
public CouchbaseTemplate(final ClusterInfo clusterInfo,
final Bucket client,
final CouchbaseConverter converter,
final TranslationService translationService) {
this.clusterInfo = clusterInfo;
@@ -128,8 +131,10 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
this.converter = converter == null ? getDefaultConverter() : converter;
this.translationService = translationService == null ? getDefaultTranslationService() : translationService;
this.mappingContext = this.converter.getMappingContext();
}
private TranslationService getDefaultTranslationService() {
JacksonTranslationService t = new JacksonTranslationService();
t.afterPropertiesSet();
@@ -581,12 +586,14 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
converted.setId(addCommonPrefixAndSuffix(converted.getId()));
Document<String> doc = encodeAndWrap(converted, version);
Document<String> storedDoc;
//We will check version only if required
boolean versionPresent = versionProperty != null;
//If version is not set - assumption that document is new, otherwise updating
boolean existingDocument = version != null && version > 0L;
try {
switch (persistType) {
case SAVE:
@@ -640,8 +647,7 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
try {
RawJsonDocument deletedDoc = client.remove((String) objectToRemove, persistTo, replicateTo,
RawJsonDocument.class);
RawJsonDocument deletedDoc = client.remove((String) objectToRemove , persistTo, replicateTo, RawJsonDocument.class);
return deletedDoc != null;
} catch (Exception e) {
handleWriteResultError("Delete document failed: " + e.getMessage(), e);
@@ -660,7 +666,7 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
@Override
public Boolean doInBucket() {
try {
RawJsonDocument deletedDoc = client.remove(converted.getId(), persistTo, replicateTo
RawJsonDocument deletedDoc = client.remove(addCommonPrefixAndSuffix(converted.getId()), persistTo, replicateTo
, RawJsonDocument.class);
return deletedDoc != null;
} catch (Exception e) {
@@ -748,4 +754,42 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
return springDataOperationName;
}
}
}
@Override
public void keySettings(KeySettings settings) {
if (this.keySettings != null) {
throw new UnsupportedOperationException("Key settings is already set, it is no longer mutable");
}
this.keySettings = settings;
}
@Override
public KeySettings keySettings() {
return this.keySettings;
}
@Override
public String getGeneratedId(Object entity) {
ensureNotIterable(entity);
CouchbaseDocument converted = new CouchbaseDocument();
converter.write(entity, converted);
return addCommonPrefixAndSuffix(converted.getId());
}
private String addCommonPrefixAndSuffix(final String id) {
String convertedKey = id;
if (this.keySettings == null) {
return id;
}
String prefix = this.keySettings.prefix();
String delimiter = this.keySettings.delimiter();
String suffix = this.keySettings.suffix();
if (prefix != null && !prefix.equals("")) {
convertedKey = prefix + delimiter + convertedKey;
}
if (suffix != null && !suffix.equals("")) {
convertedKey = convertedKey + delimiter + suffix;
}
return convertedKey;
}
}

View File

@@ -16,12 +16,15 @@
package org.springframework.data.couchbase.core.convert;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
import com.couchbase.client.java.repository.annotation.Field;
@@ -35,6 +38,10 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.IdAttribute;
import org.springframework.data.couchbase.core.mapping.id.IdPrefix;
import org.springframework.data.couchbase.core.mapping.id.IdSuffix;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -229,7 +236,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@Override
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
if (!doesPropertyExistInSource(prop) || entity.isConstructorArgument(prop)) {
if (!doesPropertyExistInSource(prop) || entity.isConstructorArgument(prop) || isIdConstructionProperty(prop)) {
return;
}
Object obj = prop.isIdProperty() ? source.getId() : getValueInternal(prop, source, instance);
@@ -239,6 +246,10 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
private boolean doesPropertyExistInSource(final CouchbasePersistentProperty property) {
return property.isIdProperty() || source.containsKey(property.getFieldName());
}
private boolean isIdConstructionProperty(final CouchbasePersistentProperty property) {
return property.isAnnotationPresent(IdPrefix.class) || property.isAnnotationPresent(IdSuffix.class);
}
});
entity.doWithAssociations(new AssociationHandler<CouchbasePersistentProperty>() {
@@ -374,7 +385,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
writeInternal(source, target, type);
if (target.getId() == null) {
throw new MappingException("An ID property is needed, but not found on this entity.");
throw new MappingException("An ID property is needed, but not found/could not be generated on this entity.");
}
}
@@ -446,10 +457,11 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
final CouchbasePersistentProperty idProperty = entity.getIdProperty();
final CouchbasePersistentProperty versionProperty = entity.getVersionProperty();
if (idProperty != null && target.getId() == null) {
String id = accessor.getProperty(idProperty, String.class);
target.setId(id);
}
GeneratedValue generatedValueInfo = null;
final TreeMap<Integer, String> prefixes = new TreeMap<Integer, String>();
final TreeMap<Integer, String> suffixes = new TreeMap<Integer, String>();
final TreeMap<Integer, String> idAttributes = new TreeMap<Integer, String>();
target.setExpiration(entity.getExpiry());
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@@ -457,12 +469,32 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
if (prop.equals(idProperty) || (versionProperty != null && prop.equals(versionProperty))) {
return;
} else if(enableStrictFieldChecking && !prop.isAnnotationPresent(Field.class)){
} else if (enableStrictFieldChecking && !prop.isAnnotationPresent(Field.class)) {
return;
}
Object propertyObj = accessor.getProperty(prop, prop.getType());
if (null != propertyObj) {
if (prop.isAnnotationPresent(IdPrefix.class)) {
IdPrefix prefix = prop.findAnnotation(IdPrefix.class);
int order = prefix.order();
prefixes.put(order, convertToString(propertyObj));
return;
}
if (prop.isAnnotationPresent(IdSuffix.class)) {
IdSuffix suffix = prop.findAnnotation(IdSuffix.class);
int order = suffix.order();
suffixes.put(order, convertToString(propertyObj));
return;
}
if (prop.isAnnotationPresent(IdAttribute.class)) {
IdAttribute idAttribute = prop.findAnnotation(IdAttribute.class);
int order = idAttribute.order();
idAttributes.put(order, convertToString(propertyObj));
}
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, target, prop);
} else {
@@ -470,8 +502,29 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
}
}
}
private String convertToString(Object propertyObj) {
if (propertyObj instanceof String) {
return (String) propertyObj;
} else if (propertyObj instanceof Number) {
return new StringBuffer().append(propertyObj).toString();
} else {
return propertyObj.toString();
}
}
});
if (idProperty != null && target.getId() == null) {
String id = accessor.getProperty(idProperty, String.class);
if(idProperty.isAnnotationPresent(GeneratedValue.class) && (id == null || id.equals(""))) {
generatedValueInfo = idProperty.findAnnotation(GeneratedValue.class);
target.setId(generateId(generatedValueInfo, prefixes, suffixes, idAttributes));
} else {
target.setId(id);
}
}
entity.doWithAssociations(new AssociationHandler<CouchbasePersistentProperty>() {
@Override
public void doWithAssociation(final Association<CouchbasePersistentProperty> association) {
@@ -836,4 +889,49 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
}
}
private String generateId(GeneratedValue generatedValue, TreeMap<Integer, String> prefixes, TreeMap<Integer, String> suffixes,
TreeMap<Integer, String> idAttributes) {
String delimiter = generatedValue.delimiter();
StringBuilder sb = new StringBuilder();
boolean isAppending = false;
if (prefixes.size() > 0) {
appendKeyParts(sb, prefixes.values(), delimiter);
isAppending = true;
}
if (generatedValue.strategy() == USE_ATTRIBUTES && idAttributes.size() > 0) {
if(isAppending) {
sb.append(delimiter);
}
appendKeyParts(sb, idAttributes.values(), delimiter);
}
if (generatedValue.strategy() == UNIQUE) {
if(isAppending) {
sb.append(delimiter);
}
sb.append(UUID.randomUUID());
}
if (suffixes.size() > 0) {
if(isAppending) {
sb.append(delimiter);
}
appendKeyParts(sb, suffixes.values(), delimiter);
}
return sb.toString();
}
private StringBuilder appendKeyParts(StringBuilder sb, Collection<String> values, String delimiter) {
boolean isAppending = false;
for(String value : values) {
if (isAppending) {
sb.append(delimiter);
} else {
isAppending = true;
}
sb.append(value);
}
return sb;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2017 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.mapping;
/**
* Common settings for Couchbase key
* - prefix
* - suffix
* - delimiter
*
* @author Subhashni Balakrishnan
*/
public class KeySettings {
private static String DEFAULT_DELIMITER = ".";
private String commonPrefix;
private String commonSuffix;
private String delimiter;
public static KeySettings build() {
return new KeySettings();
}
protected KeySettings() {
this.delimiter = DEFAULT_DELIMITER;
}
/**
* Set common prefix
*/
public KeySettings prefix(String prefix) {
this.commonPrefix = prefix;
return this;
}
/**
* Set common suffix
*/
public KeySettings suffix(String suffix) {
this.commonSuffix = suffix;
return this;
}
/**
* Set delimiter
*/
public KeySettings delimiter(String delimiter) {
if (delimiter == null) {
return this;
}
this.delimiter = delimiter;
return this;
}
/**
* Get common prefix
*/
public String prefix() {
return this.commonPrefix;
}
/**
* Get common suffix
*/
public String suffix() {
return this.commonSuffix;
}
/**
* Get delimiter
*/
public String delimiter() {
return this.delimiter;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2017 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.mapping.id;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.couchbase.core.mapping.Document;
/**
* This annotation is targeted at {@link Document} interfaces, indicating that
* the framework should generate the document key value using the specified generator
*
* @author Subhashni Balakrishnan
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GeneratedValue {
String delimiter() default ".";
GenerationStrategy strategy() default USE_ATTRIBUTES;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2017 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.mapping.id;
/**
* Keys generation strategy
*
* @author Subhashni Balakrishnan
*/
public enum GenerationStrategy {
/**
* Constructs key from the entity attributes using the supplied id, prefix, suffix
*/
USE_ATTRIBUTES,
/**
* Uses an uuid generator
*/
UNIQUE
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2017 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.mapping.id;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;
/**
* This annotation is targeted at building the document id using the attribute value.
*
* @author Subhashni Balakrishnan
*/
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdAttribute {
int order() default 0;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2017 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.mapping.id;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;
/**
* This annotation is targeted at building the document id using the value
* as a part of a prefix build. The order determines in way which the specified
* prefix value is used in building the prefix.
*
* @author Subhashni Balakrishnan
*/
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdPrefix {
int order() default 0;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2017 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.mapping.id;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Inherited;
/**
* This annotation is targeted at building the document id using the value
* as a part of a suffix build. The order determines in way which the specified
* suffix value is used in building the suffix.
*
* @author Subhashni Balakrishnan
*/
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdSuffix {
int order() default 0;
}