Skip to content

Commit c77facd

Browse files
committed
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.
1 parent b4c213b commit c77facd

File tree

2 files changed

+39
-39
lines changed

2 files changed

+39
-39
lines changed

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

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020

2121
import lombok.AccessLevel;
2222
import lombok.AllArgsConstructor;
23+
import lombok.NonNull;
2324
import lombok.RequiredArgsConstructor;
2425

25-
import java.beans.PropertyDescriptor;
2626
import java.io.IOException;
2727
import java.util.ArrayList;
2828
import java.util.Collection;
@@ -180,7 +180,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
180180
private static final String ID_FIELD = "_id";
181181
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
182182
private static final Collection<String> ITERABLE_CLASSES;
183-
public static final SpelAwareProxyProjectionFactory PROJECTION_FACTORY = new SpelAwareProxyProjectionFactory();
184183

185184
static {
186185

@@ -198,6 +197,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
198197
private final PersistenceExceptionTranslator exceptionTranslator;
199198
private final QueryMapper queryMapper;
200199
private final UpdateMapper updateMapper;
200+
private final SpelAwareProxyProjectionFactory projectionFactory;
201201

202202
private WriteConcern writeConcern;
203203
private WriteConcernResolver writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
@@ -241,6 +241,7 @@ public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverte
241241
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
242242
this.queryMapper = new QueryMapper(this.mongoConverter);
243243
this.updateMapper = new UpdateMapper(this.mongoConverter);
244+
this.projectionFactory = new SpelAwareProxyProjectionFactory();
244245

245246
// We always have a mapping context in the converter, whether it's a simple one or not
246247
mappingContext = this.mongoConverter.getMappingContext();
@@ -303,10 +304,15 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
303304
prepareIndexCreator(applicationContext);
304305

305306
eventPublisher = applicationContext;
307+
306308
if (mappingContext instanceof ApplicationEventPublisherAware) {
307309
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
308310
}
311+
309312
resourceLoader = applicationContext;
313+
314+
projectionFactory.setBeanFactory(applicationContext);
315+
projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
310316
}
311317

312318
/**
@@ -2356,12 +2362,10 @@ private Document addFieldsForProjection(Document fields, Class<?> domainType, Cl
23562362
return fields;
23572363
}
23582364

2359-
ProjectionInformation projectionInformation = PROJECTION_FACTORY.getProjectionInformation(targetType);
2360-
if (projectionInformation.isClosed()) {
2365+
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(targetType);
23612366

2362-
for (PropertyDescriptor descriptor : projectionInformation.getInputProperties()) {
2363-
fields.append(descriptor.getName(), 1);
2364-
}
2367+
if (projectionInformation.isClosed()) {
2368+
projectionInformation.getInputProperties().forEach(it -> fields.append(it.getName(), 1));
23652369
}
23662370

23672371
return fields;
@@ -2612,46 +2616,35 @@ public T doWith(Document object) {
26122616
* @param <T>
26132617
* @since 2.0
26142618
*/
2615-
class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
2616-
2617-
private final Class<S> entityType;
2618-
private final Class<T> targetType;
2619-
private final String collectionName;
2620-
private final EntityReader<Object, Bson> reader;
2621-
2622-
ProjectingReadCallback(EntityReader<Object, Bson> reader, Class<S> entityType, Class<T> targetType,
2623-
String collectionName) {
2619+
@RequiredArgsConstructor
2620+
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
26242621

2625-
this.reader = reader;
2626-
this.entityType = entityType;
2627-
this.targetType = targetType;
2628-
this.collectionName = collectionName;
2629-
}
2622+
private final @NonNull EntityReader<Object, Bson> reader;
2623+
private final @NonNull Class<S> entityType;
2624+
private final @NonNull Class<T> targetType;
2625+
private final @NonNull String collectionName;
26302626

2627+
/*
2628+
* (non-Javadoc)
2629+
* @see org.springframework.data.mongodb.core.MongoTemplate.DocumentCallback#doWith(org.bson.Document)
2630+
*/
2631+
@SuppressWarnings("unchecked")
26312632
public T doWith(Document object) {
26322633

2633-
if (null != object) {
2634-
maybeEmitEvent(new AfterLoadEvent<>(object, targetType, collectionName));
2635-
}
2636-
2637-
T target = doRead(object, entityType, targetType);
2638-
2639-
if (null != target) {
2640-
maybeEmitEvent(new AfterConvertEvent<>(object, target, collectionName));
2634+
if (object == null) {
2635+
return null;
26412636
}
26422637

2643-
return target;
2644-
}
2645-
2646-
private T doRead(Document source, Class entityType, Class targetType) {
2647-
2648-
if (targetType != entityType && targetType.isInterface()) {
2638+
Class<?> typeToRead = targetType.isInterface() || targetType.isAssignableFrom(entityType) ? entityType
2639+
: targetType;
2640+
Object source = reader.read(typeToRead, object);
2641+
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, source) : source;
26492642

2650-
S target = (S) reader.read(entityType, source);
2651-
return (T) PROJECTION_FACTORY.createProjection(targetType, target);
2643+
if (result == null) {
2644+
maybeEmitEvent(new AfterConvertEvent<>(object, result, collectionName));
26522645
}
26532646

2654-
return (T) reader.read(targetType, source);
2647+
return (T) result;
26552648
}
26562649
}
26572650

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,16 @@ public void existsShouldReturnFalseWhenNoElementMatchesQuery() {
343343
assertThat(template.query(Person.class).matching(query(where("firstname").is("spock"))).exists()).isFalse();
344344
}
345345

346+
@Test // DATAMONGO-1734
347+
public void returnsTargetObjectDirectlyIfProjectionInterfaceIsImplemented() {
348+
assertThat(template.query(Person.class).as(Contact.class).all()).allMatch(it -> it instanceof Person);
349+
}
350+
351+
interface Contact {}
352+
346353
@Data
347354
@org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS)
348-
static class Person {
355+
static class Person implements Contact {
349356
@Id String id;
350357
String firstname;
351358
}

0 commit comments

Comments
 (0)