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

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])

Related

Why Hybris modelService.save() doesn't work inside the ifPresent() method?

private void doSomething(someProcessModel process){
CustomerModel customer = process.getCustomerModel();
customer.getFoos().stream()
.filter(foo -> foo.getCountryCode().equals(process.getCountryCode()))
.findFirst()
.ifPresent(foo -> {
if(foo.getSomeNumber() == null){
foo.setSomeNumber("1234567");
modelService.save(foo);
}
});
}
As seen in the code snippet above, I have a 'CustomerModel' that has an attribute 'Foos'. It's a one-to-many relationship. As you can see I have done some filtering and in the end, I want to update the value of 'someNumber' attribute of 'Foo' if it is null. I've confirmed that everything is working as the "someNumber" attribute's value is updated during the debugging. It doesn't save at all as I have done my checking in the HMC. I have also validated that the Interceptor doesn't have any condition that would throw an error. There is nothing being shown in the log either.
I am wondering is it a legal approach to do the "modelService.save()' inside the 'ifPresent()' method? What could be the possible issue here?
I have found the root cause now as I have face the same issue again.
Context to my original question
To give more context to my original question, the #doSomething method resides in a Hybris Business Process action class and I have ended the action prematurely while I am debugging it (by stopping the debugging) once the #doSomething method is ran.
Root cause
The mentioned problem happened when I was debugging the action class. I assumed that the ModelService#save will persist the current state of the business process once it has been ran. However, the Hybris OOTB business process will do a rollback if there is any error (and I believe it was caused by me stopping the debugging half-way).
SAP Commerce Documentation:
All actions are performed inside their own transaction. This means that changes made inside the action bean run method are rolled back in case of an error.
Solution
Let the action runs completely!
Good to know
Based on the SAP Documentation and this blog post, there will be times that we will need to bypass a business process rollback even though there is an exception being thrown and there are ways to achieve that. More info can be found in this SAP Commerce Documentation and the mentioned blog post.
However, in some circumstances it may be required to let a business exception reach the outside but also commit the transaction and deal with the exception outside. Therefore, it is possible to make the task engine not roll back the changes made during a task which failed.
You have to be cautious with a list in models as they are immutable, you have to set the whole new list. Also you called save only on a particular model, which changes its Jalo reference, that's why your list is not updated. Mutating stream and collecting it in the end will create new list that's why you can stream over the list directly from the model.
private void doSomething(someProcessModel process){
CustomerModel customer = process.getCustomerModel();
ArrayList<FooModel> foos = doSomethingOnFoos(customer.getFoos());
customer.setFoos(foos);
modelService.saveAll(foos, customer);
}
//compare the value you know exists with something that might be NULL as equals can handle that, but not the other way around
private ArrayList<FooModel> doSomethingOnFoos(ArrayList<FooModel> fooList) {
return fooList.stream()
.filter(Objects::nonNull)
.filter(foo -> process.getCountryCode().equals(foo.getCountryCode()))
.filter(foo -> Objects.isNull(foo.getSomeNumber()))
.map(foo -> foo.setSomeNumber(1234))
.collect(toList());
}

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 . :)

When does grails check for Object Staleness?

I'm using Grails 2.5.1, and I have a controller calling a service method which occasionally results in a StaleObjectStateException. The code in the service method has a try catch around the obj.save() call which just ignores the exception. However, whenever one of these conflicts occurs there's still an error printed in the log, and an error is returned to the client.
My GameController code:
def finish(String gameId) {
def model = [:]
Game game = gameService.findById(gameId)
// some other work
// this line is where the exception points to - NOT a line in GameService:
model.game = GameSummaryView.fromGame(gameService.scoreGame(game))
withFormat {
json {
render(model as JSON)
}
}
}
My GameService code:
Game scoreGame(Game game) {
game.rounds.each { Round round ->
// some other work
try {
scoreRound(round)
if (round.save()) {
updated = true
}
} catch (StaleObjectStateException ignore) {
// ignore and retry
}
}
}
The stack-trace says the exception generates from my GameController.finish method, it doesn't point to any code within my GameService.scoreGame method. This implies to me that Grails checks for staleness when a transaction is started, NOT when an object save/update is attempted?
I've come across this exception many times, and generally I fix it by not traversing the Object graph.
For example, in this case, I'd remove the game.rounds reference and replace it with:
def rounds = Round.findAllByGameId(game.id)
rounds.each {
// ....
}
But that would mean that staleness isn't checked when the transaction is created, and it isn't always practical and in my opinion kind of defeats the purpose of Grails lazy collections. If I wanted to manage all the associations myself I would.
I've read the documentation regarding Pessimistic and Optimistic Locking, but my code follows the examples there.
I'd like to understand more about how/when Grails (GORM) checks for staleness and where to handle it?
You don't show or discuss any transaction configuration, but that's probably what's causing the confusion. Based on what you're seeing, I'm guessing that you have #Transactional annotations in your controller. I say that because if that's the case, a transaction starts there, and (assuming your service is transactional) the service method joins the current transaction.
In the service you call save() but you don't flush the session. That's better for performance, especially if there were another part of the workflow where you make other changes - you wouldn't want to push two or more sets of updates to each object when you can push all the changes at once. Since you don't flush, and since the transaction doesn't commit at the end of the method as it would if the controller hadn't started the transaction, the updates are only pushed when the controller method finishes and the transaction commits.
You'd be better off moving all of your transactional (and business) logic to the service and remove every trace of transactions from your controllers. Avoid "fixing" this by eagerly flushing unless you're willing to take the performance hit.
As for the staleness check - it's fairly simple. When Hibernate generates the SQL to make the changes, it's of the form UPDATE tablename SET col1=?, col2=?, ..., colN=? where id=? and version=?. The id will obviously match, but if the version has incremented, then the version part of the where clause won't match and the JDBC update count will be 0, not 1, and this is interpreted to mean that someone else made a change between your reading and updating the data.

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.

why read-only access is writing to my db, in GORM?

In my app, I have a code like this:
// 1
Foo.get(123).example = "my example" // as expected, don't change value in db
// 2
Foo.get(123).bars.each { bar ->
bar.value *= -1 // it's changing "value" field in database!! WHY?
}
note: Foo and Bar are tables in my DB
Why is gorm saving in database is second case?
I don't have any save() method in code.
Tks
SOLVED:
I need to use read() to get a readonly session.
(Foo.discard() also works)
Doc: http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.1.1%20Basic%20CRUD
(In the first case, I guess I made mistest)
Both should save, so the first example appears to be a bug. Grails requests run in the context of an OpenSessionInView interceptor. This opens a Hibernate session at the beginning of each request and binds it to the thread, and flushes and closes it at the end of the request. This helps a lot with lazy loading, but can have unexpected consequences like you're seeing.
Although you're not explicitly saving, the logic in the Hibernate flush involves finding all attached instances that have been modified and pushing the updates to the database. This is a performance optimization since if each change had been pushed it would slow things down. So everything that can wait until a flush is queued up.
So the only time you need to explicitly save is for new instances, and when you want to check validation errors.

Resources