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/README.md b/README.md
index 952d036e..fb8d21fd 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,8 @@ It is distributed from the [Couchbase Maven Repository](http://files.couchbase.c
```
Currently, the project depends on the following packages:
- * couchbase.couchbase-client: 1.1.0
- * org.springframework.spring-context: 3.1.3.RELEASE
+ * couchbase.couchbase-client: 1.1.2
+ * org.springframework.spring-context: 3.2.1.RELEASE
* cglib.cglib: 2.2.2
* (When Testing) junit.junit: 4.11
diff --git a/pom.xml b/pom.xml
index fb05ef39..6b82ee93 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,12 +41,12 @@
couchbasecouchbase-client
- 1.1.0
+ 1.1.2org.springframeworkspring-context
- 3.1.3.RELEASE
+ 3.2.1.RELEASEjar
@@ -63,9 +63,24 @@
org.springframeworkspring-test
- 3.1.3.RELEASE
+ 3.2.1.RELEASEtest
+
+ org.springframework.data
+ spring-data-commons-core
+ 1.4.0.RELEASE
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.9.12
+
+
+ org.springframework
+ spring-tx
+ 3.2.1.RELEASE
+ spring-data-couchbase
diff --git a/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java b/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java
index adc0b9fb..1be3be41 100644
--- a/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java
+++ b/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java
@@ -27,8 +27,8 @@ import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
/**
- * The CouchbaseCache class implements the common cache operations on top of
- * a CouchbaseClient instance.
+ * The CouchbaseCache class implements the Spring Cache interface
+ * on top of Couchbase Server and the Couchbase Java SDK.
*/
public class CouchbaseCache implements Cache {
diff --git a/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java b/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java
index 42ff2f2e..213c71ea 100644
--- a/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java
+++ b/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java
@@ -31,8 +31,11 @@ import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
/**
- * The CouchbaseCacheManager handles CouchbaseClient connections and
- * orchestrates them as needed.
+ * The CouchbaseCacheManager orchestrates CouchbaseCache instances.
+ *
+ * Since more than one current CouchbaseClient connection can be used
+ * for caching, the CouchbaseCacheManager orchestrates and handles
+ * them for the Spring Cache abstraction layer.
*/
public class CouchbaseCacheManager extends AbstractCacheManager {
diff --git a/src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java b/src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java
new file mode 100644
index 00000000..fa1b21f7
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+package com.couchbase.spring.config;
+
+import com.couchbase.client.CouchbaseClient;
+import com.couchbase.spring.core.CouchbaseMappingContext;
+import com.couchbase.spring.core.CouchbaseTemplate;
+import com.couchbase.spring.core.convert.MappingCouchbaseConverter;
+import com.couchbase.spring.core.mapping.Document;
+import java.util.HashSet;
+import java.util.Set;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.data.annotation.Persistent;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Base class for Spring Data Couchbase configuration using JavaConfig.
+ */
+@Configuration
+public abstract class AbstractCouchbaseConfiguration {
+
+ /**
+ * Return the {@link CouchbaseClient} instance to connect to.
+ */
+ @Bean
+ public abstract CouchbaseClient couchbaseClient() throws Exception;
+
+ /**
+ * Creates a {@link CouchbaseTemplate}.
+ */
+ @Bean
+ public CouchbaseTemplate couchbaseTemplate() throws Exception {
+ return new CouchbaseTemplate(couchbaseClient(), mappingCouchbaseConverter());
+ }
+
+ /**
+ * Creates a {@link MappingCouchbaseConverter} using the configured {@link
+ * #couchbaseMappingContext}.
+ */
+ @Bean
+ public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception {
+ return new MappingCouchbaseConverter(couchbaseMappingContext());
+ }
+
+ /**
+ * Creates a {@link CouchbaseMappingContext} equipped with entity classes
+ * scanned from the mapping base package.
+ */
+ @Bean
+ public CouchbaseMappingContext couchbaseMappingContext() throws Exception {
+ CouchbaseMappingContext mappingContext = new CouchbaseMappingContext();
+ mappingContext.setInitialEntitySet(getInitialEntitySet());
+ return mappingContext;
+ }
+
+ /**
+ * Scans the mapping base package for classes annotated with {@link Document}.
+ */
+ protected Set> getInitialEntitySet() throws ClassNotFoundException {
+ String basePackage = getMappingBasePackage();
+ Set> initialEntitySet = new HashSet>();
+
+ if(StringUtils.hasText(basePackage)) {
+ ClassPathScanningCandidateComponentProvider componentProvider =
+ new ClassPathScanningCandidateComponentProvider(false);
+ componentProvider.addIncludeFilter(
+ new AnnotationTypeFilter(Document.class));
+ componentProvider.addIncludeFilter(
+ new AnnotationTypeFilter(Persistent.class));
+
+ for (BeanDefinition candidate :
+ componentProvider.findCandidateComponents(basePackage)) {
+ initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
+ AbstractCouchbaseConfiguration.class.getClassLoader()));
+ }
+ }
+
+ return initialEntitySet;
+ }
+
+ /**
+ * Return the base package to scan for mapped {@link Document}s. Will return
+ * the package name of the configuration class (the concrete class, not this
+ * one here) by default. So if you have a {@code com.acme.AppConfig} extending
+ * {@link AbstractCouchbaseConfiguration} the base package will be considered
+ * {@code com.acme} unless the method is overridden to implement alternate
+ * behavior.
+ *
+ * @return the base package to scan for mapped {@link Document} classes or
+ * {@literal null} to not enable scanning for entities.
+ */
+ protected String getMappingBasePackage() {
+ return getClass().getPackage().getName();
+ }
+
+}
diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java b/src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java
new file mode 100644
index 00000000..271a9a9d
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+package com.couchbase.spring.core;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.DataAccessResourceFailureException;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.dao.support.PersistenceExceptionTranslator;
+
+import com.couchbase.client.ObservedException;
+import com.couchbase.client.ObservedModifiedException;
+import com.couchbase.client.ObservedTimeoutException;
+import com.couchbase.client.vbucket.ConnectionException;
+
+/**
+ * Simple {@link PersistenceExceptionTranslator} for Couchbase.
+ *
+ * Convert the given runtime exception to an appropriate exception from the
+ * {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation
+ * is appropriate: any other exception may have resulted from user code, and should not
+ * be translated.
+ */
+public class CouchbaseExceptionTranslator implements PersistenceExceptionTranslator {
+
+ @Override
+ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+ if(ex instanceof ConnectionException) {
+ return new DataAccessResourceFailureException(ex.getMessage(), ex);
+ }
+
+ if(ex instanceof ObservedException ||
+ ex instanceof ObservedTimeoutException ||
+ ex instanceof ObservedModifiedException) {
+ return new DataIntegrityViolationException(ex.getMessage(), ex);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java b/src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java
new file mode 100644
index 00000000..c1740f7e
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+package com.couchbase.spring.core;
+
+import com.couchbase.spring.core.mapping.BasicCouchbasePersistentEntity;
+import com.couchbase.spring.core.mapping.BasicCouchbasePersistentProperty;
+import com.couchbase.spring.core.mapping.CouchbasePersistentProperty;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.data.mapping.context.AbstractMappingContext;
+import org.springframework.data.mapping.model.SimpleTypeHolder;
+import org.springframework.data.util.TypeInformation;
+
+public class CouchbaseMappingContext
+ extends AbstractMappingContext, CouchbasePersistentProperty>
+ implements ApplicationContextAware {
+
+ private ApplicationContext context;
+
+ @Override
+ protected BasicCouchbasePersistentEntity> createPersistentEntity(TypeInformation typeInformation) {
+ BasicCouchbasePersistentEntity entity = new BasicCouchbasePersistentEntity(typeInformation);
+ if(context != null) {
+ entity.setApplicationContext(context);
+ }
+ return entity;
+ }
+
+ @Override
+ protected CouchbasePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, BasicCouchbasePersistentEntity> owner, SimpleTypeHolder simpleTypeHolder) {
+ return new BasicCouchbasePersistentProperty(field, descriptor, owner, simpleTypeHolder);
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ this.context = applicationContext;
+ super.setApplicationContext(applicationContext);
+ }
+
+}
diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java
new file mode 100644
index 00000000..02efe96e
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+package com.couchbase.spring.core;
+
+import java.util.Collection;
+
+public interface CouchbaseOperations {
+
+ /**
+ * 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
+ * {@link CouchbaseConverter}.
+ *
+ *
+ * @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
new file mode 100644
index 00000000..f0628fd1
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+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;
+ private final CouchbaseExceptionTranslator exceptionTranslator =
+ new CouchbaseExceptionTranslator();
+
+ 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);
+ }
+
+ 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) {
+ MappingCouchbaseConverter converter = new MappingCouchbaseConverter(
+ new CouchbaseMappingContext());
+ converter.afterPropertiesSet();
+ return converter;
+ }
+
+ public void insert(Object objectToSave) {
+ ensureNotIterable(objectToSave);
+
+ ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
+ couchbaseConverter.write(objectToSave, converted);
+ client.add(converted.getId(), converted.getExpiry(), converted.getRawValue());
+ }
+
+ 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.getRawValue());
+ }
+
+ 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.getRawValue());
+ }
+
+ 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.");
+ }
+ }
+ }
+
+ private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex) {
+ RuntimeException resolved = this.exceptionTranslator.translateExceptionIfPossible(ex);
+ return resolved == null ? ex : resolved;
+ }
+
+
+}
diff --git a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java
new file mode 100644
index 00000000..ecc69ae8
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+package com.couchbase.spring.core.convert;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.support.GenericConversionService;
+import org.springframework.data.convert.EntityInstantiators;
+
+public abstract class AbstractCouchbaseConverter implements CouchbaseConverter,
+ InitializingBean {
+
+ protected final GenericConversionService conversionService;
+ protected EntityInstantiators instantiators = new EntityInstantiators();
+
+ public AbstractCouchbaseConverter(
+ GenericConversionService conversionService) {
+ this.conversionService = conversionService;
+ }
+
+ public ConversionService getConversionService() {
+ return conversionService;
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+
+ }
+
+}
diff --git a/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java
new file mode 100644
index 00000000..f0c06148
--- /dev/null
+++ b/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2009-2012 Couchbase, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
+ * IN THE SOFTWARE.
+ */
+
+package com.couchbase.spring.core.convert;
+
+import com.couchbase.spring.core.mapping.ConvertedCouchbaseDocument;
+import com.couchbase.spring.core.mapping.CouchbasePersistentEntity;
+import com.couchbase.spring.core.mapping.CouchbasePersistentProperty;
+import org.springframework.data.convert.EntityConverter;
+import org.springframework.data.convert.EntityReader;
+
+public interface CouchbaseConverter extends
+ EntityConverter,
+ CouchbasePersistentProperty, Object, ConvertedCouchbaseDocument>,
+ CouchbaseWriter