development on converter infrastructure
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
|
||||
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||
<classpathentry kind="src" path="src/test/java"/>
|
||||
<classpathentry kind="src" path="src/test/resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
|
||||
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.MongoException;
|
||||
|
||||
public interface CollectionCallback<T> {
|
||||
|
||||
T doInCollection(DBCollection collection) throws MongoException, DataAccessException;
|
||||
|
||||
}
|
||||
@@ -97,13 +97,20 @@ public class MongoBeanPropertyDocumentSource implements DocumentSource<DBObject>
|
||||
protected void initialize(Object source) {
|
||||
this.source = source;
|
||||
this.mappedClass = source.getClass();
|
||||
this.mappedFields = new HashMap<String, PropertyDescriptor>();
|
||||
this.mappedProperties = new HashSet<String>();
|
||||
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (pd.getWriteMethod() != null) {
|
||||
this.mappedFields.put(pd.getName(), pd);
|
||||
this.mappedProperties.add(pd.getName());
|
||||
if (mappedClass.getClass().equals("java.util.Map")) {
|
||||
|
||||
} else {
|
||||
this.mappedFields = new HashMap<String, PropertyDescriptor>();
|
||||
this.mappedProperties = new HashSet<String>();
|
||||
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (pd.getWriteMethod() != null) {
|
||||
this.mappedFields.put(pd.getName(), pd);
|
||||
this.mappedProperties.add(pd.getName());
|
||||
}
|
||||
}
|
||||
if (mappedProperties.size() == 0) {
|
||||
logger.warn("No properties mapped for object [" + source + "], type = [" + mappedClass + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
|
||||
public interface MongoConverter extends MongoWriter<Object>, MongoReader<Object> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
public interface MongoDocumentWriter {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
public interface MongoReader<T> {
|
||||
|
||||
T read(Class<? extends T> clazz, DBObject dbo);
|
||||
//T read(DBObject dbo);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
public interface MongoReaderWriter<T> extends MongoWriter<T>, MongoReader<T> {
|
||||
|
||||
}
|
||||
@@ -24,8 +24,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.datastore.document.AbstractDocumentStoreTemplate;
|
||||
import org.springframework.datastore.document.DocumentMapper;
|
||||
import org.springframework.datastore.document.DocumentSource;
|
||||
import org.springframework.datastore.document.mongodb.query.Query;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -43,12 +41,11 @@ public class MongoTemplate extends AbstractDocumentStoreTemplate<DB> implements
|
||||
|
||||
private String defaultCollectionName;
|
||||
|
||||
private MongoConverter mongoConverter;
|
||||
|
||||
//TODO expose configuration...
|
||||
private CollectionOptions defaultCollectionOptions;
|
||||
|
||||
// public MongoTemplate() {
|
||||
// super();
|
||||
// }
|
||||
|
||||
public MongoTemplate(DB db) {
|
||||
super();
|
||||
@@ -60,24 +57,16 @@ public class MongoTemplate extends AbstractDocumentStoreTemplate<DB> implements
|
||||
public String getDefaultCollectionName() {
|
||||
return defaultCollectionName;
|
||||
}
|
||||
|
||||
//TODO would one ever consider passing in a DBCollection object?
|
||||
|
||||
|
||||
public void setDefaultCollectionName(String defaultCollection) {
|
||||
this.defaultCollectionName = defaultCollection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void execute(String command) {
|
||||
execute((DBObject)JSON.parse(command));
|
||||
public void executeCommand(String jsonCommand) {
|
||||
executeCommand((DBObject)JSON.parse(jsonCommand));
|
||||
}
|
||||
|
||||
public void execute(DocumentSource<DBObject> command) {
|
||||
execute(command.getDocument());
|
||||
}
|
||||
|
||||
public void execute(DBObject command) {
|
||||
|
||||
public void executeCommand(DBObject command) {
|
||||
CommandResult cr = getConnection().command(command);
|
||||
String err = cr.getErrorMessage();
|
||||
if (err != null) {
|
||||
@@ -114,10 +103,7 @@ public class MongoTemplate extends AbstractDocumentStoreTemplate<DB> implements
|
||||
getConnection().getCollection(collectionName)
|
||||
.drop();
|
||||
}
|
||||
|
||||
public void saveObject(Object object) {
|
||||
saveObject(getRequiredDefaultCollectionName(), object);
|
||||
}
|
||||
|
||||
|
||||
private String getRequiredDefaultCollectionName() {
|
||||
String name = getDefaultCollectionName();
|
||||
@@ -128,69 +114,104 @@ public class MongoTemplate extends AbstractDocumentStoreTemplate<DB> implements
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public void saveObject(String collectionName, Object source) {
|
||||
MongoBeanPropertyDocumentSource docSrc = new MongoBeanPropertyDocumentSource(source);
|
||||
save(collectionName, docSrc);
|
||||
|
||||
public void save(Object objectToSave) {
|
||||
save(getRequiredDefaultCollectionName(), objectToSave);
|
||||
}
|
||||
|
||||
public void save(String collectionName, DocumentSource<DBObject> documentSource) {
|
||||
DBObject dbDoc = documentSource.getDocument();
|
||||
WriteResult wr = null;
|
||||
try {
|
||||
wr = getConnection().getCollection(collectionName).save(dbDoc);
|
||||
} catch (MongoException e) {
|
||||
throw new DataRetrievalFailureException(wr.getLastError().getErrorMessage(), e);
|
||||
public void save(String collectionName, Object objectToSave) {
|
||||
BasicDBObject dbDoc = new BasicDBObject();
|
||||
this.mongoConverter.write(objectToSave, dbDoc);
|
||||
saveDBObject(collectionName, dbDoc);
|
||||
}
|
||||
|
||||
public <T> void save(String collectionName, T objectToSave, MongoWriter<T> writer) {
|
||||
BasicDBObject dbDoc = new BasicDBObject();
|
||||
this.mongoConverter.write(objectToSave, dbDoc);
|
||||
saveDBObject(collectionName, dbDoc);
|
||||
}
|
||||
|
||||
|
||||
protected void saveDBObject(String collectionName, BasicDBObject dbDoc) {
|
||||
if (dbDoc.keySet().size() > 0 ) {
|
||||
WriteResult wr = null;
|
||||
try {
|
||||
wr = getConnection().getCollection(collectionName).save(dbDoc);
|
||||
} catch (MongoException e) {
|
||||
throw new DataRetrievalFailureException(wr.getLastError().getErrorMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <T> List<T> queryForCollection(String collectionName, Class<T> targetClass) {
|
||||
DocumentMapper<DBObject, T> mapper = MongoBeanPropertyDocumentMapper.newInstance(targetClass);
|
||||
return queryForCollection(collectionName, mapper);
|
||||
}
|
||||
|
||||
public <T> List<T> queryForCollection(String collectionName, DocumentMapper<DBObject, T> mapper) {
|
||||
|
||||
List<T> results = new ArrayList<T>();
|
||||
DBCollection collection = getConnection().getCollection(collectionName);
|
||||
for (DBObject dbo : collection.find()) {
|
||||
results.add(mapper.mapDocument(dbo));
|
||||
Object obj = mongoConverter.read(targetClass, dbo);
|
||||
//effectively acts as a query on the collection restricting it to elements of a specific type
|
||||
if (targetClass.isInstance(obj)) {
|
||||
results.add(targetClass.cast(obj));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public <T> List<T> queryForCollection(String collectionName, Class<T> targetClass, MongoReader<T> reader) {
|
||||
List<T> results = new ArrayList<T>();
|
||||
DBCollection collection = getConnection().getCollection(collectionName);
|
||||
for (DBObject dbo : collection.find()) {
|
||||
results.add(reader.read(targetClass, dbo));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, Query query, Class<T> targetClass) {
|
||||
DocumentMapper<DBObject, T> mapper = MongoBeanPropertyDocumentMapper.newInstance(targetClass);
|
||||
return queryForList(collectionName, query, mapper);
|
||||
return queryForList(collectionName, query.getQueryObject(), targetClass);
|
||||
}
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, Query query, DocumentMapper<DBObject, T> mapper) {
|
||||
return queryForList(collectionName, query.getQueryObject(), mapper);
|
||||
public <T> List<T> queryForList(String collectionName, Query query, Class<T> targetClass, MongoReader<T> reader) {
|
||||
return queryForList(collectionName, query.getQueryObject(), targetClass, reader);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, String query, Class<T> targetClass) {
|
||||
DocumentMapper<DBObject, T> mapper = MongoBeanPropertyDocumentMapper.newInstance(targetClass);
|
||||
return queryForList(collectionName, query, mapper);
|
||||
return queryForList(collectionName, (DBObject)JSON.parse(query), targetClass);
|
||||
}
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, String query, DocumentMapper<DBObject, T> mapper) {
|
||||
return queryForList(collectionName, (DBObject)JSON.parse(query), mapper);
|
||||
public <T> List<T> queryForList(String collectionName, String query, Class<T> targetClass, MongoReader<T> reader) {
|
||||
return queryForList(collectionName, (DBObject)JSON.parse(query), targetClass, reader);
|
||||
}
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, DBObject query, Class<T> targetClass) {
|
||||
DocumentMapper<DBObject, T> mapper = MongoBeanPropertyDocumentMapper.newInstance(targetClass);
|
||||
return queryForList(collectionName, query, mapper);
|
||||
}
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, DBObject query, DocumentMapper<DBObject, T> mapper) {
|
||||
|
||||
|
||||
public <T> List<T> queryForList(String collectionName, DBObject query, Class<T> targetClass) {
|
||||
DBCollection collection = getConnection().getCollection(collectionName);
|
||||
List<T> results = new ArrayList<T>();
|
||||
for (DBObject dbo : collection.find(query)) {
|
||||
results.add(mapper.mapDocument(dbo));
|
||||
Object obj = mongoConverter.read(targetClass,dbo);
|
||||
//effectively acts as a query on the collection restricting it to elements of a specific type
|
||||
if (targetClass.isInstance(obj)) {
|
||||
results.add(targetClass.cast(obj));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public RuntimeException translateIfNecessary(RuntimeException ex) {
|
||||
public <T> List<T> queryForList(String collectionName, DBObject query, Class<T> targetClass, MongoReader<T> reader) {
|
||||
DBCollection collection = getConnection().getCollection(collectionName);
|
||||
List<T> results = new ArrayList<T>();
|
||||
for (DBObject dbo : collection.find(query)) {
|
||||
results.add(reader.read(targetClass, dbo));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public RuntimeException convertMongoAccessException(RuntimeException ex) {
|
||||
return MongoDbUtils.translateMongoExceptionIfPossible(ex);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
public interface MongoWriter<T> {
|
||||
|
||||
void write(T t, DBObject dbo);
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package org.springframework.datastore.document.mongodb;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.bson.types.CodeWScope;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
public class SimpleMongoConverter implements MongoConverter {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
public static final Set<String> SIMPLE_TYPES;
|
||||
|
||||
static {
|
||||
Set<String> basics = new HashSet<String>();
|
||||
basics.add(boolean.class.getName());
|
||||
basics.add(long.class.getName());
|
||||
basics.add(short.class.getName());
|
||||
basics.add(int.class.getName());
|
||||
basics.add(byte.class.getName());
|
||||
basics.add(float.class.getName());
|
||||
basics.add(double.class.getName());
|
||||
basics.add(char.class.getName());
|
||||
basics.add(Boolean.class.getName());
|
||||
basics.add(Long.class.getName());
|
||||
basics.add(Short.class.getName());
|
||||
basics.add(Integer.class.getName());
|
||||
basics.add(Byte.class.getName());
|
||||
basics.add(Float.class.getName());
|
||||
basics.add(Double.class.getName());
|
||||
basics.add(Character.class.getName());
|
||||
basics.add(String.class.getName());
|
||||
basics.add(java.util.Date.class.getName());
|
||||
// basics.add(Time.class.getName());
|
||||
// basics.add(Timestamp.class.getName());
|
||||
// basics.add(java.sql.Date.class.getName());
|
||||
// basics.add(BigDecimal.class.getName());
|
||||
// basics.add(BigInteger.class.getName());
|
||||
basics.add(Locale.class.getName());
|
||||
// basics.add(Calendar.class.getName());
|
||||
// basics.add(GregorianCalendar.class.getName());
|
||||
// basics.add(java.util.Currency.class.getName());
|
||||
// basics.add(TimeZone.class.getName());
|
||||
// basics.add(Object.class.getName());
|
||||
basics.add(Class.class.getName());
|
||||
// basics.add(byte[].class.getName());
|
||||
// basics.add(Byte[].class.getName());
|
||||
// basics.add(char[].class.getName());
|
||||
// basics.add(Character[].class.getName());
|
||||
// basics.add(Blob.class.getName());
|
||||
// basics.add(Clob.class.getName());
|
||||
// basics.add(Serializable.class.getName());
|
||||
// basics.add(URI.class.getName());
|
||||
// basics.add(URL.class.getName());
|
||||
basics.add(DBRef.class.getName());
|
||||
basics.add(Pattern.class.getName());
|
||||
basics.add(CodeWScope.class.getName());
|
||||
basics.add(ObjectId.class.getName());
|
||||
// TODO check on enums.. basics.add(Enum.class.getName());
|
||||
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
|
||||
}
|
||||
|
||||
protected GenericConversionService conversionService = new GenericConversionService();
|
||||
|
||||
public SimpleMongoConverter() {
|
||||
initializeConverters();
|
||||
}
|
||||
|
||||
protected void initializeConverters() {
|
||||
|
||||
conversionService.addConverter(new Converter<ObjectId, String>() {
|
||||
public String convert(ObjectId id) {
|
||||
return id.toString();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public SimpleMongoConverter(GenericConversionService conversionService) {
|
||||
super();
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public void write(Object obj, DBObject dbo) {
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(obj);
|
||||
initBeanWrapper(bw);
|
||||
|
||||
PropertyDescriptor[] propertyDescriptors = BeanUtils
|
||||
.getPropertyDescriptors(obj.getClass());
|
||||
for (PropertyDescriptor pd : propertyDescriptors) {
|
||||
if (isSimpleType(pd.getPropertyType())) {
|
||||
Object value = bw.getPropertyValue(pd.getName());
|
||||
String keyToUse = ("id".equals(pd.getName()) ? "_id" : pd
|
||||
.getName());
|
||||
|
||||
if (isValidProperty(pd)) {
|
||||
|
||||
//TODO validate Enums...
|
||||
|
||||
// This will leverage the conversion service.
|
||||
dbo.put(keyToUse, value);
|
||||
} else {
|
||||
logger.warn("Unable to map property " + pd.getName() + ". Skipping.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Object read(Class<? extends Object> clazz, DBObject dbo) {
|
||||
|
||||
Assert.state(clazz != null, "Mapped class was not specified");
|
||||
Object mappedObject = BeanUtils.instantiate(clazz);
|
||||
BeanWrapper bw = PropertyAccessorFactory
|
||||
.forBeanPropertyAccess(mappedObject);
|
||||
initBeanWrapper(bw);
|
||||
|
||||
// Iterate over properties of the object.
|
||||
PropertyDescriptor[] propertyDescriptors = BeanUtils
|
||||
.getPropertyDescriptors(clazz);
|
||||
for (PropertyDescriptor pd : propertyDescriptors) {
|
||||
if (isSimpleType(pd.getPropertyType())) {
|
||||
if (dbo.containsField(pd.getName())) {
|
||||
Object value = dbo.get(pd.getName());
|
||||
if (value instanceof ObjectId) {
|
||||
setObjectIdOnObject(bw, pd, (ObjectId) value);
|
||||
} else {
|
||||
if (isValidProperty(pd)) {
|
||||
// This will leverage the conversion service.
|
||||
bw.setPropertyValue(pd.getName(),
|
||||
dbo.get(pd.getName()));
|
||||
} else {
|
||||
logger.warn("Unable to map DBObject field "
|
||||
+ pd.getName() + " to property "
|
||||
+ pd.getName() + ". Skipping.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mappedObject;
|
||||
}
|
||||
|
||||
protected void setObjectIdOnObject(BeanWrapper bw, PropertyDescriptor pd,
|
||||
ObjectId value) {
|
||||
// TODO strategy for setting the id field. suggest looking for public
|
||||
// property 'Id' or private field id or _id;
|
||||
|
||||
}
|
||||
|
||||
protected boolean isValidProperty(PropertyDescriptor descriptor) {
|
||||
return (descriptor.getReadMethod() != null && descriptor
|
||||
.getWriteMethod() != null);
|
||||
}
|
||||
|
||||
protected boolean isSimpleType(Class propertyType) {
|
||||
if (propertyType == null)
|
||||
return false;
|
||||
if (propertyType.isArray()) {
|
||||
return isSimpleType(propertyType.getComponentType());
|
||||
}
|
||||
return SIMPLE_TYPES.contains(propertyType.getName());
|
||||
}
|
||||
|
||||
protected void initBeanWrapper(BeanWrapper bw) {
|
||||
bw.setConversionService(conversionService);
|
||||
}
|
||||
|
||||
}
|
||||
13
spring-datastore-mongodb/src/test/resources/log4j.properties
Normal file
13
spring-datastore-mongodb/src/test/resources/log4j.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.category.org.apache.activemq=ERROR
|
||||
log4j.category.org.springframework.batch=DEBUG
|
||||
log4j.category.org.springframework.transaction=INFO
|
||||
|
||||
log4j.category.org.hibernate.SQL=DEBUG
|
||||
# for debugging datasource initialization
|
||||
# log4j.category.test.jdbc=DEBUG
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<import resource="classpath:/META-INF/spring/app-context.xml"/>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user