Skip to content

Commit 6f06cce

Browse files
committed
DATAMONGO-1012 - Identifier initialization for lazy DBRef proxies with field access.
We now initialize the ID property for proxies created for lazily initialized DBRefs. This will allow the lookup of ID properties for types that use field access without initializing the entire proxy.
1 parent 6fe7f22 commit 6f06cce

File tree

8 files changed

+267
-29
lines changed

8 files changed

+267
-29
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.convert;
17+
18+
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
19+
20+
import com.mongodb.DBRef;
21+
22+
/**
23+
* @author Oliver Gierke
24+
*/
25+
public interface DbRefProxyHandler {
26+
27+
Object populateId(MongoPersistentProperty property, DBRef source, Object proxy);
28+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ public interface DbRefResolver {
3939
* @param callback will never be {@literal null}.
4040
* @return
4141
*/
42-
Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback);
42+
Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
43+
DbRefProxyHandler proxyHandler);
4344

4445
/**
4546
* Creates a {@link DBRef} instance for the given {@link org.springframework.data.mongodb.core.mapping.DBRef}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.convert;
17+
18+
import org.springframework.data.mapping.context.MappingContext;
19+
import org.springframework.data.mapping.model.BeanWrapper;
20+
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
21+
import org.springframework.data.mapping.model.SpELContext;
22+
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
23+
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
24+
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
25+
26+
import com.mongodb.BasicDBObject;
27+
import com.mongodb.DBObject;
28+
import com.mongodb.DBRef;
29+
30+
/**
31+
* @author Oliver Gierke
32+
*/
33+
class DefaultDbRefProxyHandler implements DbRefProxyHandler {
34+
35+
private final SpELContext spELContext;
36+
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
37+
private final ValueResolver resolver;
38+
39+
/**
40+
* @param spELContext must not be {@literal null}.
41+
* @param conversionService must not be {@literal null}.
42+
* @param mappingContext must not be {@literal null}.
43+
*/
44+
public DefaultDbRefProxyHandler(SpELContext spELContext,
45+
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, ValueResolver resolver) {
46+
47+
this.spELContext = spELContext;
48+
this.mappingContext = mappingContext;
49+
this.resolver = resolver;
50+
}
51+
52+
/*
53+
* (non-Javadoc)
54+
* @see org.springframework.data.mongodb.core.convert.DbRefProxyHandler#populateId(com.mongodb.DBRef, java.lang.Object)
55+
*/
56+
@Override
57+
public Object populateId(MongoPersistentProperty property, DBRef source, Object proxy) {
58+
59+
if (source == null) {
60+
return proxy;
61+
}
62+
63+
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(property);
64+
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
65+
66+
if (idProperty.usePropertyAccess()) {
67+
return proxy;
68+
}
69+
70+
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(proxy, spELContext);
71+
BeanWrapper<Object> proxyWrapper = BeanWrapper.create(proxy, null);
72+
73+
DBObject object = new BasicDBObject(idProperty.getFieldName(), source.getId());
74+
ObjectPath objectPath = ObjectPath.ROOT.push(proxy, persistentEntity, null);
75+
proxyWrapper.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath));
76+
77+
return proxy;
78+
}
79+
}

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,14 @@ public DefaultDbRefResolver(MongoDbFactory mongoDbFactory) {
7777
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#resolveDbRef(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.convert.DbRefResolverCallback)
7878
*/
7979
@Override
80-
public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback) {
80+
public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
81+
DbRefProxyHandler handler) {
8182

8283
Assert.notNull(property, "Property must not be null!");
8384
Assert.notNull(callback, "Callback must not be null!");
8485

8586
if (isLazyDbRef(property)) {
86-
return createLazyLoadingProxy(property, dbref, callback);
87+
return createLazyLoadingProxy(property, dbref, callback, handler);
8788
}
8889

8990
return callback.resolve(property);
@@ -112,7 +113,8 @@ public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef ann
112113
* @param callback must not be {@literal null}.
113114
* @return
114115
*/
115-
private Object createLazyLoadingProxy(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback) {
116+
private Object createLazyLoadingProxy(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
117+
DbRefProxyHandler handler) {
116118

117119
Class<?> propertyType = property.getType();
118120
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, dbref, exceptionTranslator, callback);
@@ -122,7 +124,7 @@ private Object createLazyLoadingProxy(MongoPersistentProperty property, DBRef db
122124
Factory factory = (Factory) objenesis.newInstance(getEnhancedTypeFor(propertyType));
123125
factory.setCallbacks(new Callback[] { interceptor });
124126

125-
return factory;
127+
return handler.populateId(property, dbref, factory);
126128
}
127129

128130
ProxyFactory proxyFactory = new ProxyFactory();
@@ -135,7 +137,7 @@ private Object createLazyLoadingProxy(MongoPersistentProperty property, DBRef db
135137
proxyFactory.addInterface(propertyType);
136138
proxyFactory.addAdvice(interceptor);
137139

138-
return proxyFactory.getProxy();
140+
return handler.populateId(property, dbref, proxyFactory.getProxy());
139141
}
140142

141143
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.convert;
17+
18+
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
19+
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
20+
21+
import com.mongodb.DBObject;
22+
23+
/**
24+
* Default implementation of {@link DbRefResolverCallback}.
25+
*
26+
* @author Oliver Gierke
27+
*/
28+
class DefaultDbRefResolverCallback implements DbRefResolverCallback {
29+
30+
private final DBObject surroundingObject;
31+
private final ObjectPath path;
32+
private final ValueResolver resolver;
33+
private final SpELExpressionEvaluator evaluator;
34+
35+
/**
36+
* Creates a new {@link DefaultDbRefResolverCallback} using the given {@link DBObject}, {@link ObjectPath},
37+
* {@link ValueResolver} and {@link SpELExpressionEvaluator}.
38+
*
39+
* @param surroundingObject must not be {@literal null}.
40+
* @param path must not be {@literal null}.
41+
* @param evaluator must not be {@literal null}.
42+
* @param resolver must not be {@literal null}.
43+
*/
44+
public DefaultDbRefResolverCallback(DBObject surroundingObject, ObjectPath path, SpELExpressionEvaluator evaluator,
45+
ValueResolver resolver) {
46+
47+
this.surroundingObject = surroundingObject;
48+
this.path = path;
49+
this.resolver = resolver;
50+
this.evaluator = evaluator;
51+
}
52+
53+
/*
54+
* (non-Javadoc)
55+
* @see org.springframework.data.mongodb.core.convert.DbRefResolverCallback#resolve(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
56+
*/
57+
@Override
58+
public Object resolve(MongoPersistentProperty property) {
59+
return resolver.getValueInternal(property, surroundingObject, evaluator, path);
60+
}
61+
}

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

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import org.springframework.context.ApplicationContextAware;
3232
import org.springframework.core.convert.ConversionException;
3333
import org.springframework.core.convert.ConversionService;
34-
import org.springframework.core.convert.support.ConversionServiceFactory;
34+
import org.springframework.core.convert.support.DefaultConversionService;
3535
import org.springframework.data.convert.CollectionFactory;
3636
import org.springframework.data.convert.EntityInstantiator;
3737
import org.springframework.data.convert.TypeMapper;
@@ -74,14 +74,15 @@
7474
* @author Thomas Darimont
7575
* @author Christoph Strobl
7676
*/
77-
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
77+
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, ValueResolver {
7878

7979
protected static final Logger LOGGER = LoggerFactory.getLogger(MappingMongoConverter.class);
8080

8181
protected final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
8282
protected final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
8383
protected final QueryMapper idMapper;
8484
protected final DbRefResolver dbRefResolver;
85+
8586
protected ApplicationContext applicationContext;
8687
protected MongoTypeMapper typeMapper;
8788
protected String mapKeyDotReplacement = null;
@@ -94,11 +95,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
9495
* @param mongoDbFactory must not be {@literal null}.
9596
* @param mappingContext must not be {@literal null}.
9697
*/
97-
@SuppressWarnings("deprecation")
9898
public MappingMongoConverter(DbRefResolver dbRefResolver,
9999
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
100100

101-
super(ConversionServiceFactory.createDefaultConversionService());
101+
super(new DefaultConversionService());
102102

103103
Assert.notNull(dbRefResolver, "DbRefResolver must not be null!");
104104
Assert.notNull(mappingContext, "MappingContext must not be null!");
@@ -277,21 +277,21 @@ public void doWithPersistentProperty(MongoPersistentProperty prop) {
277277
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
278278
public void doWithAssociation(Association<MongoPersistentProperty> association) {
279279

280-
MongoPersistentProperty property = association.getInverse();
280+
final MongoPersistentProperty property = association.getInverse();
281281
Object value = dbo.get(property.getName());
282282

283283
if (value == null) {
284284
return;
285285
}
286286

287287
DBRef dbref = value instanceof DBRef ? (DBRef) value : null;
288-
wrapper.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, new DbRefResolverCallback() {
289288

290-
@Override
291-
public Object resolve(MongoPersistentProperty property) {
292-
return getValueInternal(property, dbo, evaluator, currentPath);
293-
}
294-
}));
289+
DbRefProxyHandler handler = new DefaultDbRefProxyHandler(spELContext, mappingContext,
290+
MappingMongoConverter.this);
291+
DbRefResolverCallback callback = new DefaultDbRefResolverCallback(dbo, currentPath, evaluator,
292+
MappingMongoConverter.this);
293+
294+
wrapper.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
295295
}
296296
});
297297

@@ -813,9 +813,13 @@ protected DBRef createDBRef(Object target, MongoPersistentProperty property) {
813813
idMapper.convertId(id));
814814
}
815815

816-
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator evaluator,
816+
/*
817+
* (non-Javadoc)
818+
* @see org.springframework.data.mongodb.core.convert.ValueResolver#getValueInternal(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, com.mongodb.DBObject, org.springframework.data.mapping.model.SpELExpressionEvaluator, java.lang.Object)
819+
*/
820+
@Override
821+
public Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator evaluator,
817822
ObjectPath path) {
818-
819823
return new MongoDbPropertyValueProvider(dbo, evaluator, path).getPropertyValue(prop);
820824
}
821825

@@ -1036,11 +1040,11 @@ private class MongoDbPropertyValueProvider implements PropertyValueProvider<Mong
10361040

10371041
/**
10381042
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link SpELExpressionEvaluator} and
1039-
* parent object.
1043+
* {@link ObjectPath}.
10401044
*
10411045
* @param source must not be {@literal null}.
10421046
* @param evaluator must not be {@literal null}.
1043-
* @param parent can be {@literal null}.
1047+
* @param path can be {@literal null}.
10441048
*/
10451049
public MongoDbPropertyValueProvider(DBObject source, SpELExpressionEvaluator evaluator, ObjectPath path) {
10461050

@@ -1078,7 +1082,7 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
10781082
private class ConverterAwareSpELExpressionParameterValueProvider extends
10791083
SpELExpressionParameterValueProvider<MongoPersistentProperty> {
10801084

1081-
private final ObjectPath parent;
1085+
private final ObjectPath path;
10821086

10831087
/**
10841088
* Creates a new {@link ConverterAwareSpELExpressionParameterValueProvider}.
@@ -1088,10 +1092,10 @@ private class ConverterAwareSpELExpressionParameterValueProvider extends
10881092
* @param delegate must not be {@literal null}.
10891093
*/
10901094
public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator,
1091-
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate, ObjectPath parent) {
1095+
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate, ObjectPath path) {
10921096

10931097
super(evaluator, conversionService, delegate);
1094-
this.parent = parent;
1098+
this.path = path;
10951099
}
10961100

10971101
/*
@@ -1100,23 +1104,23 @@ public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluato
11001104
*/
11011105
@Override
11021106
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, MongoPersistentProperty> parameter) {
1103-
return readValue(object, parameter.getType(), parent);
1107+
return readValue(object, parameter.getType(), path);
11041108
}
11051109
}
11061110

11071111
@SuppressWarnings("unchecked")
1108-
private <T> T readValue(Object value, TypeInformation<?> type, ObjectPath parent) {
1112+
private <T> T readValue(Object value, TypeInformation<?> type, ObjectPath path) {
11091113

11101114
Class<?> rawType = type.getType();
11111115

11121116
if (conversions.hasCustomReadTarget(value.getClass(), rawType)) {
11131117
return (T) conversionService.convert(value, rawType);
11141118
} else if (value instanceof DBRef) {
1115-
return potentiallyReadOrResolveDbRef((DBRef) value, type, parent, rawType);
1119+
return potentiallyReadOrResolveDbRef((DBRef) value, type, path, rawType);
11161120
} else if (value instanceof BasicDBList) {
1117-
return (T) readCollectionOrArray(type, (BasicDBList) value, parent);
1121+
return (T) readCollectionOrArray(type, (BasicDBList) value, path);
11181122
} else if (value instanceof DBObject) {
1119-
return (T) read(type, (DBObject) value, parent);
1123+
return (T) read(type, (DBObject) value, path);
11201124
} else {
11211125
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
11221126
}

0 commit comments

Comments
 (0)