Skip to content

Commit bcb40b1

Browse files
christophstroblmp911de
authored andcommitted
Unwrap LazyLoadingProxy before checking isNew.
Closes #5031 Original pull request: #5033
1 parent 024d49d commit bcb40b1

File tree

6 files changed

+99
-5
lines changed

6 files changed

+99
-5
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.bson.types.ObjectId;
1919
import org.jspecify.annotations.Nullable;
2020
import org.springframework.data.mapping.PersistentPropertyAccessor;
21+
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
2122
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2223
import org.springframework.data.mongodb.core.query.Collation;
2324
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
@@ -107,6 +108,13 @@ public Class<ID> getIdType() {
107108
return fallbackIdType;
108109
}
109110

111+
@Override
112+
public boolean isNew(T entity) {
113+
114+
T unwrapped = entity instanceof LazyLoadingProxy proxy ? (T) proxy.getTarget() : entity;
115+
return super.isNew(unwrapped);
116+
}
117+
110118
@Override
111119
public boolean isVersioned() {
112120
return this.entityMetadata.hasVersionProperty();

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public enum Sex {
7777
User unwrappedUser;
7878

7979
@DocumentReference User spiritAnimal;
80+
@DocumentReference(lazy = true) User lazySpiritAnimal;
8081

8182
int visits;
8283

@@ -325,6 +326,14 @@ public void setSpiritAnimal(User spiritAnimal) {
325326
this.spiritAnimal = spiritAnimal;
326327
}
327328

329+
public User getLazySpiritAnimal() {
330+
return lazySpiritAnimal;
331+
}
332+
333+
public void setLazySpiritAnimal(User lazySpiritAnimal) {
334+
this.lazySpiritAnimal = lazySpiritAnimal;
335+
}
336+
328337
@Override
329338
public int hashCode() {
330339

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
*/
1616
package org.springframework.data.mongodb.repository;
1717

18-
import static org.assertj.core.api.Assertions.*;
19-
import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.*;
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatNoException;
20+
import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.assertProxyIsResolved;
2021

2122
import java.util.ArrayList;
2223
import java.util.Arrays;
@@ -37,12 +38,14 @@
3738
*
3839
* @author Thomas Darimont
3940
* @author Oliver Gierke
41+
* @author Christoph Strobl
4042
*/
4143
@ContextConfiguration(locations = "PersonRepositoryIntegrationTests-context.xml")
4244
@ExtendWith(SpringExtension.class)
4345
public class PersonRepositoryLazyLoadingIntegrationTests {
4446

4547
@Autowired PersonRepository repository;
48+
@Autowired UserRepository userRepository;
4649
@Autowired MongoOperations operations;
4750

4851
@BeforeEach
@@ -123,4 +126,47 @@ public void shouldLoadAssociationWithDbRefOnConcreteDomainClassAndLazyLoadingEna
123126
assertProxyIsResolved(coworker, true);
124127
assertThat(coworker.getUsername()).isEqualTo(thomas.getUsername());
125128
}
129+
130+
@Test // GH-5031
131+
void allowsSavingEntityLoadedViaLazyDBRef() {
132+
133+
User thomas = new User();
134+
thomas.id = "tom";
135+
thomas.username = "Thomas";
136+
userRepository.save(thomas);
137+
138+
Person oliver = new Person();
139+
oliver.id = "ollie";
140+
oliver.setFirstname("Oliver");
141+
oliver.coworker = thomas;
142+
repository.save(oliver);
143+
144+
Person loaded = repository.findById(oliver.id).get();
145+
User coworker = loaded.getCoworker();
146+
assertThat(coworker.getUsername()).isEqualTo(thomas.getUsername());
147+
148+
assertThatNoException().isThrownBy(() -> userRepository.save(coworker));
149+
}
150+
151+
@Test // GH-5031
152+
void allowsSavingEntityLoadedViaLazyDocumentReference() {
153+
154+
User thomas = new User();
155+
thomas.id = "tom";
156+
thomas.username = "Thomas";
157+
userRepository.save(thomas);
158+
159+
Person oliver = new Person();
160+
oliver.id = "ollie";
161+
oliver.setFirstname("Oliver");
162+
oliver.lazySpiritAnimal = thomas;
163+
repository.save(oliver);
164+
165+
Person loaded = repository.findById(oliver.id).get();
166+
User spiritAnimal = loaded.getLazySpiritAnimal();
167+
assertThat(spiritAnimal.getUsername()).isEqualTo(thomas.getUsername());
168+
169+
assertThatNoException().isThrownBy(() -> userRepository.save(spiritAnimal));
170+
}
171+
126172
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2025 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.repository;
17+
18+
import org.springframework.data.repository.CrudRepository;
19+
20+
/**
21+
* @author Christoph Strobl
22+
*/
23+
public interface UserRepository extends CrudRepository<User, String> {}

spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@
1919
</property>
2020
</bean>
2121

22+
<bean class="org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean">
23+
<constructor-arg value="org.springframework.data.mongodb.repository.UserRepository"/>
24+
<property name="mongoOperations" ref="mongoTemplate"/>
25+
</bean>
26+
2227
</beans>

src/main/antora/modules/ROOT/pages/mongodb/mapping/document-references.adoc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ Required properties that are also defined as lazy loading ``DBRef`` and used as
4747

4848
TIP: Lazily loaded ``DBRef``s can be hard to debug.
4949
Make sure tooling does not accidentally trigger proxy resolution by e.g. calling `toString()` or some inline debug rendering invoking property getters.
50-
Please consider to enable _trace_ logging for `org.springframework.data.mongodb.core.convert.DefaultDbRefResolver` to gain insight on `DBRef` resolution.
50+
Please consider to enable _trace_ logging for `org.springframework.data.mongodb.core.convert.DefaultDbRefResolver` to gain insight on `DBRef` resolution. +
51+
Though technically possible, avoid saving back individual lazily loaded entities obtained via properties of the referencing root.
5152

5253
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].
53-
For those cases please consider falling back to an interface type (eg. switch from `ArrayList` to `List`) or provide the required `--add-opens` argument.
54+
For those cases please consider falling back to an interface type (eg. switch from `ArrayList` to `List`) or provide the required `--add-opens` argument. +
55+
5456

5557
[[mapping-usage.document-references]]
5658
== Using Document References
@@ -500,6 +502,7 @@ A few more general remarks:
500502
* Do you use cyclic references?
501503
Ask your self if you need them.
502504
* Lazy document references are hard to debug.
503-
Make sure tooling does not accidentally trigger proxy resolution by e.g. calling `toString()`.
505+
Make sure tooling does not accidentally trigger proxy resolution by e.g. calling `toString()`. +
506+
Though technically possible, avoid saving back individual lazily loaded entities obtained via properties of the referencing root.
504507
* There is no support for reading document references using reactive infrastructure.
505508
====

0 commit comments

Comments
 (0)