From c77facda907c7980bf4013abae25a9dca677caa1 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 20 Jul 2017 14:50:21 +0200 Subject: [PATCH] DATAMONGO-1733 - Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../data/mongodb/core/MongoTemplate.java | 69 +++++++++---------- .../ExecutableFindOperationSupportTests.java | 9 ++- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index d86671b8f..83d33d16b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/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.*; 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, private static final String ID_FIELD = "_id"; private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE; private static final Collection ITERABLE_CLASSES; - public static final SpelAwareProxyProjectionFactory PROJECTION_FACTORY = new SpelAwareProxyProjectionFactory(); static { @@ -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, 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, 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, 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, * @param * @since 2.0 */ - class ProjectingReadCallback implements DocumentCallback { - - private final Class entityType; - private final Class targetType; - private final String collectionName; - private final EntityReader reader; - - ProjectingReadCallback(EntityReader reader, Class entityType, Class targetType, - String collectionName) { + @RequiredArgsConstructor + private class ProjectingReadCallback implements DocumentCallback { - this.reader = reader; - this.entityType = entityType; - this.targetType = targetType; - this.collectionName = collectionName; - } + private final @NonNull EntityReader reader; + private final @NonNull Class entityType; + private final @NonNull Class 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; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java index 889e4054b..099f810f3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java @@ -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; }