Skip to content

Commit 9391558

Browse files
committed
Mark @AutoTimestamp properties dirty so they are persisted across all datastores. Fixes #15120
1 parent 9cb1a0f commit 9391558

File tree

2 files changed

+26
-26
lines changed

2 files changed

+26
-26
lines changed

grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/codecs/PersistentEntityCodec.groovy

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ import org.grails.datastore.mapping.engine.internal.MappingUtils
6464
import org.grails.datastore.mapping.model.EmbeddedPersistentEntity
6565
import org.grails.datastore.mapping.model.PersistentEntity
6666
import org.grails.datastore.mapping.model.PersistentProperty
67-
import org.grails.datastore.mapping.model.config.GormProperties
6867
import org.grails.datastore.mapping.model.types.Association
6968
import org.grails.datastore.mapping.model.types.Embedded
7069
import org.grails.datastore.mapping.model.types.EmbeddedCollection
@@ -249,12 +248,6 @@ class PersistentEntityCodec extends BsonPersistentEntityCodec {
249248
}
250249

251250
}
252-
else {
253-
// schedule lastUpdated if necessary
254-
if (entity.getPropertyByName(GormProperties.LAST_UPDATED) != null) {
255-
dirtyProperties.add(GormProperties.LAST_UPDATED)
256-
}
257-
}
258251

259252
for (propertyName in dirtyProperties) {
260253
def prop = entity.getPropertyByName(propertyName)

grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/events/AutoTimestampEventListener.java

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232

3333
import org.springframework.beans.factory.annotation.Value;
3434
import org.springframework.context.ApplicationEvent;
35+
import org.springframework.util.ReflectionUtils;
3536

3637
import grails.gorm.annotation.AutoTimestamp;
3738
import org.grails.datastore.gorm.timestamp.DefaultTimestampProvider;
3839
import org.grails.datastore.gorm.timestamp.TimestampProvider;
3940
import org.grails.datastore.mapping.config.Entity;
4041
import org.grails.datastore.mapping.config.Settings;
4142
import org.grails.datastore.mapping.core.Datastore;
43+
import org.grails.datastore.mapping.dirty.checking.DirtyCheckable;
4244
import org.grails.datastore.mapping.engine.EntityAccess;
4345
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent;
4446
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener;
@@ -148,10 +150,25 @@ private void initializeIfNecessary(PersistentEntity entity, String name) {
148150
public boolean beforeUpdate(PersistentEntity entity, EntityAccess ea) {
149151
Set<String> props = getLastUpdatedPropertyNames(entity.getName());
150152
if (props != null) {
153+
Object entityObject = ea.getEntity();
154+
boolean isDirtyCheckable = entityObject instanceof DirtyCheckable;
155+
156+
// For dirty-checking datastores (e.g., MongoDB), only set autotimestamp if entity has dirty properties
157+
if (isDirtyCheckable) {
158+
List<String> dirtyPropertyNames = ((DirtyCheckable) entityObject).listDirtyPropertyNames();
159+
if (dirtyPropertyNames.isEmpty()) {
160+
return true;
161+
}
162+
}
163+
151164
for (String prop : props) {
152165
Class<?> lastUpdateType = ea.getPropertyType(prop);
153166
Object timestamp = timestampProvider.createTimestamp(lastUpdateType);
154167
ea.setProperty(prop, timestamp);
168+
// Mark property as dirty for datastores that use dirty checking (e.g., MongoDB)
169+
if (isDirtyCheckable) {
170+
((DirtyCheckable) entityObject).markDirty(prop);
171+
}
155172
}
156173
}
157174
return true;
@@ -167,18 +184,6 @@ protected Set<String> getDateCreatedPropertyNames(String entityName) {
167184
return properties == null ? null : properties.orElse(null);
168185
}
169186

170-
private static Field getFieldFromHierarchy(Class<?> entity, String fieldName) {
171-
Class<?> clazz = entity;
172-
while (clazz != null) {
173-
try {
174-
return clazz.getDeclaredField(fieldName);
175-
} catch (NoSuchFieldException e) {
176-
clazz = clazz.getSuperclass();
177-
}
178-
}
179-
return null;
180-
}
181-
182187
protected void storeDateCreatedAndLastUpdatedInfo(PersistentEntity persistentEntity) {
183188
if (persistentEntity.isInitialized()) {
184189
ClassMapping<?> classMapping = persistentEntity.getMapping();
@@ -190,13 +195,15 @@ protected void storeDateCreatedAndLastUpdatedInfo(PersistentEntity persistentEnt
190195
} else if (property.getName().equals(DATE_CREATED_PROPERTY)) {
191196
storeTimestampAvailability(entitiesWithDateCreated, persistentEntity, property);
192197
} else {
193-
Field field = getFieldFromHierarchy(persistentEntity.getJavaClass(), property.getName());
194-
if (field != null && field.isAnnotationPresent(AutoTimestamp.class)) {
195-
AutoTimestamp autoTimestamp = field.getAnnotation(AutoTimestamp.class);
196-
if (autoTimestamp.value() == AutoTimestamp.EventType.UPDATED) {
197-
storeTimestampAvailability(entitiesWithLastUpdated, persistentEntity, property);
198-
} else {
199-
storeTimestampAvailability(entitiesWithDateCreated, persistentEntity, property);
198+
Field field = ReflectionUtils.findField(persistentEntity.getJavaClass(), property.getName());
199+
if (field != null) {
200+
if (field.isAnnotationPresent(AutoTimestamp.class)) {
201+
AutoTimestamp autoTimestamp = field.getAnnotation(AutoTimestamp.class);
202+
if (autoTimestamp.value() == AutoTimestamp.EventType.UPDATED) {
203+
storeTimestampAvailability(entitiesWithLastUpdated, persistentEntity, property);
204+
} else {
205+
storeTimestampAvailability(entitiesWithDateCreated, persistentEntity, property);
206+
}
200207
}
201208
}
202209
}

0 commit comments

Comments
 (0)