diff --git a/pom.xml b/pom.xml index 9a1889723d..4f10692c9d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 5.0.0-SNAPSHOT + 5.0.x-GH-5031-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index fc88571622..613c93d98d 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -15,7 +15,7 @@ org.springframework.data spring-data-mongodb-parent - 5.0.0-SNAPSHOT + 5.0.x-GH-5031-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 595e5a4250..cdc32798ab 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 5.0.0-SNAPSHOT + 5.0.x-GH-5031-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java index 13c0198aa0..dc6f6a576e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java @@ -19,11 +19,13 @@ import org.bson.Document; import org.jspecify.annotations.Nullable; + import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.core.mapping.Wrapped; import com.mongodb.DBRef; @@ -32,23 +34,9 @@ * @author Christoph Strobl * @author Mark Paluch */ -class DefaultDbRefProxyHandler implements DbRefProxyHandler { - - private final MappingContext, MongoPersistentProperty> mappingContext; - private final ValueResolver resolver; - private final Function evaluatorFactory; - - /** - * @param mappingContext must not be {@literal null}. - * @param resolver must not be {@literal null}. - */ - public DefaultDbRefProxyHandler(MappingContext, MongoPersistentProperty> mappingContext, - ValueResolver resolver, Function evaluatorFactory) { - - this.mappingContext = mappingContext; - this.resolver = resolver; - this.evaluatorFactory = evaluatorFactory; - } +record DefaultDbRefProxyHandler( + MappingContext, MongoPersistentProperty> mappingContext, ValueResolver resolver, + Function evaluatorFactory) implements DbRefProxyHandler { @Override public Object populateId(MongoPersistentProperty property, @Nullable DBRef source, Object proxy) { @@ -65,11 +53,12 @@ public Object populateId(MongoPersistentProperty property, @Nullable DBRef sourc } ValueExpressionEvaluator evaluator = evaluatorFactory.apply(proxy); - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor((Wrapped) () -> proxy); Document object = new Document(idProperty.getFieldName(), source.getId()); ObjectPath objectPath = ObjectPath.ROOT.push(proxy, entity, null); - accessor.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath)); + accessor.setProperty(idProperty, + resolver.getValueInternal(idProperty, object, evaluator, objectPath)); return proxy; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java index 6329d74d4f..241ba46478 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java @@ -17,6 +17,8 @@ import org.jspecify.annotations.Nullable; +import org.springframework.data.mongodb.core.mapping.Wrapped; + import com.mongodb.DBRef; /** @@ -28,7 +30,7 @@ * @since 1.5 * @see LazyLoadingProxyFactory */ -public interface LazyLoadingProxy { +public interface LazyLoadingProxy extends Wrapped { /** * Initializes the proxy and returns the wrapped value. @@ -36,6 +38,7 @@ public interface LazyLoadingProxy { * @return * @since 1.5 */ + @Override Object getTarget(); /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java index e865009319..f302c36572 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java @@ -26,6 +26,7 @@ import java.util.Map; import org.jspecify.annotations.Nullable; + import org.springframework.data.annotation.Id; import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.expression.ValueExpression; @@ -33,6 +34,7 @@ import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.MappingException; +import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.mongodb.MongoCollectionUtils; @@ -188,6 +190,16 @@ public void verify() { verifyFieldTypes(); } + @Override + public boolean isNew(Object bean) { + return super.isNew(Wrapped.getTargetObject(bean)); + } + + @Override + public PersistentPropertyAccessor getPropertyAccessor(B bean) { + return super.getPropertyAccessor(Wrapped.getTargetObject(bean)); + } + @Override public EvaluationContext getEvaluationContext(@Nullable Object rootObject) { return super.getEvaluationContext(rootObject); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java index f1d67e4ae8..7eb07a109a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java @@ -83,7 +83,7 @@ default boolean hasCollation() { /** * Get the entities shard key if defined. * - * @return {@link ShardKey#none()} if not not set. + * @return {@link ShardKey#none()} if not set. * @since 3.0 */ ShardKey getShardKey(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Wrapped.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Wrapped.java new file mode 100644 index 0000000000..e85219bcda --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Wrapped.java @@ -0,0 +1,43 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.mapping; + +/** + * Marker interface for objects that wrap another object, providing a way to retrieve the wrapped value. + * + * @author Mark Paluch + * @since 5.0 + */ +public interface Wrapped { + + /** + * Returns the wrapped value. + * + * @return + */ + Object getTarget(); + + /** + * Unwraps the given object if it is an instance of {@link Wrapped}. If not, returns the object as-is. + * + * @param object the object to unwrap, must not be {@literal null}. + * @return the unwrapped object or the original object if it is not wrapped. + */ + static T getTargetObject(T object) { + return object instanceof Wrapped w ? (T) w.getTarget() : object; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java index 443108d2f0..c3989b7f73 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java @@ -17,6 +17,7 @@ import org.bson.types.ObjectId; import org.jspecify.annotations.Nullable; + import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.query.Collation; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java index ee411eb7c7..543e06b024 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java @@ -19,16 +19,23 @@ import java.util.Date; +import org.bson.types.ObjectId; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.mapping.callback.EntityCallbacks; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.DocumentReference; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; /** * Integration test for the auditing support. @@ -36,14 +43,17 @@ * @author Oliver Gierke * @author Mark Paluch */ -public class AuditingIntegrationTests { +@ContextConfiguration(locations = "auditing.xml") +@ExtendWith(SpringExtension.class) +class AuditingIntegrationTests { - @Test // DATAMONGO-577, DATAMONGO-800, DATAMONGO-883, DATAMONGO-2261 - public void enablesAuditingAndSetsPropertiesAccordingly() throws Exception { + @Autowired MongoMappingContext mappingContext; + @Autowired ApplicationContext context; + @Autowired MongoTemplate template; - AbstractApplicationContext context = new ClassPathXmlApplicationContext("auditing.xml", getClass()); + @Test // DATAMONGO-577, DATAMONGO-800, DATAMONGO-883, DATAMONGO-2261 + void enablesAuditingAndSetsPropertiesAccordingly() throws Exception { - MongoMappingContext mappingContext = context.getBean(MongoMappingContext.class); mappingContext.getPersistentEntity(Entity.class); EntityCallbacks callbacks = EntityCallbacks.create(context); @@ -55,18 +65,67 @@ public void enablesAuditingAndSetsPropertiesAccordingly() throws Exception { assertThat(entity.modified).isEqualTo(entity.created); Thread.sleep(10); - entity.id = 1L; + entity.id = "foo"; entity = callbacks.callback(BeforeConvertCallback.class, entity, "collection-1"); assertThat(entity.created).isNotNull(); assertThat(entity.modified).isNotEqualTo(entity.created); - context.close(); } - class Entity { + @Test // GH-5031 + void handlesDocumentReferenceAuditingCorrectly() throws InterruptedException { + + template.remove(TopDocumentReferenceLevelEntity.class).all(); + + Entity entity = new Entity(); + template.insert(entity); + + TopDocumentReferenceLevelEntity tle = new TopDocumentReferenceLevelEntity(); + tle.entity = entity; + template.insert(tle); + + Thread.sleep(200); + + TopDocumentReferenceLevelEntity loadAndModify = template.findById(tle.id, TopDocumentReferenceLevelEntity.class); + Date created = loadAndModify.entity.getCreated(); + Date modified = loadAndModify.entity.getModified(); + template.save(loadAndModify.entity); + + TopDocumentReferenceLevelEntity loaded = template.findById(tle.id, TopDocumentReferenceLevelEntity.class); + + assertThat(loaded.entity.getCreated()).isEqualTo(created); + assertThat(loaded.entity.getModified()).isNotEqualTo(modified); + } + + @Test // GH-5031 + void handlesDbRefAuditingCorrectly() throws InterruptedException { + + template.remove(TopDbRefLevelEntity.class).all(); + + Entity entity = new Entity(); + template.insert(entity); + + TopDbRefLevelEntity tle = new TopDbRefLevelEntity(); + tle.entity = entity; + template.insert(tle); + + Thread.sleep(200); + + TopDbRefLevelEntity loadAndModify = template.findById(tle.id, TopDbRefLevelEntity.class); + Date created = loadAndModify.entity.getCreated(); + Date modified = loadAndModify.entity.getModified(); + template.save(loadAndModify.entity); + + TopDbRefLevelEntity loaded = template.findById(tle.id, TopDbRefLevelEntity.class); - @Id Long id; + assertThat(loaded.entity.getCreated()).isEqualTo(created); + assertThat(loaded.entity.getModified()).isNotEqualTo(modified); + } + + static class Entity { + + @Id String id; @CreatedDate Date created; Date modified; @@ -74,5 +133,32 @@ class Entity { public Date getModified() { return modified; } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setModified(Date modified) { + this.modified = modified; + } } + + static class TopDocumentReferenceLevelEntity { + + @Id ObjectId id; + @DocumentReference(lazy = true) Entity entity; + + } + + static class TopDbRefLevelEntity { + + @Id ObjectId id; + @DBRef(lazy = true) Entity entity; + + } + } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java index 2628a3a5ca..c077ed6462 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java @@ -44,11 +44,11 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.DirectFieldAccessor; import org.springframework.data.annotation.AccessType; import org.springframework.data.annotation.AccessType.Type; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceCreator; -import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.MongoExceptionTranslator; @@ -487,11 +487,11 @@ void shouldEagerlyResolveIdPropertyWithFieldAccess() { ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, object); - PersistentPropertyAccessor accessor = propertyEntity.getPropertyAccessor(result.dbRefToConcreteType); + DirectFieldAccessor accessor = new DirectFieldAccessor(result.dbRefToConcreteType); MongoPersistentProperty idProperty = mappingContext.getRequiredPersistentEntity(LazyDbRefTarget.class) .getIdProperty(); - assertThat(accessor.getProperty(idProperty)).isNotNull(); + assertThat(accessor.getPropertyValue(idProperty.getName())).isNotNull(); assertProxyIsResolved(result.dbRefToConcreteType, false); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java index eeca60bc3e..0905705909 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java @@ -77,6 +77,7 @@ public enum Sex { User unwrappedUser; @DocumentReference User spiritAnimal; + @DocumentReference(lazy = true) User lazySpiritAnimal; int visits; @@ -325,6 +326,14 @@ public void setSpiritAnimal(User spiritAnimal) { this.spiritAnimal = spiritAnimal; } + public User getLazySpiritAnimal() { + return lazySpiritAnimal; + } + + public void setLazySpiritAnimal(User lazySpiritAnimal) { + this.lazySpiritAnimal = lazySpiritAnimal; + } + @Override public int hashCode() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java index f94a52e916..f985d8e375 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.mongodb.repository; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.assertProxyIsResolved; import java.util.ArrayList; import java.util.Arrays; @@ -37,12 +38,14 @@ * * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl */ @ContextConfiguration(locations = "PersonRepositoryIntegrationTests-context.xml") @ExtendWith(SpringExtension.class) public class PersonRepositoryLazyLoadingIntegrationTests { @Autowired PersonRepository repository; + @Autowired UserRepository userRepository; @Autowired MongoOperations operations; @BeforeEach @@ -123,4 +126,47 @@ public void shouldLoadAssociationWithDbRefOnConcreteDomainClassAndLazyLoadingEna assertProxyIsResolved(coworker, true); assertThat(coworker.getUsername()).isEqualTo(thomas.getUsername()); } + + @Test // GH-5031 + void allowsSavingEntityLoadedViaLazyDBRef() { + + User thomas = new User(); + thomas.id = "tom"; + thomas.username = "Thomas"; + userRepository.save(thomas); + + Person oliver = new Person(); + oliver.id = "ollie"; + oliver.setFirstname("Oliver"); + oliver.coworker = thomas; + repository.save(oliver); + + Person loaded = repository.findById(oliver.id).get(); + User coworker = loaded.getCoworker(); + assertThat(coworker.getUsername()).isEqualTo(thomas.getUsername()); + + assertThatNoException().isThrownBy(() -> userRepository.save(coworker)); + } + + @Test // GH-5031 + void allowsSavingEntityLoadedViaLazyDocumentReference() { + + User thomas = new User(); + thomas.id = "tom"; + thomas.username = "Thomas"; + userRepository.save(thomas); + + Person oliver = new Person(); + oliver.id = "ollie"; + oliver.setFirstname("Oliver"); + oliver.lazySpiritAnimal = thomas; + repository.save(oliver); + + Person loaded = repository.findById(oliver.id).get(); + User spiritAnimal = loaded.getLazySpiritAnimal(); + assertThat(spiritAnimal.getUsername()).isEqualTo(thomas.getUsername()); + + assertThatNoException().isThrownBy(() -> userRepository.save(spiritAnimal)); + } + } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserRepository.java new file mode 100644 index 0000000000..1226331c79 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository; + +import org.springframework.data.repository.CrudRepository; + +/** + * @author Christoph Strobl + */ +public interface UserRepository extends CrudRepository {} diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml index 8466692f83..234f3cd7b7 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml @@ -5,8 +5,15 @@ xsi:schemaLocation="http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - - + + + + + + + + + + diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml index 1fc7b9bea2..8c6194f0a4 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml @@ -19,4 +19,9 @@ + + + + + diff --git a/src/main/antora/modules/ROOT/pages/mongodb/mapping/document-references.adoc b/src/main/antora/modules/ROOT/pages/mongodb/mapping/document-references.adoc index 1dec452dcf..b5e618d008 100644 --- a/src/main/antora/modules/ROOT/pages/mongodb/mapping/document-references.adoc +++ b/src/main/antora/modules/ROOT/pages/mongodb/mapping/document-references.adoc @@ -47,10 +47,12 @@ Required properties that are also defined as lazy loading ``DBRef`` and used as TIP: Lazily loaded ``DBRef``s can be hard to debug. Make sure tooling does not accidentally trigger proxy resolution by e.g. calling `toString()` or some inline debug rendering invoking property getters. -Please consider to enable _trace_ logging for `org.springframework.data.mongodb.core.convert.DefaultDbRefResolver` to gain insight on `DBRef` resolution. +Please consider to enable _trace_ logging for `org.springframework.data.mongodb.core.convert.DefaultDbRefResolver` to gain insight on `DBRef` resolution. + +Though technically possible, avoid saving back individual lazily loaded entities obtained via properties of the referencing root. CAUTION: Lazy loading may require class proxies, that in turn, might need access to jdk internals, that are not open, starting with Java 16+, due to https://openjdk.java.net/jeps/396[JEP 396: Strongly Encapsulate JDK Internals by Default]. -For those cases please consider falling back to an interface type (eg. switch from `ArrayList` to `List`) or provide the required `--add-opens` argument. +For those cases please consider falling back to an interface type (eg. switch from `ArrayList` to `List`) or provide the required `--add-opens` argument. + + [[mapping-usage.document-references]] == Using Document References @@ -500,6 +502,7 @@ A few more general remarks: * Do you use cyclic references? Ask your self if you need them. * Lazy document references are hard to debug. -Make sure tooling does not accidentally trigger proxy resolution by e.g. calling `toString()`. +Make sure tooling does not accidentally trigger proxy resolution by e.g. calling `toString()`. + +Though technically possible, avoid saving back individual lazily loaded entities obtained via properties of the referencing root. * There is no support for reading document references using reactive infrastructure. ====