Refactoring read to use instantiators.

This commit is contained in:
Michael Nitschinger
2013-02-07 14:51:56 +01:00
parent f59c182dbe
commit 83aa6e0acf
8 changed files with 141 additions and 69 deletions

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -35,6 +35,5 @@ public interface CouchbasePersistentProperty extends
* custom annotation.
*/
String getFieldName();
String getOriginalName();
}

View File

@@ -107,7 +107,6 @@ public class CouchbaseTemplateTest {
assertEquals(id, found.getId());
assertEquals(name, found.getName());
assertEquals(active, found.getActive());
}
/**