views:

69

answers:

2

When I change a domain object, rather than updating the database record, I'd like to archive the old record record and create a new one. I'd like to force GORM to do this automatically.

For example, if I had this domain class:

class User {
    String username
    String email
    Boolean active
}

then I'd like

user.email = email
user.save()

to set the active flag on the user to false, keeping a record of the old email. A new record with the new email address and active = true would be inserted.

Is this a common pattern? Is it easy to do in the domain class so that it happens transparently?

+2  A: 

One option I can think of would be to implement a soft delete and then persist a new user. I realize this is not exactly what you're asking for but I don't know how to do this with a single save. Not ideal IMO.

Another (probably better) approach would be to use Hibernate Envers to "version" the User entity and keep an history of changes. I don't know the exact status of Envers with Grails but it looks like there is a plugin for it. See also [grails-user] GORM & Envers / Audit History Tables.

Pascal Thivent
This is also a good global solution. Have you ever tried Envers with Grails ?
fabien7474
@fabien7474: No, never. I honestly don't know what the current status of Envers with Grails is (I don't really do Grails actually).
Pascal Thivent
+2  A: 

You can use GORM event beforeUpdate into User domain class (see the documentation here and here) with something like the following should work (not tested) :

def beforeUpdate() {
  if (isDirty('email') { //enters only if email was changed. Works only with Grails 1.3.x
  User.withNewSession {
    //Create the new entry
    new User(username:username, email:email).save()
    //Update the old entry with the old email value
    email = getPersistentValue('email')
  }
  }
}
fabien7474
Interesting, I didn't think about that. But how do you get the new inserted user (to replace your old instance)?
Pascal Thivent
If i create the new User in a new Session, it will be in another transaction, won't it? Ideally, I'd like it to rollback with the rest of the transaction if something fails.
ataylor
@ataylor yes it is another transaction. Just check the return valuse of save() and propagates the error to the other transaction for rolling back@Pascal The only way I have in mind now is : User.findByEmail(new_email), or storing the new user ID in a scope like session or flash
fabien7474