@@ -75,7 +75,7 @@
org.codehaus.jackson
jackson-core-asl
- 1.9.11
+ 1.9.12
diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java
index 2fc5de58..02efe96e 100644
--- a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java
@@ -22,10 +22,15 @@
package com.couchbase.spring.core;
+import java.util.Collection;
+
public interface CouchbaseOperations {
/**
- * Insert the object into the connected bucket.
+ * Save the given object.
+ *
+ * When the document already exists (specified by its unique id),
+ * then it will be overriden. Otherwise it will be created.
*
*
* The object is converted to a JSON representation using an instance of
@@ -34,5 +39,78 @@ public interface CouchbaseOperations {
*
* @param objectToSave the object to store in the bucket.
*/
+ void save(Object objectToSave);
+
+ /**
+ * Save a list of objects.
+ *
+ * When one of the documents already exists (specified by its unique id),
+ * then it will be overriden. Otherwise it will be created.
+ *
+ * @param batchToSave the list of objects to store in the bucket.
+ */
+ void save(Collection extends Object> batchToSave);
+
+ /**
+ * Insert the given object.
+ *
+ * When the document already exists (specified by its unique id),
+ * then it will not be overriden. Use the {@link CouchbaseOperations#save}
+ * method for this.
+ *
+ *
+ * The object is converted to a JSON representation using an instance of
+ * {@link CouchbaseConverter}.
+ *
+ *
+ * @param objectToSave the object to add to the bucket.
+ */
void insert(Object objectToSave);
+
+ /**
+ * Insert a list of objects.
+ *
+ * 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.
+ *
+ * @param batchToSave the list of objects to add to the bucket.
+ */
+ void insert(Collection extends Object> batchToSave);
+
+ /**
+ * Update the given object.
+ *
+ * When the document does not exists (specified by its unique id),
+ * then it will not be created. Use the {@link CouchbaseOperations#save}
+ * method for this.
+ *
+ *
+ * The object is converted to a JSON representation using an instance of
+ * {@link CouchbaseConverter}.
+ *
+ *
+ * @param objectToSave the object to add to the bucket.
+ */
+ void update(Object objectToSave);
+
+ /**
+ * Insert a list of objects.
+ *
+ * When one of the documents does not exists (specified by its unique id),
+ * then it will not be created. Use the {@link CouchbaseOperations#save}
+ * method for this.
+ *
+ * @param batchToSave the list of objects to add to the bucket.
+ */
+ void update(Collection extends Object> batchToSave);
+
+ /**
+ * Find an object by its given Id and map it to the corresponding entity.
+ *
+ * @param id the unique ID of the document.
+ * @param entityClass the entity to map to.
+ * @return returns the found object or null otherwise.
+ */
+ public T findById(String id, Class entityClass);
}
diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
index 048168c1..5be3427f 100644
--- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
@@ -22,15 +22,37 @@
package com.couchbase.spring.core;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.data.mapping.context.MappingContext;
+
import com.couchbase.client.CouchbaseClient;
import com.couchbase.spring.core.convert.CouchbaseConverter;
import com.couchbase.spring.core.convert.MappingCouchbaseConverter;
import com.couchbase.spring.core.mapping.ConvertedCouchbaseDocument;
+import com.couchbase.spring.core.mapping.CouchbasePersistentEntity;
+import com.couchbase.spring.core.mapping.CouchbasePersistentProperty;
public class CouchbaseTemplate implements CouchbaseOperations {
private CouchbaseClient client;
private CouchbaseConverter couchbaseConverter;
+ protected final MappingContext extends CouchbasePersistentEntity>,
+ CouchbasePersistentProperty> mappingContext;
+ private static final Collection ITERABLE_CLASSES;
+
+ static {
+ Set iterableClasses = new HashSet();
+ iterableClasses.add(List.class.getName());
+ iterableClasses.add(Collection.class.getName());
+ iterableClasses.add(Iterator.class.getName());
+ ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses);
+ }
public CouchbaseTemplate(CouchbaseClient client) {
this(client, null);
@@ -39,6 +61,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
public CouchbaseTemplate(CouchbaseClient client, CouchbaseConverter converter) {
this.client = client;
this.couchbaseConverter = converter == null ? getDefaultConverter(client) : converter;
+ this.mappingContext = this.couchbaseConverter.getMappingContext();
}
private CouchbaseConverter getDefaultConverter(CouchbaseClient client) {
@@ -48,12 +71,73 @@ public class CouchbaseTemplate implements CouchbaseOperations {
return converter;
}
- @Override
public void insert(Object objectToSave) {
+ ensureNotIterable(objectToSave);
+
+ ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
+ couchbaseConverter.write(objectToSave, converted);
+ client.add(converted.getId(), converted.getExpiry(), converted.getValue());
+ }
+
+ public void insert(Collection extends Object> batchToSave) {
+ Iterator extends Object> iter = batchToSave.iterator();
+ while(iter.hasNext()) {
+ insert(iter.next());
+ }
+ }
+
+ public void save(Object objectToSave) {
+ ensureNotIterable(objectToSave);
+
ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
client.set(converted.getId(), converted.getExpiry(), converted.getValue());
}
+
+ public void save(Collection extends Object> batchToSave) {
+ Iterator extends Object> iter = batchToSave.iterator();
+ while(iter.hasNext()) {
+ save(iter.next());
+ }
+ }
+ public void update(Object objectToSave) {
+ ensureNotIterable(objectToSave);
+ ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
+ couchbaseConverter.write(objectToSave, converted);
+ client.replace(converted.getId(), converted.getExpiry(), converted.getValue());
+ }
+
+ public void update(Collection extends Object> batchToSave) {
+ Iterator extends Object> iter = batchToSave.iterator();
+ while(iter.hasNext()) {
+ save(iter.next());
+ }
+ }
+
+ public T findById(String id, Class entityClass) {
+ String result = (String) client.get(id);
+ if(result == null) {
+ return null;
+ }
+
+ ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(id, result);
+ return couchbaseConverter.read(entityClass, converted);
+ }
+
+ /**
+ * Make sure the given object is not a iterable.
+ *
+ * @param o the object to verify.
+ */
+ protected 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.");
+ }
+ }
+ }
+
+
}
diff --git a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java
index 1d7a56f6..253012bc 100644
--- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java
+++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java
@@ -31,6 +31,7 @@ import java.io.OutputStream;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonParser;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -64,8 +65,14 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
}
@Override
- public R read(Class type, ConvertedCouchbaseDocument s) {
- throw new UnsupportedOperationException("Not supported yet.");
+ public R read(Class type, ConvertedCouchbaseDocument doc) {
+ JsonFactory jsonFactory = new JsonFactory();
+ try {
+ JsonParser parser = jsonFactory.createJsonParser(doc.getValue());
+ } catch(Exception e) {
+ throw new MappingException("Could not read from JSON while converting "
+ + doc.getId());
+ }
}
@Override
diff --git a/src/test/java/com/couchbase/spring/core/Beer.java b/src/test/java/com/couchbase/spring/core/Beer.java
index b160b40a..56c18a41 100644
--- a/src/test/java/com/couchbase/spring/core/Beer.java
+++ b/src/test/java/com/couchbase/spring/core/Beer.java
@@ -24,6 +24,8 @@ package com.couchbase.spring.core;
import org.springframework.data.annotation.Id;
+import com.couchbase.spring.core.mapping.Field;
+
/**
* Test class for persisting and loading from {@link CouchbaseTemplate}.
*/
@@ -34,6 +36,7 @@ public class Beer {
private String name;
+ @Field("is_active")
private boolean active = true;
public Beer(String id) {
diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java
index 3ef5304d..09223929 100644
--- a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java
+++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java
@@ -25,6 +25,7 @@ package com.couchbase.spring.core;
import com.couchbase.client.CouchbaseClient;
import com.couchbase.spring.TestApplicationConfig;
import com.couchbase.spring.core.mapping.Document;
+import com.couchbase.spring.core.mapping.Field;
import static org.junit.Assert.*;
import org.junit.Test;
@@ -45,30 +46,70 @@ public class CouchbaseTemplateTest {
private CouchbaseTemplate template;
@Test
- public void insertsSimpleEntityCorrectly() throws Exception {
+ public void saveSimpleEntityCorrectly() throws Exception {
String id = "beers:awesome-stout";
String name = "The Awesome Stout";
boolean active = false;
Beer beer = new Beer(id).setName(name).setActive(active);
- template.insert(beer);
+ template.save(beer);
String result = (String) client.get(id);
- String expected = "{\"active\":" + active + ",\"name\":\"" + name + "\"}";
+ String expected = "{\"is_active\":" + active + ",\"name\":\"" + name + "\"}";
assertNotNull(result);
assertEquals(expected, result);
}
@Test
- public void insertDocumentWithExpiry() throws Exception {
+ public void saveDocumentWithExpiry() throws Exception {
String id = "simple-doc-with-expiry";
DocumentWithExpiry doc = new DocumentWithExpiry(id);
- template.insert(doc);
+ template.save(doc);
assertNotNull(client.get(id));
Thread.sleep(3000);
assertNull(client.get(id));
}
+ @Test
+ public void insertDoesNotOverride() {
+ String id ="double-insert-test";
+ String expected = "{\"name\":\"Mr. A\"}";
+
+ SimplePerson doc = new SimplePerson(id, "Mr. A");
+ template.insert(doc);
+ String result = (String) client.get(id);
+ assertEquals(expected, result);
+
+ doc = new SimplePerson(id, "Mr. B");
+ template.insert(doc);
+ result = (String) client.get(id);
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void updateDoesNotInsert() {
+ String id ="update-does-not-insert";
+ SimplePerson doc = new SimplePerson(id, "Nice Guy");
+ template.update(doc);
+ assertNull(client.get(id));
+ }
+
+ /**
+ * A sample document with just an id and property.
+ */
+ @Document
+ class SimplePerson {
+ @Id
+ private final String id;
+ @Field
+ private final String name;
+
+ public SimplePerson(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+ }
+
/**
* A sample document that expires in 2 seconds.
*/
@@ -80,6 +121,5 @@ public class CouchbaseTemplateTest {
public DocumentWithExpiry(String id) {
this.id = id;
}
-
}
}