Search code examples
hibernategrailsormgrails-orm

How to check isDirty('transient_fieldName') for transient fields in Grails


Having a case in one of my domain class that we store a transient field to Mongo database in beforeInsert and afterUpdate hooks which works perfectly with following conditions:-

  • Inserts works well without any issue.
  • Updates works well if there is a modified non transient field

The problem is isDirty works for non-transient properties.

Code is as below:

class ResoruceInstance {
    def configurationService
    Status status

   //Transient
    Map<String, Object> configuration
    static transients = ['configuration']


    public Map<String, Object> getConfiguration() {
        if(!configuration)
            configuration =  configurationService.get(id, CollectionConstants.RESOURCE_INSTANCE_IDENTIFIER)

        return configuration
    }

    def afterInsert() {
        configurationService.save(id, CollectionConstants.RESOURCE_INSTANCE_IDENTIFIER, configuration)
     }
    def afterUpdate() {
        if(this.isDirty("configuration"))
            configurationService.save(id, CollectionConstants.RESOURCE_INSTANCE_IDENTIFIER, configuration)
     }

}

To handle this problem I created isDirtyMongo('transient_field'). This works well till the time a non-transient property is modified as afterUpdate is called only for transient properties.

Modified hook is as below:

def afterUpdate() {
            if(this.isDirtyMongo("configuration"))
                configurationService.save(id, CollectionConstants.RESOURCE_INSTANCE_IDENTIFIER, configuration)
         }

boolean isDirtyMongo(String property){
//return whether is dirty or not
}

So, the ultimate question is how can we call an update hook for transient field modifications as well.

Any help would be highly appreciated.


Solution

  • Implement the Interceptor.findDirty:

    public class TransientFieldDirtinessInterceptor extends EmptyInterceptor {
        @Override
        public int[] findDirty(Object entity, ..., String[] propertyNames, ...) {
            if ((entity instanceof EntityToCheck) && isTransientFieldDirty(entity)) {
                // Just return all fields as dirty
                int[] result = new int[propertyNames.length];
                for(int i = 0; i < result.length; i++) {
                    result[i] = i;
                }
                return result;
            }
            // Use Hibernate's default dirty-checking algorithm
            return null;
        }
    }
    

    Basically, let Hibernate think that all fields are dirty if the transient field is dirty.

    You could try to optimize this a little bit to mark just the first property as dirty (no matter how many of them are dirty, the entity is dirty if at least one property is dirty):

    int[] result = new int[1];
    result[0] = 0;
    return result;
    

    However, this will always exclude other properties from the SQL update statement if you use @DynamicUpdate for these entities, so I assume that the more clear and consistent way is to mark all properties as dirty (without @DynamicUpdate all properties are always included in the SQL update statement anyway).