Skip to content

Commit d0edb0d

Browse files
committed
Extract Wrapped support to PersistentEntity.
1 parent 4b331e7 commit d0edb0d

File tree

8 files changed

+142
-29
lines changed

8 files changed

+142
-29
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
import org.bson.Document;
2121
import org.jspecify.annotations.Nullable;
22-
import org.springframework.data.mapping.PersistentPropertyAccessor;
22+
23+
import org.springframework.beans.DirectFieldAccessor;
2324
import org.springframework.data.mapping.context.MappingContext;
2425
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
2526
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
@@ -65,11 +66,15 @@ public Object populateId(MongoPersistentProperty property, @Nullable DBRef sourc
6566
}
6667

6768
ValueExpressionEvaluator evaluator = evaluatorFactory.apply(proxy);
68-
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy);
69+
70+
DirectFieldAccessor accessor = new DirectFieldAccessor(proxy);
71+
// TODO
72+
// PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy);
6973

7074
Document object = new Document(idProperty.getFieldName(), source.getId());
7175
ObjectPath objectPath = ObjectPath.ROOT.push(proxy, entity, null);
72-
accessor.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath));
76+
accessor.setPropertyValue(idProperty.getName(),
77+
resolver.getValueInternal(idProperty, object, evaluator, objectPath));
7378

7479
return proxy;
7580
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import org.jspecify.annotations.Nullable;
1919

20+
import org.springframework.data.mongodb.core.mapping.Wrapped;
21+
2022
import com.mongodb.DBRef;
2123

2224
/**
@@ -28,14 +30,15 @@
2830
* @since 1.5
2931
* @see LazyLoadingProxyFactory
3032
*/
31-
public interface LazyLoadingProxy {
33+
public interface LazyLoadingProxy extends Wrapped {
3234

3335
/**
3436
* Initializes the proxy and returns the wrapped value.
3537
*
3638
* @return
3739
* @since 1.5
3840
*/
41+
@Override
3942
Object getTarget();
4043

4144
/**

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
import java.util.Map;
2727

2828
import org.jspecify.annotations.Nullable;
29+
2930
import org.springframework.data.annotation.Id;
3031
import org.springframework.data.expression.ValueEvaluationContext;
3132
import org.springframework.data.expression.ValueExpression;
3233
import org.springframework.data.expression.ValueExpressionParser;
3334
import org.springframework.data.mapping.Association;
3435
import org.springframework.data.mapping.AssociationHandler;
3536
import org.springframework.data.mapping.MappingException;
37+
import org.springframework.data.mapping.PersistentPropertyAccessor;
3638
import org.springframework.data.mapping.PropertyHandler;
3739
import org.springframework.data.mapping.model.BasicPersistentEntity;
3840
import org.springframework.data.mongodb.MongoCollectionUtils;
@@ -188,6 +190,16 @@ public void verify() {
188190
verifyFieldTypes();
189191
}
190192

193+
@Override
194+
public boolean isNew(Object bean) {
195+
return super.isNew(Wrapped.getTargetObject(bean));
196+
}
197+
198+
@Override
199+
public <B> PersistentPropertyAccessor<B> getPropertyAccessor(B bean) {
200+
return super.getPropertyAccessor(Wrapped.getTargetObject(bean));
201+
}
202+
191203
@Override
192204
public EvaluationContext getEvaluationContext(@Nullable Object rootObject) {
193205
return super.getEvaluationContext(rootObject);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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.core.mapping;
17+
18+
/**
19+
* @author Mark Paluch
20+
*/
21+
public interface Wrapped {
22+
23+
/**
24+
* Returns the wrapped value.
25+
*
26+
* @return
27+
*/
28+
Object getTarget();
29+
30+
/**
31+
* Unwraps the given object if it is an instance of {@link Wrapped}. If not, returns the object as-is.
32+
*
33+
* @param object the object to unwrap, must not be {@literal null}.
34+
* @return the unwrapped object or the original object if it is not wrapped.
35+
*/
36+
static <T> T getTargetObject(T object) {
37+
return object instanceof Wrapped w ? (T) w.getTarget() : object;
38+
}
39+
40+
}

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
import org.bson.types.ObjectId;
1919
import org.jspecify.annotations.Nullable;
20+
2021
import org.springframework.data.mapping.PersistentPropertyAccessor;
21-
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
2222
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2323
import org.springframework.data.mongodb.core.query.Collation;
2424
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
@@ -108,13 +108,6 @@ public Class<ID> getIdType() {
108108
return fallbackIdType;
109109
}
110110

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-
118111
@Override
119112
public boolean isVersioned() {
120113
return this.entityMetadata.hasVersionProperty();

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,40 @@
1919

2020
import java.util.Date;
2121

22+
import org.bson.types.ObjectId;
2223
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.extension.ExtendWith;
2325

24-
import org.springframework.context.support.AbstractApplicationContext;
25-
import org.springframework.context.support.ClassPathXmlApplicationContext;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.context.ApplicationContext;
2628
import org.springframework.data.annotation.CreatedDate;
2729
import org.springframework.data.annotation.Id;
2830
import org.springframework.data.annotation.LastModifiedDate;
2931
import org.springframework.data.mapping.callback.EntityCallbacks;
32+
import org.springframework.data.mongodb.core.MongoTemplate;
33+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
3034
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
3135
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
36+
import org.springframework.test.context.ContextConfiguration;
37+
import org.springframework.test.context.junit.jupiter.SpringExtension;
3238

3339
/**
3440
* Integration test for the auditing support.
3541
*
3642
* @author Oliver Gierke
3743
* @author Mark Paluch
3844
*/
39-
public class AuditingIntegrationTests {
45+
@ContextConfiguration(locations = "auditing.xml")
46+
@ExtendWith(SpringExtension.class)
47+
class AuditingIntegrationTests {
4048

41-
@Test // DATAMONGO-577, DATAMONGO-800, DATAMONGO-883, DATAMONGO-2261
42-
public void enablesAuditingAndSetsPropertiesAccordingly() throws Exception {
49+
@Autowired MongoMappingContext mappingContext;
50+
@Autowired ApplicationContext context;
51+
@Autowired MongoTemplate template;
4352

44-
AbstractApplicationContext context = new ClassPathXmlApplicationContext("auditing.xml", getClass());
53+
@Test // DATAMONGO-577, DATAMONGO-800, DATAMONGO-883, DATAMONGO-2261
54+
void enablesAuditingAndSetsPropertiesAccordingly() throws Exception {
4555

46-
MongoMappingContext mappingContext = context.getBean(MongoMappingContext.class);
4756
mappingContext.getPersistentEntity(Entity.class);
4857

4958
EntityCallbacks callbacks = EntityCallbacks.create(context);
@@ -55,24 +64,68 @@ public void enablesAuditingAndSetsPropertiesAccordingly() throws Exception {
5564
assertThat(entity.modified).isEqualTo(entity.created);
5665

5766
Thread.sleep(10);
58-
entity.id = 1L;
67+
entity.id = "foo";
5968

6069
entity = callbacks.callback(BeforeConvertCallback.class, entity, "collection-1");
6170

6271
assertThat(entity.created).isNotNull();
6372
assertThat(entity.modified).isNotEqualTo(entity.created);
64-
context.close();
6573
}
6674

67-
class Entity {
75+
@Test // GH-5031
76+
void handlesAuditingCorrectly() throws InterruptedException {
77+
78+
template.remove(TopLevelEntity.class).all();
79+
80+
Entity entity = new Entity();
81+
template.insert(entity);
82+
83+
TopLevelEntity tle = new TopLevelEntity();
84+
tle.entity = entity;
85+
template.insert(tle);
86+
87+
Thread.sleep(200);
88+
89+
TopLevelEntity loadAndModify = template.findById(tle.id, TopLevelEntity.class);
90+
Date created = loadAndModify.entity.getCreated();
91+
Date modified = loadAndModify.entity.getModified();
92+
template.save(loadAndModify.entity);
93+
94+
TopLevelEntity loaded = template.findById(tle.id, TopLevelEntity.class);
95+
96+
assertThat(loaded.entity.getCreated()).isEqualTo(created);
97+
assertThat(loaded.entity.getModified()).isNotEqualTo(modified);
98+
}
99+
100+
static class Entity {
68101

69-
@Id Long id;
102+
@Id String id;
70103
@CreatedDate Date created;
71104
Date modified;
72105

73106
@LastModifiedDate
74107
public Date getModified() {
75108
return modified;
76109
}
110+
111+
public Date getCreated() {
112+
return created;
113+
}
114+
115+
public void setCreated(Date created) {
116+
this.created = created;
117+
}
118+
119+
public void setModified(Date modified) {
120+
this.modified = modified;
121+
}
77122
}
123+
124+
static class TopLevelEntity {
125+
126+
@Id ObjectId id;
127+
@DocumentReference(lazy = true) Entity entity;
128+
129+
}
130+
78131
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@
4444
import org.mockito.Mockito;
4545
import org.mockito.junit.jupiter.MockitoExtension;
4646

47+
import org.springframework.beans.DirectFieldAccessor;
4748
import org.springframework.data.annotation.AccessType;
4849
import org.springframework.data.annotation.AccessType.Type;
4950
import org.springframework.data.annotation.Id;
5051
import org.springframework.data.annotation.PersistenceCreator;
51-
import org.springframework.data.mapping.PersistentPropertyAccessor;
5252
import org.springframework.data.mapping.PropertyPath;
5353
import org.springframework.data.mongodb.MongoDatabaseFactory;
5454
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
@@ -487,11 +487,11 @@ void shouldEagerlyResolveIdPropertyWithFieldAccess() {
487487

488488
ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, object);
489489

490-
PersistentPropertyAccessor accessor = propertyEntity.getPropertyAccessor(result.dbRefToConcreteType);
490+
DirectFieldAccessor accessor = new DirectFieldAccessor(result.dbRefToConcreteType);
491491
MongoPersistentProperty idProperty = mappingContext.getRequiredPersistentEntity(LazyDbRefTarget.class)
492492
.getIdProperty();
493493

494-
assertThat(accessor.getProperty(idProperty)).isNotNull();
494+
assertThat(accessor.getPropertyValue(idProperty.getName())).isNotNull();
495495
assertProxyIsResolved(result.dbRefToConcreteType, false);
496496
}
497497

spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@
55
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
66
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
77

8-
<mongo:auditing mapping-context-ref="customMappingContext" />
9-
10-
<bean id="customMappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
8+
<mongo:auditing mapping-context-ref="mongoMappingContext"/>
9+
10+
<mongo:db-factory dbname="repositories"/>
11+
12+
<mongo:mapping-converter auto-index-creation="true"/>
13+
14+
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
15+
<constructor-arg ref="mongoDbFactory"/>
16+
<constructor-arg ref="mappingConverter"/>
17+
</bean>
1118

1219
</beans>

0 commit comments

Comments
 (0)