Skip to content

Commit 211423c

Browse files
committed
Allow customization of autotimestamp dateCreated and lastUpdated properties
1 parent 5646c30 commit 211423c

File tree

3 files changed

+99
-78
lines changed

3 files changed

+99
-78
lines changed

grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/config/Entity.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class Entity<P extends Property> {
5656
* @return Whether automatic time stamps should be applied to 'lastUpdate' and 'dateCreated' properties
5757
*/
5858
boolean autoTimestamp = true
59+
String dateCreated = null
60+
String lastUpdated = null
61+
5962
/**
6063
* @return Whether the entity should be autowired
6164
*/
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package grails.gorm.tests
2+
3+
import grails.persistence.Entity
4+
5+
class CustomAutotimeStampSpec extends GormDatastoreSpec{
6+
7+
void "Test when the auto timestamp properties are customized, they are correctly set"() {
8+
when:"An entity is persisted"
9+
def r = new RecordCustom(name: "Test")
10+
r.save(flush:true)
11+
session.clear()
12+
r = RecordCustom.get(r.id)
13+
14+
then:"the custom lastUpdated and dateCreated are set"
15+
r.modified != null && r.modified < new Date()
16+
r.created != null && r.created < new Date()
17+
18+
when:"An entity is modified"
19+
Date previousCreated = r.created
20+
Date previousModified = r.modified
21+
r.name = "Test 2"
22+
r.save(flush:true)
23+
session.clear()
24+
r = RecordCustom.get(r.id)
25+
26+
then:"the custom lastUpdated property is updated and dateCreated is not"
27+
r.modified != null && previousModified > r.modified
28+
previousCreated == r.created
29+
}
30+
@Override
31+
List getDomainClasses() {
32+
[RecordCustom]
33+
}
34+
}
35+
36+
@Entity
37+
class RecordCustom {
38+
Long id
39+
String name
40+
Date created
41+
Date modified
42+
43+
static mapping = {
44+
dateCreated 'created'
45+
lastUpdated 'modified'
46+
}
47+
}

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

Lines changed: 49 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public class AutoTimestampEventListener extends AbstractPersistenceEventListener
4545
public static final String DATE_CREATED_PROPERTY = "dateCreated";
4646
public static final String LAST_UPDATED_PROPERTY = "lastUpdated";
4747

48-
protected Map<String, Boolean> entitiesWithDateCreated = new ConcurrentHashMap<String, Boolean>();
49-
protected Map<String, Boolean> entitiesWithLastUpdated = new ConcurrentHashMap<String, Boolean>();
50-
protected Collection<String> uninitializedEntities = new ConcurrentLinkedQueue<String>();
48+
protected Map<String, String> entitiesWithDateCreated = new ConcurrentHashMap<>();
49+
protected Map<String, String> entitiesWithLastUpdated = new ConcurrentHashMap<>();
50+
protected Collection<String> uninitializedEntities = new ConcurrentLinkedQueue<>();
5151

5252

5353
private TimestampProvider timestampProvider = new DefaultTimestampProvider();
@@ -80,8 +80,7 @@ protected void onPersistenceEvent(final AbstractPersistenceEvent event) {
8080

8181
if (event.getEventType() == EventType.PreInsert) {
8282
beforeInsert(event.getEntity(), event.getEntityAccess());
83-
}
84-
else if (event.getEventType() == EventType.PreUpdate) {
83+
} else if (event.getEventType() == EventType.PreUpdate) {
8584
beforeUpdate(event.getEntity(), event.getEntityAccess());
8685
}
8786
}
@@ -96,82 +95,69 @@ public boolean beforeInsert(PersistentEntity entity, EntityAccess ea) {
9695
initializeIfNecessary(entity, name);
9796
Class<?> dateCreatedType = null;
9897
Object timestamp = null;
99-
if (hasDateCreated(name)) {
100-
dateCreatedType = ea.getPropertyType(DATE_CREATED_PROPERTY);
98+
String prop = getDateCreatedPropertyName(name);
99+
if (prop != null) {
100+
dateCreatedType = ea.getPropertyType(prop);
101101
timestamp = timestampProvider.createTimestamp(dateCreatedType);
102-
ea.setProperty(DATE_CREATED_PROPERTY, timestamp);
102+
ea.setProperty(prop, timestamp);
103103
}
104-
if (hasLastUpdated(name)) {
105-
Class<?> lastUpdateType = ea.getPropertyType(LAST_UPDATED_PROPERTY);
106-
if(dateCreatedType == null || !lastUpdateType.isAssignableFrom(dateCreatedType)) {
104+
prop = getLastUpdatedPropertyName(name);
105+
if (prop != null) {
106+
Class<?> lastUpdateType = ea.getPropertyType(prop);
107+
if (dateCreatedType == null || !lastUpdateType.isAssignableFrom(dateCreatedType)) {
107108
timestamp = timestampProvider.createTimestamp(lastUpdateType);
108109
}
109-
ea.setProperty(LAST_UPDATED_PROPERTY, timestamp);
110+
ea.setProperty(prop, timestamp);
110111
}
111112
return true;
112113
}
113114

114115
private void initializeIfNecessary(PersistentEntity entity, String name) {
115-
if(uninitializedEntities.contains(name)) {
116+
if (uninitializedEntities.contains(name)) {
116117
storeDateCreatedAndLastUpdatedInfo(entity);
117118
uninitializedEntities.remove(name);
118119
}
119120
}
120121

121122
public boolean beforeUpdate(PersistentEntity entity, EntityAccess ea) {
122-
if (hasLastUpdated(entity.getName())) {
123-
Class<?> lastUpdateType = ea.getPropertyType(LAST_UPDATED_PROPERTY);
123+
String prop = getLastUpdatedPropertyName(entity.getName());
124+
if (prop != null) {
125+
Class<?> lastUpdateType = ea.getPropertyType(prop);
124126
Object timestamp = timestampProvider.createTimestamp(lastUpdateType);
125-
ea.setProperty(LAST_UPDATED_PROPERTY, timestamp);
127+
ea.setProperty(prop, timestamp);
126128
}
127129
return true;
128130
}
129131

130-
/**
131-
* Here for binary compatibility. Deprecated.
132-
*
133-
* @deprecated Use {@link #hasLastUpdated(String)} instead
134-
*/
135-
@Deprecated
136-
protected boolean hasLastUpdated(PersistentEntity entity) {
137-
return hasLastUpdated(entity.getName());
132+
protected String getLastUpdatedPropertyName(String n) {
133+
return entitiesWithLastUpdated.get(n);
138134
}
139135

140-
protected boolean hasLastUpdated(String n) {
141-
return entitiesWithLastUpdated.containsKey(n) && entitiesWithLastUpdated.get(n);
142-
}
143-
144-
/**
145-
* Here for binary compatibility. Deprecated.
146-
*
147-
* @deprecated Use {@link #hasDateCreated(String)} instead
148-
*/
149-
@Deprecated
150-
protected boolean hasDateCreated(PersistentEntity entity) {
151-
return hasDateCreated(entity.getName());
152-
}
153-
154-
protected boolean hasDateCreated(String n) {
155-
return entitiesWithDateCreated.containsKey(n)&& entitiesWithDateCreated.get(n);
136+
protected String getDateCreatedPropertyName(String n) {
137+
return entitiesWithDateCreated.get(n);
156138
}
157139

158140
protected void storeDateCreatedAndLastUpdatedInfo(PersistentEntity persistentEntity) {
159-
if(persistentEntity.isInitialized()) {
160-
141+
if (persistentEntity.isInitialized()) {
161142
ClassMapping<?> classMapping = persistentEntity.getMapping();
162143
Entity mappedForm = classMapping.getMappedForm();
163-
if(mappedForm == null || mappedForm.isAutoTimestamp()) {
164-
storeTimestampAvailability(entitiesWithDateCreated, persistentEntity, persistentEntity.getPropertyByName(DATE_CREATED_PROPERTY));
165-
storeTimestampAvailability(entitiesWithLastUpdated, persistentEntity, persistentEntity.getPropertyByName(LAST_UPDATED_PROPERTY));
144+
if (mappedForm == null || mappedForm.isAutoTimestamp()) {
145+
storeTimestampAvailability(entitiesWithDateCreated, persistentEntity,
146+
persistentEntity.getPropertyByName(mappedForm.getDateCreated() != null?
147+
mappedForm.getDateCreated() : DATE_CREATED_PROPERTY));
148+
storeTimestampAvailability(entitiesWithLastUpdated, persistentEntity,
149+
persistentEntity.getPropertyByName(mappedForm.getLastUpdated() != null?
150+
mappedForm.getLastUpdated() : LAST_UPDATED_PROPERTY));
166151
}
167-
}
168-
else {
152+
} else {
169153
uninitializedEntities.add(persistentEntity.getName());
170154
}
171155
}
172156

173-
protected void storeTimestampAvailability(Map<String, Boolean> timestampAvailabilityMap, PersistentEntity persistentEntity, PersistentProperty<?> property) {
174-
timestampAvailabilityMap.put(persistentEntity.getName(), property != null && timestampProvider.supportsCreating(property.getType()));
157+
protected void storeTimestampAvailability(Map<String, String> timestampAvailabilityMap, PersistentEntity persistentEntity, PersistentProperty<?> property) {
158+
if (property != null && timestampProvider.supportsCreating(property.getType())) {
159+
timestampAvailabilityMap.put(persistentEntity.getName(), property.getName());
160+
}
175161
}
176162

177163
public void persistentEntityAdded(PersistentEntity entity) {
@@ -186,25 +172,25 @@ public void setTimestampProvider(TimestampProvider timestampProvider) {
186172
this.timestampProvider = timestampProvider;
187173
}
188174

189-
private void processAllEntries(final Set<Map.Entry<String, Boolean>> entries, final Runnable runnable) {
190-
Map<String, Boolean> originalValues = new LinkedHashMap<String, Boolean>();
191-
for(Map.Entry<String, Boolean> entry: entries) {
175+
private void processAllEntries(final Set<Map.Entry<String, String>> entries, final Runnable runnable) {
176+
Map<String, String> originalValues = new LinkedHashMap<>();
177+
for (Map.Entry<String, String> entry: entries) {
192178
originalValues.put(entry.getKey(), entry.getValue());
193-
entry.setValue(false);
179+
entry.setValue(null);
194180
}
195181
runnable.run();
196-
for(Map.Entry<String, Boolean> entry: entries) {
182+
for (Map.Entry<String, String> entry: entries) {
197183
entry.setValue(originalValues.get(entry.getKey()));
198184
}
199185
}
200186

201-
private void processEntries(final List<Class> classes, Map<String, Boolean> entities, final Runnable runnable) {
202-
Set<Map.Entry<String, Boolean>> entries = new HashSet<>();
187+
private void processEntries(final List<Class> classes, Map<String, String> entities, final Runnable runnable) {
188+
Set<Map.Entry<String, String>> entries = new HashSet<>();
203189
final List<String> classNames = new ArrayList<>(classes.size());
204-
for(Class clazz: classes) {
190+
for (Class clazz: classes) {
205191
classNames.add(clazz.getName());
206192
}
207-
for (Map.Entry<String, Boolean> entry: entities.entrySet()) {
193+
for (Map.Entry<String, String> entry: entities.entrySet()) {
208194
if (classNames.contains(entry.getKey())) {
209195
entries.add(entry);
210196
}
@@ -238,7 +224,7 @@ public void withoutLastUpdated(final List<Class> classes, final Runnable runnabl
238224
* @param runnable The code to execute while the last updated listener is disabled
239225
*/
240226
public void withoutLastUpdated(final Class clazz, final Runnable runnable) {
241-
ArrayList<Class> list = new ArrayList<Class>(1);
227+
ArrayList<Class> list = new ArrayList<>(1);
242228
list.add(clazz);
243229
withoutLastUpdated(list, runnable);
244230
}
@@ -269,7 +255,7 @@ public void withoutDateCreated(final List<Class> classes, final Runnable runnabl
269255
* @param runnable The code to execute while the date created listener is disabled
270256
*/
271257
public void withoutDateCreated(final Class clazz, final Runnable runnable) {
272-
ArrayList<Class> list = new ArrayList<Class>(1);
258+
ArrayList<Class> list = new ArrayList<>(1);
273259
list.add(clazz);
274260
withoutDateCreated(list, runnable);
275261
}
@@ -280,12 +266,7 @@ public void withoutDateCreated(final Class clazz, final Runnable runnable) {
280266
* @param runnable The code to execute while the timestamp listeners are disabled
281267
*/
282268
public void withoutTimestamps(final Runnable runnable) {
283-
withoutDateCreated(new Runnable() {
284-
@Override
285-
public void run() {
286-
withoutLastUpdated(runnable);
287-
}
288-
});
269+
withoutDateCreated(() -> withoutLastUpdated(runnable));
289270
}
290271

291272
/**
@@ -295,12 +276,7 @@ public void run() {
295276
* @param runnable The code to execute while the timestamp listeners are disabled
296277
*/
297278
public void withoutTimestamps(final List<Class> classes, final Runnable runnable) {
298-
withoutDateCreated(classes, new Runnable() {
299-
@Override
300-
public void run() {
301-
withoutLastUpdated(classes, runnable);
302-
}
303-
});
279+
withoutDateCreated(classes, () -> withoutLastUpdated(classes, runnable));
304280
}
305281

306282
/**
@@ -310,12 +286,7 @@ public void run() {
310286
* @param runnable The code to execute while the timestamp listeners are disabled
311287
*/
312288
public void withoutTimestamps(final Class clazz, final Runnable runnable) {
313-
withoutDateCreated(clazz, new Runnable() {
314-
@Override
315-
public void run() {
316-
withoutLastUpdated(clazz, runnable);
317-
}
318-
});
289+
withoutDateCreated(clazz, () -> withoutLastUpdated(clazz, runnable));
319290
}
320291

321292
}

0 commit comments

Comments
 (0)