2

I have a very basic question in saving objects which I get from a client sent via JSON.

I have a customer object which is transfered to the client, after editing the customer its send back to Grails and needs to be saved in the database. For performance I am not sending the complete customer object over the wire.

The problem is now if I want to store the customer object Grails validates of course the relationships of the customer object and fails. This is OK because I havent sent the relationsships.

My question is now how do I solve this problem now? Do I need to query the database again with the customer id and update the edited properties or is there a more elegant way? This looks a little bit expensive from database perspective as I need to read the database each time when storeing an object. As well from code perspective I need to check which properties are set and update them.

Thank you!

Gambo
  • 1,572
  • 3
  • 26
  • 52

1 Answers1

3

You cannot use save() for doing partial update, since grails cannot guess what fields you actually want to update: Maybe you REALLY want to set a field-value to NULL, so Grails cannot just ignore those fields. So I see two options:

  1. Do it like you have described: Load the instance from DB, set the values and save again. You have mentioned, that you do not like to care what fields are updated, and you just want to take all attributes of your JSON instance. So assuming your already parsed JSON-instance is called jsonInstance and your database version of the customer is customerInstance, you can do:

     jsonInstance.properties.each { field -> 
         customerInstance."${field.key}" = field.value
     }
    

    However, note that there are security limitations (if an attacker injects an 'id' attribute or other relevant attribute values into it, those will be just overwritten).

  2. Use executeUpdate-function, see: http://www.grails.org/doc/latest/ref/Domain%20Classes/executeUpdate.html

    I think, if you really want to save performance, then go like this. However you have some hardcoded DML, which will cost maintainability and flexibility.

Chris
  • 8,031
  • 10
  • 41
  • 67
  • You can do shorter with `jsonInstance`, just with `customerInstance.properties = jsonInstance as Map` or, more securely, `customerInstance.properties = (jsonInstance as Map).subMap(['allowedField', 'anotherAllowedField'])`. – Victor Sergienko Aug 15 '11 at 16:09
  • if you do it like this, you will lose all other properties set in `customerInstance`, which are set on the customerInstance. this is exactly what he does not want, so you have to merge those properties into the existing ones. isn't it? – Chris Aug 15 '11 at 19:05
  • Sorry, what "other" properties? This will set only properties jsonInstance contains - or subMap() parameter mentions. What TO does want, IIGIR, is not to read the object again and only update the changed properties. This, you're right, can't be done with GORM other then HQL, and will totally bypass validation. If you mean nested properties, both ways will support them. – Victor Sergienko Aug 15 '11 at 20:14
  • BTW, neither TO nor you define a way to determine what properties were changed: we either need to compare each field to its default value or check some field's flag. – Victor Sergienko Aug 15 '11 at 20:15
  • Me again, I am doing now the update process complete by hand. For security I need to take care about every id and property I am getting from the client. This is not the best and of course not DRY but its giving me the control over all properties I want let to update. I think this is deal if I want to have a flexible REST interface and I cant never ever trust a user to give me the right params. So code wise this means: 1. I read the id from db 2. check if the instance belongs to the user 3. copy all updated properties in db instance 4. save db instance – Gambo Aug 16 '11 at 15:20
  • Have you considered to use `executeUpdate`? – Chris Aug 16 '11 at 19:24
  • No I didnt as it makes no difference and I need to get the model from the db anyway before changing it. now I assign the new values with modelToUpdate.property1 = modelFromClient.property2 or leave this place for security holes as well? – Gambo Aug 16 '11 at 21:32
  • ok, then you have a standard use-case. regarding security: you should secure your channel somehow, using authentication or something, but this is obvious. good luck ;) – Chris Aug 16 '11 at 21:58