From d01aaff90df03fe355e2540efa896124e455b466 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 09:07:10 +0100 Subject: [PATCH] Upgrading dependencies and more tests for templates. --- .gitignore | 4 + pom.xml | 8 +- .../spring/core/CouchbaseOperations.java | 80 ++++++++++++++++- .../spring/core/CouchbaseTemplate.java | 86 ++++++++++++++++++- .../convert/MappingCouchbaseConverter.java | 11 ++- .../java/com/couchbase/spring/core/Beer.java | 3 + .../spring/core/CouchbaseTemplateTest.java | 52 +++++++++-- 7 files changed, 230 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index e08c7941..bc8404d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ target/ .DS_Store + +.classpath +.project +.settings/* diff --git a/pom.xml b/pom.xml index 22fa28c0..86e1b5c2 100644 --- a/pom.xml +++ b/pom.xml @@ -42,12 +42,12 @@ couchbase couchbase-client - 1.1.0 + 1.1.2 org.springframework spring-context - 3.1.3.RELEASE + 3.2.1.RELEASE jar @@ -64,7 +64,7 @@ org.springframework spring-test - 3.1.3.RELEASE + 3.2.1.RELEASE test @@ -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 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 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 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, + 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 batchToSave) { + Iterator 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 batchToSave) { + Iterator 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 batchToSave) { + Iterator 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; } - } }