In Grails is there something in domain class like onLoad()? - grails

Guys,
I have a following domain class:
class Product {
String name,
String productRecord,
static transients = ['productRecord']
}
productRecord is a field which is generated automatically based on the id of the Product instance.
So I've been thinking, is there a place which will be automatically called when a domain instance is load to generate the productRecord number?
What's the best way to do that?

You can probably leverage the built-in Domain Events:
GORM supports the registration of events as methods that get fired
when certain events occurs such as deletes, inserts and updates. The
following is a list of supported events:
beforeInsert - Executed before an object is initially persisted to
the database
beforeUpdate - Executed before an object is updated
beforeDelete - Executed before an object is deleted
beforeValidate - Executed before an object is validated
afterInsert - Executed after an object is persisted to the database
afterUpdate - Executed after an object has been updated
afterDelete - Executed after an object has been deleted
onLoad - Executed when an object is loaded from the database
Have a look at the docs for some examples.

Typically this is done by creating a read-only getter method and put the generation logic in there. For example:
class Product {
String name,
String getProductRecord{ "Record " + id },
static transients = ['productRecord']
}
Another example is available here.

Related

Grails 3: Disable database binding for incoming data

In Grails If I have a Command object such as
class MyCommand {
MyObject object;
}
If the incoming request data has an ID value for object then when the Grails data binding occurs my command object is actually populated with a instance of the object from the database.
I don't want this. I just want a new instance of MyObject populated with the incoming request data. I don't care of if there is already an instance in the DB with the same ID, I will handle that on my own.
How can disable this DB type data-binding at either a global level or preferably some way (annotation?) at the property level.
The only other alternative I can think of is when I send the request data I will have the ID values and object properties separate and join them later. I don't want to do that if it can be avoided.
You can have endpoints called differently using ROLES. For example:
def show(){
Person user = new Person()
if(isSuperuser() && params?.id){
user = Person.get(params.id.toLong())
}else{
user = Person.get(springSecurityService.principal.id)
}
...
}
This sets it up so only admin's can supply the ID otherwise it uses the principal to get the logged in user.

Grails: Is it possible to prevent a domain class instance from being persisted?

I want to create an (one) instance of a Domain class (which, as expected, has a GORM interface to my database) and only use it as a container to pass data around, like a Map object. I want to make absolutely sure that my instance is never going to get persisted in the database. I'm afraid that GORM, with all its cleverness, will somehow manage to save it in the database behind the scene even without an explicit call to save(). Is there a way to specify a "do not persist this" clause when instantiating my object? I know how to prevent persistence on a domain class, what I want is to prevent persistence on a particular instance of the class only.
The solution I have now is to create a class in groovy/src/ that carries the same properties and methods, and use it as my data container, and do type casts as required. It feels wrong, fails DRY, and hacky.
Of course you may also tell me that I should stop being so paranoid and that Grails is never going to persist an domain class instance without an explicit save.
Assume that, you already know how to prevent persistence(table creation) on a domain class. Furthermore, you also know that w/o explicit .save() object won't be persisted.
So, what do you want actually? Is it like.. even if someone accidentally call obj.save(), it will never persist.
Although that doesn't make any sense, but according to your query ,
Is there a way to specify a "do not persist this" clause when
instantiating my object?
Yes, there is a way :
class MyFishyDomain {
String pwd
// properties
// constraints
def beforeInsert() {
if (!this.pwd.equals("drago")) return false
}
def beforeUpdate () {
if (!this.pwd.equals("drago")) return false
}
}
Now..
new MyFishyDomain(pwd:"drago").save() // success
new MyFishyDomain(pwd:"rambo").save() // fail
By the way, if you want to permanently disable Create+Update+Delete But at the same time want to issue query against domain then solution is:
static mapping = {
cache usage: "read-only"
}
def beforeInsert() {
return false
}
Grails will not save an instance of your domain class without an explicit call to save() on the instance. You can create an instance and pass it around, and it will not be persisted.

How to load any attributes without using the Mapping ' lazy: false' in Grails

Constantly faced with a problem when I need to compare and manipulate objects that reference other objects. For example:
Class Student {
...
String Name
Integer Age
...
}
Class Stuff {
...
Student student
...
}
When I invoke an instance of Stuff (Stuff.get (id)/load(id)) and will access the Name, Age and other attribute I see in debug mode (stuff .name = null, they're like 'null' although they are not null. It
command when analyzing values ​​of these attributes (stuff
.name == "pen") error occurs.
I need to invoke the instances and compare their values ​​to execute business rules, but do not know how to resolve this issue.
I read something about the inclusion in the configuration Stuff Mapping 'student lazy: false' for all the time you need to load the instance ofstuff , also charge the Student, but that in addition to overload the memory (since stuff is a Domain Great) would solve this case being the only solution to put all references as 'lazy: false' which would slow the application just to make a simple comparison.
Does anyone know how to invoke instances (Stuff), automatically invoking the attribute to be working (student) just to make the comparison of data, without using the 'student lazy: false' that invokes the data at all times?...
Using Grails 2.2.0 e o Groovy 2
Stuff don't have a property called name so you should get MissingPropertyException calling stuff.name. This has nothing to do with the lazy or eager relationship.
You can check the definition of a lazy relationship in the documentation and also the difference between the types of fetch.
To access the name property you need to access the student property before:
Stuff instance = Stuff.get(id)
println instance.student.name //this, if lazy, will trigger a new database query.
If you know that your code will access the Student instance by the relation with Stuff you could fetch both in one database access (eager and not lazy):
Stuff instance = Stuff.withCriteria {
eq('id', id)
fetchMode("student", FetchMode.JOIN)
}

How to dynamically add a property / field to a domain class in Grails?

For a project I'm currently working on I need to dynamically add properties to a domain class and persist them later in the database. In general, I need a key/value store attached to a "normal" domain class. Sadly I cannot use a NoSQL database (e.g. Redis).
My approach would be to handle the additional properties on a save() by identifying them within afterInsert or afterUpdate and writing them to another table - I would prefer not to use a map property within the domain class but an additional "Field" table (to better support searches).
I tried to add properties using the metaClass approach:
person.metaClass.middlename = "Biterius"
assert person.middlename == "Biterius" // OK
This works and I can identify the additional properties in the afterInsert/afterUpdate methods but it seems that I cannot change the value thereafter - i.e., the following does not work:
person.middlename = "Tiberius"
assert person.middlename == "Tiberius" // FAIL
Then I tried an Expando approach by extending the Person class by the Expando class (directly ("Person extends Expando") and via an abstract intermediate class ("Person extends AbstractPerson" and "AbstractPerson extends Expando")).
def person = new Person()
assert person in Person // OK
assert person in AbstractPerson // OK
assert person in Expando // OK
Both variants did not work - I could assign values to arbitrary "properties" but the values were not stored!
person.mynewproperty = "Tiberius" // no MissingPropertyException is thrown
println person.mynewproperty // returns null
So how can I add properties to a domain class programmatically during runtime, change them and retrieve them during afterInsert or afterUpdate in order to "manually" store them in a "Fields" table?
Or am I doing something completely wrong? Are there other / simpler ways to do this?
What about turning your DB into a "NoSQL" one?
In one of my projects, I just used a String-property to store a map as JSON-Object.
For Groovy it's not a big problem to convert between a map and a JSON-Object. And since you can access a map just like an object with properties, I found this solution very convenient.
Only drawback: you have to plan the size of your String-property in advance...
Update: sorry, just read that you want to support searches...
what about
class Person {
...
static hasMany = [extProperties:KeyValue]
...
def invokeMethod(String name, args) {
if (name.startsWith('get')) {
//an unknown properties's getter is called
}
//add same for setter
}
}
class KeyValue {
String key
String value
}
I guess such a schema would give you all freedom you need. Even without the hasMany, you can make use of invokeMethod to handle your external tables...
The getter and setter can save your values in a transient string propertie (static transients = ['myTransientProperty']). This property should be available in the afterInsert / `afterUpdate´ events.
Why don't you just create a map of strings on the domain object and store your extra data there manually? Unless you're storing complex data you should be able to cast anything you need to/from a string.

Custom Insert and Update methods defeated by automatic Save

I am porting a Grails application from Oracle to MySQL database. The original Oracle version is a legacy database which uses a few complex Views leveraging Oracle's INSTEAD OF INSERT OR UPDATE feature which MySQL doesn't have. As a workaround I have implement Insert and Update methods on the Domain classes which point to these kinds of Views. For example,
class AdminUser {
//...
def update() {
if(this.validate()) {
Sql sql = Utils.getHibernateSql()
sql.execute(
"update table_x ...",
[...]
)
sql.execute(
"update table_y ...)",
[...]
)
sql.execute(
"update table_z ...",
[...]
)
return true
}
return false
}
//...
}
Here is a description of the problem I am running into at the moment:
Within a service method I load an instance of AdminUser using AdminUser.get
I modify the loaded instance and call update(), all appears well
Once the service method finishes executing an exception is thrown due to something calling save on the instance.
How can I prevent the magical save happening in step (3) (I recently read somewhere that Grails will automatically save a modified Domain class instance which hasn't yet been saved upon exiting a service method, but I can't seem to find the link to that resource right now)?
You're leaving the modified instance in the hibernate session with dirty fields. When the hibernate session gets flushed, it will try to save the object again with the usual save() method.
One solution would be to discard your object from the hibernate session after you've manually saved the changes. For example:
def update() {
if(this.validate()) {
Sql sql = Utils.getHibernateSql()
sql.execute(
"update table_z ...",
[...]
)
...
this.discard() // remove the object from the hibernate session
return true
}
In addition, you can add this to your Config.groovy to require that objects be saved explicitly:
hibernate.flush.mode="manual"
If you use read() instead of get(), then the object will not auto-persist changes. You can still call save() explicitly, but the standard behavior where the OpenSessionInView interceptor flushes all unsaved dirty instances will skip the instances loaded with read().

Resources