diff --git a/pom.xml b/pom.xml
index 86e1b5c2..dc62902e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,7 +74,7 @@
org.codehaus.jackson
- jackson-core-asl
+ jackson-mapper-asl
1.9.12
diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
index 5be3427f..5c342ba0 100644
--- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
+++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java
@@ -76,7 +76,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
- client.add(converted.getId(), converted.getExpiry(), converted.getValue());
+ client.add(converted.getId(), converted.getExpiry(), converted.getRawValue());
}
public void insert(Collection extends Object> batchToSave) {
@@ -91,7 +91,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
- client.set(converted.getId(), converted.getExpiry(), converted.getValue());
+ client.set(converted.getId(), converted.getExpiry(), converted.getRawValue());
}
public void save(Collection extends Object> batchToSave) {
@@ -106,7 +106,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
- client.replace(converted.getId(), converted.getExpiry(), converted.getValue());
+ client.replace(converted.getId(), converted.getExpiry(), converted.getRawValue());
}
public void update(Collection extends Object> batchToSave) {
diff --git a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java
index cf54d9ff..ecc69ae8 100644
--- a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java
+++ b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java
@@ -25,11 +25,13 @@ 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) {
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 5a58134b..373c5549 100644
--- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java
+++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java
@@ -25,26 +25,38 @@ 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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
+import java.util.Map;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.support.ConversionServiceFactory;
+import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
+import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
+import org.springframework.data.mapping.model.ParameterValueProvider;
+import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
+import org.springframework.data.mapping.model.PropertyValueProvider;
+import org.springframework.data.mapping.model.SpELContext;
+import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.data.mapping.PropertyHandler;
+import org.springframework.util.Assert;
public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
implements ApplicationContextAware {
@@ -52,6 +64,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
protected ApplicationContext applicationContext;
protected final MappingContext extends CouchbasePersistentEntity>,
CouchbasePersistentProperty> mappingContext;
+ protected boolean useFieldAccessOnly = true;
@SuppressWarnings("deprecation")
public MappingCouchbaseConverter(MappingContext extends CouchbasePersistentEntity>,
@@ -67,46 +80,58 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
return mappingContext;
}
+
+ private ParameterValueProvider getParameterProvider(
+ CouchbasePersistentEntity> entity, ConvertedCouchbaseDocument source, Object parent) {
+
+ CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, parent);
+ PersistentEntityParameterValueProvider parameterProvider =
+ new PersistentEntityParameterValueProvider(
+ entity, provider, parent);
+
+ return parameterProvider;
+ }
+
@Override
public R read(Class type, ConvertedCouchbaseDocument doc) {
- CouchbasePersistentEntity> entity = mappingContext.getPersistentEntity(type);
+ return read(type, doc, null);
+ }
+
+ public R read(Class type, final ConvertedCouchbaseDocument doc, Object parent) {
+ final CouchbasePersistentEntity entity = (CouchbasePersistentEntity)
+ mappingContext.getPersistentEntity(type);
- R decoded = null;
- try {
- decoded = type.getConstructor(String.class).newInstance(doc.getId());
- } catch(Exception e) {
- throw new MappingException("Could not instantiate object while converting "
- + doc.getId());
- }
+ ParameterValueProvider provider =
+ getParameterProvider(entity, doc, parent);
+ EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
+ R instance = instantiator.createInstance(entity, provider);
- JsonFactory jsonFactory = new JsonFactory();
- try {
- JsonParser parser = jsonFactory.createJsonParser(doc.getValue());
- parser.nextToken();
- while(parser.nextToken() != JsonToken.END_OBJECT) {
- String fieldname = parser.getCurrentName();
- parser.nextToken();
- CouchbasePersistentProperty property = entity.getPersistentProperty(fieldname);
- if(property == null) {
- continue;
- }
- Field field = type.getDeclaredField(property.getOriginalName());
- field.setAccessible(true);
-
- if(property.getType().equals(boolean.class)) {
- field.set(decoded, parser.getValueAsBoolean());
- } else if(property.getType().equals(String.class)) {
- field.set(decoded, parser.getText());
- } else {
- throw new MappingException("Unknown type in JSON found: " + property.getType());
- }
- }
- } catch(Exception e) {
- throw new MappingException("Could not read from JSON while converting "
- + doc.getId(), e);
- }
-
- return decoded;
+ final BeanWrapper, R> wrapper =
+ BeanWrapper.create(instance, conversionService);
+ final R result = wrapper.getBean();
+
+ // Set properties not already set in the constructor
+ entity.doWithProperties(new PropertyHandler() {
+ public void doWithPersistentProperty(CouchbasePersistentProperty prop) {
+
+ boolean isConstructorProperty = entity.isConstructorArgument(prop);
+ boolean hasValueForProperty = doc.containsField(prop.getFieldName());
+
+ if (!hasValueForProperty || isConstructorProperty) {
+ return;
+ }
+
+ Object obj = null;
+ if(prop.getFieldName() == "id") {
+ obj = doc.getId();
+ } else {
+ obj = doc.get(prop.getFieldName());
+ }
+ wrapper.setProperty(prop, obj, useFieldAccessOnly);
+ }
+ });
+
+ return result;
}
@Override
@@ -178,7 +203,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
jsonGenerator.writeEndObject();
jsonGenerator.close();
- target.setValue(jsonStream.toString());
+ target.setRawValue(jsonStream.toString());
}
@Override
@@ -186,5 +211,34 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter
throws BeansException {
this.applicationContext = applicationContext;
}
+
+ private class CouchbasePropertyValueProvider implements PropertyValueProvider {
+
+ private final ConvertedCouchbaseDocument source;
+ private final Object parent;
+
+ public CouchbasePropertyValueProvider(ConvertedCouchbaseDocument source, Object parent) {
+ Assert.notNull(source);
+ this.source = source;
+ this.parent = parent;
+ }
+
+ public T getPropertyValue(CouchbasePersistentProperty property) {
+ String fieldName = property.getFieldName();
+ T value = null;
+
+ if(fieldName == "id") {
+ value = (T) source.getId();
+ } else {
+ value = (T) source.get(fieldName);
+ }
+
+ if (value == null) {
+ return null;
+ }
+
+ return value;
+ }
+ }
}
diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java
index 252cd38e..b2db7d6b 100644
--- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java
+++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java
@@ -76,21 +76,5 @@ public class BasicCouchbasePersistentProperty
return annotation != null && StringUtils.hasText(annotation.value())
? annotation.value() : field.getName();
}
-
- /**
- * Return the name of the property.
- *
- * This overrides the default implementation to make sure that when
- * the property is retrieved from a JSON document it can be found
- * even when a alias is used.
- */
- @Override
- public String getName() {
- return getFieldName();
- }
-
- public String getOriginalName() {
- return name;
- }
}
diff --git a/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java b/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java
index cf6bc368..4ff45218 100644
--- a/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java
+++ b/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java
@@ -22,26 +22,37 @@
package com.couchbase.spring.core.mapping;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.springframework.data.mapping.model.MappingException;
+
public class ConvertedCouchbaseDocument {
private String id;
- private String value;
+ private String rawValue;
private int expiry;
+
+ private Map decoded;
public ConvertedCouchbaseDocument() {
this("", "", 0);
}
- public ConvertedCouchbaseDocument(String id, String value) {
- this(id, value, 0);
+ public ConvertedCouchbaseDocument(String id, String rawValue) {
+ this(id, rawValue, 0);
}
- public ConvertedCouchbaseDocument(String id, String value, int expiry) {
+ public ConvertedCouchbaseDocument(String id, String rawValue, int expiry) {
this.id = id;
- this.value = value;
+ this.rawValue = rawValue;
this.expiry = expiry;
+ this.decoded = new HashMap();
+ parseJson();
}
public void setId(String id) {
@@ -52,12 +63,14 @@ public class ConvertedCouchbaseDocument {
return id;
}
- public String getValue() {
- return value;
+ public String getRawValue() {
+ return rawValue;
}
- public void setValue(String value) {
- this.value = value;
+ public void setRawValue(String value) {
+ this.rawValue = value;
+ parseJson();
+
}
public int getExpiry() {
@@ -67,5 +80,26 @@ public class ConvertedCouchbaseDocument {
public void setExpiry(int expiry) {
this.expiry = expiry;
}
+
+ public boolean containsField(String fieldname) {
+ return decoded.containsKey(fieldname);
+ }
+
+ public Object get(String fieldname) {
+ return decoded.get(fieldname);
+ }
+
+ private void parseJson() {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ if(!getRawValue().isEmpty()) {
+ Map converted = mapper.readValue(getRawValue(),
+ new TypeReference