How to detect which field has been updated in afterUpdate|beforeUpdate GORM methods - grails

i use afterUpdate method reflected by GORM API in grails project.
class Transaction{
Person receiver;
Person sender;
}
i want to know which field modified to make afterUpdate behaves accordingly :
class Transaction{
//...............
def afterUpdate(){
if(/*Receiver is changed*/){
new TransactionHistory(proKey:'receiver',propName:this.receiver).save();
}
else
{
new TransactionHistory(proKey:'sender',propName:this.sender).save();
}
}
}
I can use beforeUpdate: and catch up the object before updating in global variable (previous as Transaction), then in afterUpdate, compare previous with the current object.
Could be?

Typically this would be done by using the isDirty method on your domain instance. For example:
// returns true if the instance value of firstName
// does not match the persisted value int he database.
person.isDirty('firstName')
However, in your case if you are using afterUpdate() the value has already been persisted to the database and isDirty won't ever return true.
You will have to implement your own checks using beforeUpdate. This could be setting a transient value that you later read. For example:
class Person {
String firstName
boolean firstNameChanged = false
static transients = ['firstNameChanged']
..
def beforeUpdate() {
firstNameChanged = this.isDirty('firstName')
}
..
def afterUpdate() {
if (firstNameChanged)
...
}
...
}

Related

Grails 3.3.10 value set in domain method is not being saved

I have a User class with a resetPasswordToken attribute, that is a UUID set when a user tries to reset his password.
On Grails 2.5.6 I had something like this that worked OK:
class UserController {
def forgotPassword(String email)
{
...
def user = User.findByEmail(email)
user.setPasswordToken()
user.save(flush: true()
...
}
}
class User {
...
String resetPasswordToken
static transients = ['passwordToken']
def setPasswordToken()
{
...
this.resetPasswordToken = (java.util.UUID.randomUUID() as String)
}
}
Now I migrated that to GRails 3.3.10 and the resetPasswordToken is NULL on the database after the forgotPassword action is invoked. If I do a println after the user.setPasswordToken() is invoked, I can see the resetPasswordToken is set to an UUID, but is not in the DB. Also checked for errors on the save, and there are no errors.
Strange thing, if I do user.resetPasswordToken = "xxxx" in the controller, the value is saved into the database correctly.
Not sure what is going on with the value set in the setPasswordToken() not being saved into the DB. Any pointers?
See the comment at https://github.com/grails/grails-data-mapping/issues/961#issuecomment-309379214. The issue you are experiencing is one of dirty checking, which changed in GORM 6.1.
Consider this code...
class Person {
String name
String email
void updateName(String newName) {
this.name = newName
}
static constraints = {
email email: true
}
}
That updateName method will not result in the name property being marked as dirty. The following code would result in the name property being marked as dirty:
class Person {
String name
String email
void updateName(String newName) {
setName newName
}
static constraints = {
email email: true
}
}
If you really want to turn on the old way of dirty checking you can do that per the instructions in the comment I linked above but be aware of the performance penalty of doing so. The recommended approach would be to use the setter or to explicitly mark the property as dirty using the markDirty method.
I hope that helps.

Grails data binding field exclusion

I am using Grails 2.5 and use Grails databinding in request methods.
For a basic example of the situation consider the following:
Domain class
class Product {
String field1
String privateField
}
Controller
class ProductController {
def update(Product productInstance) {
productInstance.save()
}
}
If I pass an existing Product to the controller like
{"id":3, "privateField":"newValue","field1":"whatever"}
the old value of privateField is overwritten. I want to enforce, that privateField is never bound from a request and avoid checking if the field is dirty.
Is there a mechanism in Grails to achieve this?
If I have to do the dirty check, how can I discard the new value and use the old one?
Pretty sure there's a "bindable" constraint.
http://grails.github.io/grails-doc/2.5.x/ref/Constraints/bindable.html
class Product {
String field1
String privateField
static constraints = {
privateField bindable: false
}
}
Should keep that field from binding automatically.
You can enforce which values are bound, but you'll need to change your method signature to get more control of the data binding process.
class ProductController {
def update() {
def productInstance = Product.get(params.id)
bindData(productInstance, params, [exclude: ['privateField']]
productInstance.save()
}
}

Grails enum as domain property intermittently saving as ordinal value in database

Grails 2.3.7, Java 1.7, tomcat
For no apparent reason(that I can see) the reviewResults property of the Review domain sometimes saves as the ordinal value of the enum.
The database schema says that the column is a varchar type.
Sometimes NA is saved as NA, however, sometimes it is saved as 0.
This is the same with passed, except passed is saved as 1 sometimes.
Any ideas why this might be happening?
abstract class Work {
// Nothing defined in here related
// to the review domain enum.
}
The Domain class in question.
class Review extends Work {
Results reviewResults
String notes
enum Results {
NA,
Passed,
Error
}
static constraints = {
reviewResults(nullable: true, enumType: 'string')
}
// Is this redundant if it is already declared in the constraints?
static mapping = {
reviewResults enumType: 'string'
}
}
The Domains related controller.
// Reviews created with Quartz job, results property is not set.
class ReviewController {
def reviewService
def show(Long id) {
def reviewInstance = Review.get(id)
def reviewResultsOptions = []
reviewResultsOptions.addAll(com.mycompany.app.Review.Results.values())
[reviewInstance: reviewInstance, reviewResultsOptions: reviewResultsOptions]
}
def closeWork(Long id, String reviewResults, String notes) {
def review = Review.get(id)
review.reviewResults = Review.Results.valueOf(reviewResults)
def result = reviewService.closeReview(review, notes)
}
}
The service for the controller/domain.
class ReviewService {
def workService
Review closeReview(Review work, String notes) {
work.notes = notes
workService.closeWork(work)
return work.errors.allErrors.empty ? work : null
}
}
The service where the Review object is finally saved.
class WorkService {
Tracking closeWork(Work workInstance) {
def tracked = new Tracking()
workInstance.setStatus(status)
workIntance.setClosed(new Date())
WorkInstance.save(flush: true)
// Set some tracking properties and save and return the tracking object.
}
}

Adding Dynamic Fields to Domain Object in Grails

I am trying to find a way to add dynamic fields to a grails domain class. I did find the dynamic domain class plugin based on Burt's article, but this is way too much for our needs.
Supposed we have a domain class of person:
class Person extends DynamicExtendableDomainObject {
String firstName
String lastName
static constraints = {
firstName(nullable: false, blank: false, maxSize: 50)
lastName(nullable: false, blank: false)
}
}
Now customer a wants to also have a birthdate field in this. By using some sort of management tool, he adds this extra field in the database.
Customer b wants to also have a field middle name, so he is adding the field middle name to the person.
Now we implemented a DynamicExtendableDomainObject class, which the Person class inherits from. This adds a custom field to each Domain class inheriting from this to store the dynamic properties as JSON in it (kind of like KiokuDB in Perl stores them).
Now when Person is instantiated, we would like to add those dynamic properties to the Person class, to be able to use the standard Grails getter and setter as well as Templating functions for those.
So on customer a we could use the scaffolding and person would output firstName, lastName, birthDate, on customer b the scaffolding would output firstName, lastName, middleName.
The storing of the properties will be implemented by using the saveinterceptor, to serialize those properties to JSON and store them in the special field.
But we have not yet found a way to add these JSON properties dynamically to the domain class during runtime. Is there a good way to handle this? And if so, how to best implement this?
You can try to add the properties at runtime to the DomainClass of type DynamicExtendableDomainObject by expanding getProperty(), setProperty(), setProperties() in the metaClass and then use beforeUpdate(), beforeInsert() and afterLoad() to hook into Persistence.
For example in Bootstrap (or service):
def yourDynamicFieldDefinitionService
for(GrailsClass c in grailsApplication.getDomainClasses()){
if(DynamicExtendableDomainObject.isAssignableFrom(c.clazz)){
Set extendedFields = yourDynamicFieldDefinitionService.getFieldsFor(c.clazz)
//getProperty()
c.clazz.metaClass.getProperty = { String propertyName ->
def result
if(extendedFields.contains(propertyName)){
result = delegate.getExtendedField(propertyName)
} else {
def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
if(metaProperty) result = metaProperty.getProperty(delegate)
}
result
}
//setProperty()
c.clazz.metaClass.setProperty = { propertyName , propertyValue ->
if(extendedFields.contains(propertyName)){
delegate.setExtendedField(propertyName, propertyValue)
delegate.blobVersionNumber += 1
} else {
def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
if(metaProperty) metaProperty.setProperty(delegate, propertyValue)
}
}
//setProperties()
def origSetProperties = c.clazz.metaClass.getMetaMethod('setProperties',List)
c.clazz.metaClass.setProperties = { def properties ->
for(String fieldName in extendedFields){
if(properties."${fieldName}"){
delegate."${fieldName}" = properties."${fieldName}"
}
}
origSetProperties.invoke(delegate,properties)
}
}
}
with
abstract DynamicExtendableDomainObject {
String yourBlobField
Long blobVersionNumber //field to signal hibernate that the instance is 'dirty'
Object getExtendedField(String fieldName){
...
}
void setExtendedField(String fieldName, Object value){
...
}
def afterLoad(){
//fill your transient storage to support getExtendedField + setExtendedField
}
def beforeUpdate(){
//serialize your transient storage to yourBlobField
}
def beforeInsert(){
//serialize your transient storage to yourBlobField
}
}

Grails/GORM not cascading save through entire object hierarchy

I'm having trouble getting saves cascaded down my object hierarchy. Below is the code of my object hierarchy.
class Entity {
static hasMany = [attributes: Attribute]
}
class Attribute extends ValuePossessor {
static belongsTo = Entity
}
abstract class ValuePossessor {
def valueService
Value value
void setValue(val) {
this.value = valueService.Create(val)
this.value.possessor = this
}
}
abstract class Value {
static belongsTo = [possessor: ValuePossessor]
}
class StringValue extends Value {
String value
}
The valueService is simply a service with a big switch statement that creates the correct value type (string, boolean, int, etc.).
Entity e = new Entity()
Attribute attr = new Attribute()
attr.setValue(1)
e.addToAttributes(attr)
e.save()
The above code correctly creates all objects, but fails to save the value object. The entity and attribute are saved, but the value is not. Am I missing some identifier needed to cascade all the way down to the value object?
Figured this out. Apparently there is some magic in the grails dynamic setters. I changed the setValue(val) method to set(val) and it started working. Lesson learned: don't override grails' dynamically added methods because they are built with magic, pixy dust, and unicorn urine.

Resources