DATACOUCH-44 - Expose PersistTo/ReplicateTo on template

While users benefit from the exposure in terms of control, it is also
needed to make the unit tests more reliable as reported in the ticket.
This commit is contained in:
Michael Nitschinger
2014-01-23 14:35:03 +01:00
parent 9452c40eeb
commit 5414ee556b
3 changed files with 243 additions and 85 deletions

View File

@@ -19,6 +19,8 @@ package org.springframework.data.couchbase.core;
import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.protocol.views.ViewResponse;
import net.spy.memcached.PersistTo;
import net.spy.memcached.ReplicateTo;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import java.util.Collection;
@@ -41,6 +43,16 @@ public interface CouchbaseOperations {
*/
void save(Object objectToSave);
/**
* Save the given object.
* <p/>
* <p>When the document already exists (specified by its unique id), then it will be overriden. Otherwise it will be
* created.</p>
*
* @param objectToSave the object to store in the bucket.
*/
void save(Object objectToSave, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Save a list of objects.
* <p/>
@@ -51,15 +63,39 @@ public interface CouchbaseOperations {
*/
void save(Collection<?> batchToSave);
/**
* Save a list of objects.
* <p/>
* <p>When one of the documents already exists (specified by its unique id), then it will be overriden. Otherwise it
* will be created.</p>
*
* @param batchToSave the list of objects to store in the bucket.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void save(Collection<?> batchToSave, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Insert the given object.
* <p/>
* <p>When the document already exists (specified by its unique id), then it will not be overriden. Use the
* {@link CouchbaseOperations#save} method for this task.</p>
*
* @param objectToSave the object to add to the bucket.
* @param objectToInsert the object to add to the bucket.
*/
void insert(Object objectToSave);
void insert(Object objectToInsert);
/**
* Insert the given object.
* <p/>
* <p>When the document already exists (specified by its unique id), then it will not be overriden. Use the
* {@link CouchbaseOperations#save} method for this task.</p>
*
* @param objectToInsert the object to add to the bucket.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void insert(Object objectToInsert, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Insert a list of objects.
@@ -67,9 +103,21 @@ public interface CouchbaseOperations {
* <p>When one of the documents already exists (specified by its unique id), then it will not be overriden. Use the
* {@link CouchbaseOperations#save} method for this.</p>
*
* @param batchToSave the list of objects to add to the bucket.
* @param batchToInsert the list of objects to add to the bucket.
*/
void insert(Collection<?> batchToSave);
void insert(Collection<?> batchToInsert);
/**
* Insert a list of objects.
* <p/>
* <p>When one of the documents already exists (specified by its unique id), then it will not be overriden. Use the
* {@link CouchbaseOperations#save} method for this.</p>
*
* @param batchToInsert the list of objects to add to the bucket.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void insert(Collection<?> batchToInsert, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Update the given object.
@@ -77,9 +125,21 @@ public interface CouchbaseOperations {
* <p>When the document does not exist (specified by its unique id) it will not be created. Use the
* {@link CouchbaseOperations#save} method for this.</p>
*
* @param objectToSave the object to add to the bucket.
* @param objectToUpdate the object to add to the bucket.
*/
void update(Object objectToSave);
void update(Object objectToUpdate);
/**
* Update the given object.
* <p/>
* <p>When the document does not exist (specified by its unique id) it will not be created. Use the
* {@link CouchbaseOperations#save} method for this.</p>
*
* @param objectToUpdate the object to add to the bucket.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void update(Object objectToUpdate, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Insert a list of objects.
@@ -87,9 +147,21 @@ public interface CouchbaseOperations {
* <p>If one of the documents does not exist (specified by its unique id), then it will not be created. Use the
* {@link CouchbaseOperations#save} method for this.</p>
*
* @param batchToSave the list of objects to add to the bucket.
* @param batchToUpdate the list of objects to add to the bucket.
*/
void update(Collection<?> batchToSave);
void update(Collection<?> batchToUpdate);
/**
* Insert a list of objects.
* <p/>
* <p>If one of the documents does not exist (specified by its unique id), then it will not be created. Use the
* {@link CouchbaseOperations#save} method for this.</p>
*
* @param batchToUpdate the list of objects to add to the bucket.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void update(Collection<?> batchToUpdate, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Find an object by its given Id and map it to the corresponding entity.
@@ -150,9 +222,21 @@ public interface CouchbaseOperations {
* If the object is a String, it will be treated as the document key
* directly.
*
* @param object the Object to remove.
* @param objectToRemove the Object to remove.
*/
void remove(Object object);
void remove(Object objectToRemove);
/**
* Remove the given object from the bucket by id.
* <p/>
* If the object is a String, it will be treated as the document key
* directly.
*
* @param objectToRemove the Object to remove.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void remove(Object objectToRemove, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Remove a list of objects from the bucket by id.
@@ -161,6 +245,15 @@ public interface CouchbaseOperations {
*/
void remove(Collection<?> batchToRemove);
/**
* Remove a list of objects from the bucket by id.
*
* @param batchToRemove the list of Objects to remove.
* @param persistTo the persistence constraint setting.
* @param replicateTo the replication constraint setting.
*/
void remove(Collection<?> batchToRemove, PersistTo persistTo, ReplicateTo replicateTo);
/**
* Executes a BucketCallback translating any exceptions as necessary.
* <p/>

View File

@@ -21,6 +21,8 @@ import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.protocol.views.View;
import com.couchbase.client.protocol.views.ViewResponse;
import com.couchbase.client.protocol.views.ViewRow;
import net.spy.memcached.PersistTo;
import net.spy.memcached.ReplicateTo;
import net.spy.memcached.internal.OperationFuture;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
@@ -49,7 +51,7 @@ import java.util.concurrent.TimeoutException;
*/
public class CouchbaseTemplate implements CouchbaseOperations {
private CouchbaseClient client;
private final CouchbaseClient client;
private CouchbaseConverter couchbaseConverter;
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private static final Collection<String> ITERABLE_CLASSES;
@@ -77,18 +79,18 @@ public class CouchbaseTemplate implements CouchbaseOperations {
public CouchbaseTemplate(final CouchbaseClient client, final TranslationService translationService) {
this.client = client;
this.couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
this.translationService = translationService == null ? getDefaultTranslationService() : translationService;
mappingContext = this.couchbaseConverter.getMappingContext();
mappingContext = couchbaseConverter.getMappingContext();
}
private CouchbaseConverter getDefaultConverter() {
private static CouchbaseConverter getDefaultConverter() {
final MappingCouchbaseConverter converter = new MappingCouchbaseConverter(new CouchbaseMappingContext());
converter.afterPropertiesSet();
return converter;
}
private TranslationService getDefaultTranslationService() {
private static TranslationService getDefaultTranslationService() {
final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService();
jacksonTranslationService.afterPropertiesSet();
return jacksonTranslationService;
@@ -102,67 +104,43 @@ public class CouchbaseTemplate implements CouchbaseOperations {
return translationService.decode(source, target);
}
public final void insert(final Object objectToSave) {
ensureNotIterable(objectToSave);
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.add(converted.getId(), converted.getExpiration(), translateEncode(converted)).get();
}
});
@Override
public final void insert(final Object objectToInsert) {
insert(objectToInsert, PersistTo.ZERO, ReplicateTo.ZERO);
}
public final void insert(final Collection<?> batchToSave) {
for (final Object aBatchToSave : batchToSave) {
insert(aBatchToSave);
@Override
public final void insert(final Collection<?> batchToInsert) {
for (final Object toInsert : batchToInsert) {
insert(toInsert);
}
}
@Override
public void save(final Object objectToSave) {
ensureNotIterable(objectToSave);
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.set(converted.getId(), converted.getExpiration(), translateEncode(converted)).get();
}
});
save(objectToSave, PersistTo.ZERO, ReplicateTo.ZERO);
}
@Override
public void save(final Collection<?> batchToSave) {
for (final Object aBatchToSave : batchToSave) {
save(aBatchToSave);
for (final Object toSave : batchToSave) {
save(toSave);
}
}
public void update(final Object objectToSave) {
ensureNotIterable(objectToSave);
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.replace(converted.getId(), converted.getExpiration(), translateEncode(converted)).get();
}
});
@Override
public void update(final Object objectToUpdate) {
update(objectToUpdate, PersistTo.ZERO, ReplicateTo.ZERO);
}
public void update(final Collection<?> batchToSave) {
for (final Object aBatchToSave : batchToSave) {
update(aBatchToSave);
@Override
public void update(final Collection<?> batchToUpdate) {
for (final Object toUpdate : batchToUpdate) {
update(toUpdate);
}
}
@Override
public final <T> T findById(final String id, final Class<T> entityClass) {
String result = execute(new BucketCallback<String>() {
@Override
@@ -212,33 +190,15 @@ public class CouchbaseTemplate implements CouchbaseOperations {
});
}
@Override
public void remove(final Object objectToRemove) {
ensureNotIterable(objectToRemove);
if (objectToRemove instanceof String) {
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.delete((String) objectToRemove).get();
}
});
return;
}
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToRemove, converted);
execute(new BucketCallback<OperationFuture<Boolean>>() {
@Override
public OperationFuture<Boolean> doInBucket() {
return client.delete(converted.getId());
}
});
remove(objectToRemove, PersistTo.ZERO, ReplicateTo.ZERO);
}
@Override
public void remove(final Collection<?> batchToRemove) {
for (final Object aBatchToRemove : batchToRemove) {
remove(aBatchToRemove);
for (final Object toRemove : batchToRemove) {
remove(toRemove);
}
}
@@ -273,7 +233,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
*
* @param o the object to verify.
*/
protected final void ensureNotIterable(Object o) {
protected static void ensureNotIterable(Object o) {
if (null != o) {
if (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName())) {
throw new IllegalArgumentException("Cannot use a collection here.");
@@ -285,4 +245,105 @@ public class CouchbaseTemplate implements CouchbaseOperations {
public CouchbaseConverter getConverter() {
return couchbaseConverter;
}
@Override
public void save(Object objectToSave, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToSave);
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.set(converted.getId(), converted.getExpiration(), translateEncode(converted), persistTo,
replicateTo).get();
}
});
}
@Override
public void save(Collection<?> batchToSave, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toSave : batchToSave) {
save(toSave, persistTo, replicateTo);
}
}
@Override
public void insert(Object objectToInsert, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToInsert);
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToInsert, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.add(converted.getId(), converted.getExpiration(), translateEncode(converted), persistTo,
replicateTo).get();
}
});
}
@Override
public void insert(Collection<?> batchToInsert, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toInsert : batchToInsert) {
insert(toInsert, persistTo,replicateTo);
}
}
@Override
public void update(Object objectToUpdate, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToUpdate);
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToUpdate, converted);
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.replace(converted.getId(), converted.getExpiration(), translateEncode(converted), persistTo,
replicateTo).get();
}
});
}
@Override
public void update(Collection<?> batchToUpdate, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toUpdate : batchToUpdate) {
update(toUpdate, persistTo, replicateTo);
}
}
@Override
public void remove(final Object objectToRemove, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToRemove);
if (objectToRemove instanceof String) {
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
return client.delete((String) objectToRemove, persistTo, replicateTo).get();
}
});
return;
}
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToRemove, converted);
execute(new BucketCallback<OperationFuture<Boolean>>() {
@Override
public OperationFuture<Boolean> doInBucket() {
return client.delete(converted.getId());
}
});
}
@Override
public void remove(Collection<?> batchToRemove, PersistTo persistTo, ReplicateTo replicateTo) {
for (Object toRemove : batchToRemove) {
remove(toRemove, persistTo, replicateTo);
}
}
}

View File

@@ -19,6 +19,8 @@ package org.springframework.data.couchbase.repository;
import com.couchbase.client.CouchbaseClient;
import com.couchbase.client.protocol.views.DesignDocument;
import com.couchbase.client.protocol.views.ViewDesign;
import net.spy.memcached.PersistTo;
import net.spy.memcached.ReplicateTo;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
@@ -40,13 +42,15 @@ public class SimpleCouchbaseRepositoryListener extends DependencyInjectionTestEx
for (int i = 0; i < 100; i++) {
User u = new User("testuser-" + i, "uname-" + i);
template.save(u);
template.save(u, PersistTo.MASTER, ReplicateTo.ZERO);
}
}
private void createAndWaitForDesignDocs(CouchbaseClient client) {
DesignDocument designDoc = new DesignDocument("user");
String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }";
String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository." +
"User\") { emit(null, null); } }";
designDoc.setView(new ViewDesign("all", mapFunction, "_count"));
client.createDesignDoc(designDoc);
}