Refactoring read to use instantiators.
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -74,7 +74,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<version>1.9.12</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<CouchbasePersistentProperty> getParameterProvider(
|
||||
CouchbasePersistentEntity<?> entity, ConvertedCouchbaseDocument source, Object parent) {
|
||||
|
||||
CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, parent);
|
||||
PersistentEntityParameterValueProvider<CouchbasePersistentProperty> parameterProvider =
|
||||
new PersistentEntityParameterValueProvider<CouchbasePersistentProperty>(
|
||||
entity, provider, parent);
|
||||
|
||||
return parameterProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R read(Class<R> type, ConvertedCouchbaseDocument doc) {
|
||||
CouchbasePersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
return read(type, doc, null);
|
||||
}
|
||||
|
||||
public <R> R read(Class<R> type, final ConvertedCouchbaseDocument doc, Object parent) {
|
||||
final CouchbasePersistentEntity<R> entity = (CouchbasePersistentEntity<R>)
|
||||
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<CouchbasePersistentProperty> 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<CouchbasePersistentEntity<R>, R> wrapper =
|
||||
BeanWrapper.create(instance, conversionService);
|
||||
final R result = wrapper.getBean();
|
||||
|
||||
// Set properties not already set in the constructor
|
||||
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
|
||||
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<CouchbasePersistentProperty> {
|
||||
|
||||
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> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String, Object> 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<String, Object>();
|
||||
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<String, Object> converted = mapper.readValue(getRawValue(),
|
||||
new TypeReference<Map<String, Object>>() { });
|
||||
this.decoded = converted;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
throw new MappingException("Error while decoding JSON object.", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,5 @@ public interface CouchbasePersistentProperty extends
|
||||
* custom annotation.
|
||||
*/
|
||||
String getFieldName();
|
||||
|
||||
String getOriginalName();
|
||||
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ public class CouchbaseTemplateTest {
|
||||
assertEquals(id, found.getId());
|
||||
assertEquals(name, found.getName());
|
||||
assertEquals(active, found.getActive());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user