Browse Source

DATAMONGO-1733 - Polishing.

Tiny improvements to the ProjectingReadCallback as we now skip null values completely. Made the ProjectionFactory an instance variable to make sure we propagate the BeanFactory and BeanClassLoader in setApplicationContext(…). Added unit test to verify instances aren't proxied unnecessarily if the interface asked for is already implemented by the target.

Original pull request: #486.
Related tickets: DATACMNS-1121.
pull/487/head
Oliver Gierke 9 years ago
parent
commit
c77facda90
  1. 69
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 9
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java

69
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

@ -20,9 +20,9 @@ import static org.springframework.data.mongodb.core.query.SerializationUtils.*; @@ -20,9 +20,9 @@ import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@ -180,7 +180,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -180,7 +180,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private static final String ID_FIELD = "_id";
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
private static final Collection<String> ITERABLE_CLASSES;
public static final SpelAwareProxyProjectionFactory PROJECTION_FACTORY = new SpelAwareProxyProjectionFactory();
static {
@ -198,6 +197,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -198,6 +197,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final PersistenceExceptionTranslator exceptionTranslator;
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final SpelAwareProxyProjectionFactory projectionFactory;
private WriteConcern writeConcern;
private WriteConcernResolver writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
@ -241,6 +241,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -241,6 +241,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
this.queryMapper = new QueryMapper(this.mongoConverter);
this.updateMapper = new UpdateMapper(this.mongoConverter);
this.projectionFactory = new SpelAwareProxyProjectionFactory();
// We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this.mongoConverter.getMappingContext();
@ -303,10 +304,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -303,10 +304,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
prepareIndexCreator(applicationContext);
eventPublisher = applicationContext;
if (mappingContext instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
}
resourceLoader = applicationContext;
projectionFactory.setBeanFactory(applicationContext);
projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
}
/**
@ -2356,12 +2362,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -2356,12 +2362,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return fields;
}
ProjectionInformation projectionInformation = PROJECTION_FACTORY.getProjectionInformation(targetType);
if (projectionInformation.isClosed()) {
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(targetType);
for (PropertyDescriptor descriptor : projectionInformation.getInputProperties()) {
fields.append(descriptor.getName(), 1);
}
if (projectionInformation.isClosed()) {
projectionInformation.getInputProperties().forEach(it -> fields.append(it.getName(), 1));
}
return fields;
@ -2612,46 +2616,35 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -2612,46 +2616,35 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @param <T>
* @since 2.0
*/
class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
private final Class<S> entityType;
private final Class<T> targetType;
private final String collectionName;
private final EntityReader<Object, Bson> reader;
ProjectingReadCallback(EntityReader<Object, Bson> reader, Class<S> entityType, Class<T> targetType,
String collectionName) {
@RequiredArgsConstructor
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
this.reader = reader;
this.entityType = entityType;
this.targetType = targetType;
this.collectionName = collectionName;
}
private final @NonNull EntityReader<Object, Bson> reader;
private final @NonNull Class<S> entityType;
private final @NonNull Class<T> targetType;
private final @NonNull String collectionName;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoTemplate.DocumentCallback#doWith(org.bson.Document)
*/
@SuppressWarnings("unchecked")
public T doWith(Document object) {
if (null != object) {
maybeEmitEvent(new AfterLoadEvent<>(object, targetType, collectionName));
}
T target = doRead(object, entityType, targetType);
if (null != target) {
maybeEmitEvent(new AfterConvertEvent<>(object, target, collectionName));
if (object == null) {
return null;
}
return target;
}
private T doRead(Document source, Class entityType, Class targetType) {
if (targetType != entityType && targetType.isInterface()) {
Class<?> typeToRead = targetType.isInterface() || targetType.isAssignableFrom(entityType) ? entityType
: targetType;
Object source = reader.read(typeToRead, object);
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, source) : source;
S target = (S) reader.read(entityType, source);
return (T) PROJECTION_FACTORY.createProjection(targetType, target);
if (result == null) {
maybeEmitEvent(new AfterConvertEvent<>(object, result, collectionName));
}
return (T) reader.read(targetType, source);
return (T) result;
}
}

9
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java

@ -343,9 +343,16 @@ public class ExecutableFindOperationSupportTests { @@ -343,9 +343,16 @@ public class ExecutableFindOperationSupportTests {
assertThat(template.query(Person.class).matching(query(where("firstname").is("spock"))).exists()).isFalse();
}
@Test // DATAMONGO-1734
public void returnsTargetObjectDirectlyIfProjectionInterfaceIsImplemented() {
assertThat(template.query(Person.class).as(Contact.class).all()).allMatch(it -> it instanceof Person);
}
interface Contact {}
@Data
@org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS)
static class Person {
static class Person implements Contact {
@Id String id;
String firstname;
}

Loading…
Cancel
Save