Skip to content

Commit e208cef

Browse files
Enhance support for linking entities.
Add initial support for an alternative to the existing DBRef scenario. The enhancement allows to store and retrieve linked entites via their id or a customizable lookup query.
1 parent 7c44bf6 commit e208cef

18 files changed

+1779
-35
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* @author Mark Paluch
3636
* @since 1.4
3737
*/
38-
public interface DbRefResolver {
38+
public interface DbRefResolver extends ReferenceResolver {
3939

4040
/**
4141
* Resolves the given {@link DBRef} into an object of the given {@link MongoPersistentProperty}'s type. The method

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

+14-15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.springframework.data.mongodb.LazyLoadingException;
4747
import org.springframework.data.mongodb.MongoDatabaseFactory;
4848
import org.springframework.data.mongodb.MongoDatabaseUtils;
49+
import org.springframework.data.mongodb.core.convert.ReferenceLoader.ReferenceFilter;
4950
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
5051
import org.springframework.lang.Nullable;
5152
import org.springframework.objenesis.ObjenesisStd;
@@ -67,7 +68,7 @@
6768
* @author Mark Paluch
6869
* @since 1.4
6970
*/
70-
public class DefaultDbRefResolver implements DbRefResolver {
71+
public class DefaultDbRefResolver extends DefaultReferenceResolver implements DbRefResolver, ReferenceResolver {
7172

7273
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDbRefResolver.class);
7374

@@ -82,6 +83,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
8283
*/
8384
public DefaultDbRefResolver(MongoDatabaseFactory mongoDbFactory) {
8485

86+
super(new DefaultReferenceLoader(mongoDbFactory));
87+
8588
Assert.notNull(mongoDbFactory, "MongoDbFactory translator must not be null!");
8689

8790
this.mongoDbFactory = mongoDbFactory;
@@ -114,17 +117,7 @@ public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbr
114117
*/
115118
@Override
116119
public Document fetch(DBRef dbRef) {
117-
118-
MongoCollection<Document> mongoCollection = getCollection(dbRef);
119-
120-
if (LOGGER.isTraceEnabled()) {
121-
LOGGER.trace("Fetching DBRef '{}' from {}.{}.", dbRef.getId(),
122-
StringUtils.hasText(dbRef.getDatabaseName()) ? dbRef.getDatabaseName()
123-
: mongoCollection.getNamespace().getDatabaseName(),
124-
dbRef.getCollectionName());
125-
}
126-
127-
return mongoCollection.find(Filters.eq("_id", dbRef.getId())).first();
120+
return getReferenceLoader().fetch(ReferenceFilter.singleReferenceFilter(Filters.eq("_id", dbRef.getId())), ReferenceContext.fromDBRef(dbRef));
128121
}
129122

130123
/*
@@ -164,9 +157,9 @@ public List<Document> bulkFetch(List<DBRef> refs) {
164157
databaseSource.getCollectionName());
165158
}
166159

167-
List<Document> result = mongoCollection //
168-
.find(new Document("_id", new Document("$in", ids))) //
169-
.into(new ArrayList<>());
160+
List<Document> result = getReferenceLoader()
161+
.bulkFetch(ReferenceFilter.referenceFilter(new Document("_id", new Document("$in", ids))), ReferenceContext.fromDBRef(refs.iterator().next()))
162+
.collect(Collectors.toList());
170163

171164
return ids.stream() //
172165
.flatMap(id -> documentWithId(id, result)) //
@@ -504,4 +497,10 @@ protected MongoCollection<Document> getCollection(DBRef dbref) {
504497
return MongoDatabaseUtils.getDatabase(dbref.getDatabaseName(), mongoDbFactory)
505498
.getCollection(dbref.getCollectionName(), Document.class);
506499
}
500+
501+
protected MongoCollection<Document> getCollection(ReferenceContext context) {
502+
503+
return MongoDatabaseUtils.getDatabase(context.database, mongoDbFactory).getCollection(context.collection,
504+
Document.class);
505+
}
507506
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2021 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+
* https://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 java.util.stream.Stream;
19+
import java.util.stream.StreamSupport;
20+
21+
import org.bson.Document;
22+
import org.bson.conversions.Bson;
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
import org.springframework.data.mongodb.MongoDatabaseFactory;
26+
import org.springframework.data.mongodb.MongoDatabaseUtils;
27+
import org.springframework.data.mongodb.core.convert.ReferenceResolver.ReferenceContext;
28+
import org.springframework.lang.Nullable;
29+
import org.springframework.util.Assert;
30+
import org.springframework.util.StringUtils;
31+
32+
import com.mongodb.client.FindIterable;
33+
import com.mongodb.client.MongoCollection;
34+
35+
/**
36+
* @author Christoph Strobl
37+
*/
38+
public class DefaultReferenceLoader implements ReferenceLoader {
39+
40+
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultReferenceLoader.class);
41+
42+
private final MongoDatabaseFactory mongoDbFactory;
43+
44+
public DefaultReferenceLoader(MongoDatabaseFactory mongoDbFactory) {
45+
46+
Assert.notNull(mongoDbFactory, "MongoDbFactory translator must not be null!");
47+
48+
this.mongoDbFactory = mongoDbFactory;
49+
}
50+
51+
@Override
52+
public Stream<Document> bulkFetch(ReferenceFilter filter, ReferenceContext context) {
53+
54+
MongoCollection<Document> collection = getCollection(context);
55+
56+
if (LOGGER.isTraceEnabled()) {
57+
LOGGER.trace("Bulk fetching {} from {}.{}.", filter,
58+
StringUtils.hasText(context.getDatabase()) ? context.getDatabase()
59+
: collection.getNamespace().getDatabaseName(),
60+
context.getCollection());
61+
}
62+
63+
return filter.apply(collection);
64+
}
65+
66+
protected MongoCollection<Document> getCollection(ReferenceContext context) {
67+
68+
return MongoDatabaseUtils.getDatabase(context.database, mongoDbFactory).getCollection(context.collection,
69+
Document.class);
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2021 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+
* https://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 java.util.function.BiFunction;
19+
import java.util.stream.Stream;
20+
21+
import org.bson.Document;
22+
import org.bson.conversions.Bson;
23+
import org.springframework.data.mongodb.core.convert.ReferenceLoader.ReferenceFilter;
24+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
25+
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
26+
import org.springframework.lang.Nullable;
27+
28+
/**
29+
* @author Christoph Strobl
30+
*/
31+
public class DefaultReferenceResolver implements ReferenceResolver {
32+
33+
private final ReferenceLoader referenceLoader;
34+
35+
public DefaultReferenceResolver(ReferenceLoader referenceLoader) {
36+
this.referenceLoader = referenceLoader;
37+
}
38+
39+
@Override
40+
public ReferenceLoader getReferenceLoader() {
41+
return referenceLoader;
42+
}
43+
44+
@Nullable
45+
@Override
46+
public Object resolveReference(MongoPersistentProperty property, Object source, ReferenceReader referenceReader,
47+
BiFunction<ReferenceContext, ReferenceFilter, Stream<Document>> lookupFunction) {
48+
49+
if (isLazyReference(property)) {
50+
return createLazyLoadingProxy(property, source, referenceReader, lookupFunction);
51+
}
52+
53+
return referenceReader.readReference(property, source, lookupFunction);
54+
}
55+
56+
private Object createLazyLoadingProxy(MongoPersistentProperty property, Object source,
57+
ReferenceReader referenceReader, BiFunction<ReferenceContext, ReferenceFilter, Stream<Document>> lookupFunction) {
58+
return new LazyLoadingProxyGenerator(referenceReader).createLazyLoadingProxy(property, source, lookupFunction);
59+
}
60+
61+
protected boolean isLazyReference(MongoPersistentProperty property) {
62+
63+
if (property.findAnnotation(DocumentReference.class) != null) {
64+
return property.findAnnotation(DocumentReference.class).lazy();
65+
}
66+
67+
return property.getDBRef() != null && property.getDBRef().lazy();
68+
}
69+
}

0 commit comments

Comments
 (0)