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

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.

Related

why does save() not save the data and save(flush: true) is required?

RaceRegistration domain has embedded raceParticipant and raceParticipant has a field bibNumber which is Integer.
I have a method for nulling out all bibNumbers of registrations but without flush:true in save, the nulling out of bibs dont work. The bibs are not set to null.
def nullifyBibNumbers(Long id){
...
def regss = RaceRegistration.createCriteria().list(){
eq('compositeEvent', event)
}
regss.each{ r ->
r.raceParticipant.bibNumber = null
r.save()
}
render "Bibs resetted!"
}
If i add flush:true then the bibs are set to null.
regss.each{ r ->
r.raceParticipant.bibNumber = null
r.save(flush: true)
}
I am wondering why you need flush in order for the value to be set to null? I am guessing the problem is with regard to how i am obtain the registration list using createCriteria(). I appreciate any help in this dilemma i am facing. Thanks!
As you probably figured out, save(flush: true) forces Hibernate to write any pending changes to the database. Without the explicit flush, you're relying on a Hibernate transaction to automatically flush when the transaction commits.
The reason only an explicit flush is working for you is because you're not calling save() within a transaction.
The cleanest fix is to create a Grails service, put nullifyBibNumbers() in it, and make the service transactional. That will cause nullifyBibNumbers() to get wrapped in a transaction so that you can use save() without an explicit flush.
If nullifyBibNumbers() is already in a service, you can add #Transactional to the service class, just keep in mind that it will make all methods (perhaps only the public ones?) transactional. Having said that, you can use #NotTransactional on a method to disable transactions.
The value is null in your domain object. But you are talking about null in the database, I guess?
It shouldn't matter. This is basic ORM. As a developer you don't care about when the flush is done. Typically this would be at the end of a transaction. The ORM will then flush all of the changes for that transaction at once.
It works on what is called the first-level cache during the transaction, and tries to avoid going to the db until it is explicitly requested (flush:true) or required (end of transaction).
Without the using of
save(flush: true)
The object will not be persisted immediately.
You can follow the documentation link and see the following information:
The save method informs the persistence context that an instance
should be saved or updated. The object will not be persisted
immediately unless the flush argument is used.
Related to the null issue you are facing make sure that the following condition are met.
The save method returns null if validation failed and the instance was
not persisted, or the instance itself if successful.
You do not need the flush in order for the value to be set to null.
The flush only care of a quick update of the database.
ok i fixed this problem using HQL instead of domain saves. Still i would appreciate why save() didnt work and save(flush:true) saved the data. Thanks!
RaceRegistration.executeUpdate("update RaceRegistration set raceParticipant.bibNumber = null where compositeEvent.id = :ev", [ev: id])

In GORM, Object is not modified in database when the object property is modified at Controller

In my Grails App, I have bootstrapped an object of a domain class as:
def user1 = new Users(userID: 1, name: "John Doe")
user1.save()
In my dashboard controller i have retrieved the object and modified its property name as:
Users userObj = Users.get((Long) 1)
println(userObj as JSON); // This gives me: {"class":"com.prabin.domains.Users","id":1,"name":"John Doe"}
userObj.name = "anonymous"
Now i create a new Users object to retrieve the changed object with same ID 1 as
Users otherUserObj = Users.get((Long) 1) // **Line 2** Is this retrieving from database or from GORM session?
print(otherUserObj as JSON)// This gives me: {"class":"com.prabin.domains.Users","id":1,"name":"anonymous"}
But the value of object in database is not changed. And even when i retrieve the Users object of same id 1 in another controller it gives me the initial object rather than the modified as:
Users userObjAtDifferentController = Users.get(1);
print(userObjAtDifferentController) //This gives me: {"class":"com.prabin.domains.Users","id":1,"name":"John Doe"}
My question is, if the value is not changed in the database, why it gives me the modified object at Line 2 though i have retrieved the object using GORM query (which i guess should retrieve from the database)?
I have also tried using save() after the modification but the result is same.
userObj.save() //doesn't show the changes in database.
My guess is that the object is not being saved to the database because some constraint(s) are invalid. You can determine whether this is the case by replacing your calls to save() with save(failOnError: true). If my guess is correct, an exception will be thrown if saving to the database fails.
When you call the save() method on a domain object, it may not persist in the database immediately. In order to persist the changed value to the database, you would need to do the following.
userObj.save(flush: true)
By using flush, you are telling GORM to persist immediately in the database.
In some cases when validation fails, the data will still not persist in the database. The save() method will fail silently. To catch validation errors as well as save to the database immediately, you would want to do the following
userObj.save(flush:true, failOnError:true)
If validation errors exist, then the GROM will throw ValidationException (http://docs.grails.org/latest/api/grails/validation/ValidationException.html)
You need to consider two things:
If you do save(), it only retains in hibernate session, until you flush it the changes does not persist in database. So, better do save(flush:true, failOnError: true) and put in try/catch block and print exception.
And another important things is, your method in controller needs to be with #Transactional annotation, without it your changes does not persist in database, you will encounter Connection is read-only. Queries leading to data modification are not allowed. exception.
Hope this helps, let me know if your issue is fixed. Happy coding . :)

Entity not saved

i created my domain-class via editor as usual, i create the controller and some views as usual too.
I use the ui as usual to create an database-entry as usual, and the .save() method returns true.
But no database-row has been insert to the database!
Any suggestions?
Do something like this to your domain object and then print the errors. Save will refuse to save if there is any constraints violation.
def user = new User(params)
if (user.save()) {
return user
}
else {
user.errors.allErrors.each {
println it
}
}
you might get more insight if you do save( flush:true, failOnError: true ). This will throw an exception and help you diagnose it if it is hibernate failing down the chain.
Also, you might want to make sure your database settings are right ( i.e., in-memory databases get wiped ). Do you see anything when you call Domain.list() afterwards?
It was an different Case
I found
static transactional = false
on the top of my class.
BS!

Cancel/Block the save of a domain object based on some criteria?

i have a need to block or cancel a save of a domain object based on some property.
Can this be done in a constraint?
Example:
An 'Order' domain object has a state of 'invoiced' then the order should not be able to be updated anymore..
Any suggestions on how to tackle this?
I see no reason why you couldn't simply use a constraint for this (as you suggested). Something like this should do it
class Order {
String state
static constraints = {
state(validator: {stateValue, self ->
// only check state if this object has already been saved
if (self.id && stateValue == 'invoiced') {
return false
}
})
}
}
If for some reason you can't use a constraint, here are a couple of alternative suggestions:
Meta-Programming
Use Groovy's method-interception capabilities to intercept calls to save(). Your interceptor should only forward the call to the intercepted save() if the order does not have an invoiced state.
There are some good examples of how to do this in the Programming Groovy book
GORM Events
GORM provides a number of events that are triggered during a persisted objects lifecycle. It may be possible in the beforeUpdate or beforeValidate events to prevent updating the object (I guess throwing an exception would work)

Where to store a Doctrine variable created in a component so that it's accessible anywhere?

Note I am referring to one request, and not several requests and sessions.
I have several components that require Doctrine user object, some are located in layout, others are located in templates. Sometimes I need that Doctrine user object in action. Currently I have added a function to sfUser class that loads that object from database, which means every time I call that function I make a call to db. I'd like to know where to store this object so that I can access it without having to query db every time I need it. Again, we're talking about a single request, not several requests or something that would require session.
Can I save it in sfContext somehow? Any other places so that it can be available everywhere?
You can store it in your model's Table class, because tables are always accessed as singletones.
class sfGuardUserTable extends PluginsfGuardUserTable
{
protected $specialUser = null;
public function getSpecialUser()
{
if (null === $this->specialUser)
{
$this->specialUser = $this->findOneById(1);
}
return $this->specialUser;
}
}
Now, you can use this in actions and components like this:
$u = sfGuardUserTable::getInstance()->getSpecialUser();
And you will always end up with one query.
you can configure Doctrine cache so that the result of this specific request is always cached. What if so good about it is that if you use, say, the APC backend, you will have it cached across requests. You also get query caching as a bonus (this is not result caching, read the link I provided carefully)!

Resources