apache / apache/grails-core

Grails @Listener doesn't allow validation to work correctly

Open
#14,378 8 comments 0 reactions 0 assignees View on GitHub
relates-to: grails-data-hibernate5
Dominant language
Groovy
Stars
2.9k
Forks
975
Avg merge
1d 22h
Merged PRs (30d)
92

Description

Grails has a special field "lastUpdated", which auto-populates the update date. You can make this field nullable: false, which makes sense... why should an auto-updated field be nullable.

But what if I want to make my own auto-populated field? Like say, the user who updated the record. It would seem that Grails @Listener service was designed primarily to implement auto-updated fields. At least that's what the documentation implies, I'm sure other uses might exist, but this its main reason. So we could implement auto user update field like this:

```
class RecordUserAuditService {
def springSecurityService
@Listener([Account])
void onPreInsert(PreInsertEvent event) {
User user = springSecurityService.currentUser
event.entityAccess.setProperty('userCreated', user)
event.entityAccess.setProperty('userUpdated', user)
}
@Listener([Account])
void onPreUpdate(PreUpdateEvent event) {
User user = springSecurityService.currentUser
event.entityAccess.setProperty('userUpdated', user)
}
}

```

ok, so this will autopopulate the field...
but wait... this only gets called when the session is flushed, it doesn't get called prior to validate, and so it is impossible to auto-populate a nullable: false field. It is impossible to do what grails does for you with lastUpdated. When you call save() on your object, it will call validate() and fail... even though it would have later been correctly populated.

I think if this feature was designed properly, Grails could cease having "lastUpdated" as special, and the user could do it themselves. Personally, I hate "lastUpdated" as a name, I'd rather call it updateDate or something, but I don't have a choice, because the @Listener service doesn't work well enough to roll your own.

Another problem of @Listener is it forces you to list every single domain class. Why shouldn't I be able to have a wildcard match, and be able to check myself if my domain class has the field in question? If 95% of my domains have "userUpdated", it would be no overhead to always call it for all classes.

I also note that the example in the documentation populates Book.friendlyUrl.... it makes friendlyUrl nullable: true... but makes no mention of how critical it is to its functioning that it be set to nullable: true.

One might be tempted to make it NOT NULL in the database, and nullable: true in grails, just to force it to work... I guess that's a workaround, BUT... if you're using grails to auto-generate the schema, then that will fail during the development cycle.

### Expected Behaviour

We need a Listener that gets called before validate. We need a listener that is capable of implementing something like lastUpdated.

We need a listener that optionally gets called for all domain classes and can check if the class has a paricular property.

### Actual Behaviour

Tell us what happens instead

### Environment Information

- **Grails Version:** 4.0.12
- **JDK Version:** 1.8

Contributor guide

Open the contributing guide

Research direction

Start with the @Listener entry points onPreInsert and onPreUpdate, and trace how they relate to the validation and save lifecycle described in the issue. Compare this behavior with Grails' special lastUpdated handling. Done means non-null auto-populated fields can validate successfully and the requested optional all-domain matching behavior is covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
groovy
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.