Grails: setting transient fields in the map constructor
Asked Answered
H

3

2

I'm trying to persist Maps of properties as single JSON-encoded columns, as shown in this question.

The problem I'm having is that apparently transient properties cannot be set in the default map constructor. Given any transient field:

class Test {
    //...
    String foo
    static transients = ['foo']
}

It seems that the map constructor (which Grails overrides in various ways) simply discards transient fields:

groovy:000> t = new Test(foo:'bar')
===> Test : (unsaved)
groovy:000> t.foo
===> null

While direct assignment (through the setter method) works as expected:

groovy:000> c.foo = 'bar'
===> bar
groovy:000> c.foo
===> bar

Is there a way to make the map constructor accept transient fields?


Or rather: is there a better way to persist a Map as a single JSON-encoded DB field, rather than the method shown in the linked question?

Here's the complete example:

import grails.converters.JSON

class JsonMap {
    Map data
    String dataAsJSON

    static transients = ['data']
    def afterLoad()      { data = JSON.parse(dataAsJSON) }
    def beforeValidate() { dataAsJSON = data as JSON }
}

I can set data using the setter (which will then be converted into dataAsJSON) but not using the map constructor.

Herewith answered 9/9, 2014 at 15:55 Comment(2)
Transient properties are not persisted. I think that this is impossible this way.Evangelista
I know they are not persisted. If you read the linked question, the Map property is declared transient on purpose.Herewith
F
4

The map constructor in GORM uses the data binding mechanism, and transient properties are not data-bindable by default. But you can override this using the bindable constraint

class Test {
    //...
    String foo
    static transients = ['foo']

    static constraints = {
        foo bindable:true
    }
}
Freesia answered 10/9, 2014 at 9:13 Comment(0)
N
1

I've also replied to your original question, that you don't need json conversion to achieve what you need. However, If you need json conversion badly, why don't you implement it in your getters/setters?

class Test {
    String propsAsJson

    static transients = ['props']

    public Map getProps() {
        return JSON.parse(propsAsJson)
    }

    public void setProps(Map props) {
        propsAsJson = props as JSON
    }
}

//So you can do
Test t = new Test(props : ["foo" : "bar"])
t.save()

In this way you encapsulate the conversion stuff, and in DB you have your properties as Json.

Nitrite answered 10/9, 2014 at 8:23 Comment(1)
Wouldn't this convert the data to Json every time you modify the property verses only converting it when you save?Eichhorn
S
0

You can simplify your case by adding the JSON-conversion methods to your domain class, they should have nothing to do with GORMing:

class Test {
    String title

    void titleFromJSON( json ){ 
      title = json.toStringOfSomeKind()
    }

    def titleAsJSON(){ 
      new JSON( title )
    }

}
Superstratum answered 9/9, 2014 at 22:17 Comment(1)
This provides less functionality than the existing afterLoad / beforeValidate method, and does not solve the issue of setting the transient value using the map constructor.Herewith

© 2022 - 2024 — McMap. All rights reserved.