Object not persisting when saved in afterInsert event - grails

I have a domain class like this:
class Domain {
String a
int b
String c
...
def afterInsert(){
def anotherDomain = new AnotherDomain()
anotherDomain.x=1
anotherDomain.y=2
if(anotherDomain.save()){
println("OK")
}else{
println("ERROR")
}
}
}
It prints "OK", I can even print the anotherDomain Object, and everything seems ok, no errors, nothing, but the anotherDomain Object doesn't persist in the database

You cannot persist the domain to database unless you try to save withNewSession.
def beforeInsert(){
def anotherDomain = new AnotherDomain()
anotherDomain.x=1
anotherDomain.y=2
AnotherDomain.withNewSession{
if(anotherDomain.save()){
println("OK")
}else{
println("ERROR")
}
}
}
}
All events are fired when domain object is flushed to database. Existing session is used for the flush. The same session cannot be used to handle save() on another domain. A new session has to used to handle the persistence of AnotherDomain.
UPDATE
Using beforeInsert event makes more sense than afterInsert. If x and y are dependent of any persisted value property of Domain they can very well be fetched from hibernate cache instead of going to db.

Had same problem here and just .withNewSession wasn't enough. I've putted .save(flush: true) and everything is working fine.
def afterInsert() {
AnotherDomain.withNewSession {
new AnotherDomain(attribute1: value1, attribute2: value 2).save(flush: true)
}
}

Related

Grails: Does the addTo() method not work on newly created objects?

I'm using Grails 2.5.5, and I have a simple scenario here: A has many Bs.
And so I have a transactional service method which basically does the following:.
A a
if (isNew) {
a = new A()
} else {
a = A.findById(someId)
}
List<B> bList = getBsFromSomeOtherMethod()
bList.each { a.addToBs(it) }
a.save(failOnError: true)
The interesting thing is that if I create a new object (as in, isNew is true in the above logic), then I get the following exception when save() is called: reassociated object has dirty collection reference (or an array).
However, if I get an object which already exists in the DB, then everything works perfectly.
The workaround I found is that if I save the new object before adding Bs to A, then things work. But I would rather not have to call save() twice, and the code is just a lot cleaner if the call was just at the end.
I've googled the exception but nothing seems to explain what's going on here.
Can somebody help out with this?
Like others have said in the comments, you need to save the object so it exists in the database, and has an id. When you have a A has many Bs relationship, a new table in the database (something like a_b) is created to map A.id to B.id, which is why you can't add Bs to A without saving first.
A a
if (isNew) {
a = new A()
a.save()
} else {
a = A.findById(someId)
}
List<B> bList = getBsFromSomeOtherMethod()
bList.each { a.addToBs(it) }
a.save(failOnError: true)
Use findOrSaveBy for such an operations. You will get the proper object from db or persist new one:
def a = A.findOrSaveByField(field)
List<B> bList = getBsFromSomeOtherMethod()
bList.each { a.addToBs(it) }
a.save(failOnError: true)

Grails edit working abnormally updating database on value assign

I am using grails-2.1.1. When I load the edit page, I am assigning some value in the edit action in controller. But it is updating my table! although I am not saving. How can I stop it?
Here is my code below. My edit action in controller:
def edit() {
def accTxnMstInstance = AccTxnMst.get(params.id)
if (!accTxnMstInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'accTxnMst.label', default: 'AccTxnMst'), params.id])
redirect(action: "list")
return
}
accTxnMstInstance?.accTxnDtls?.each {
if (it?.debitCoa != null && it?.debitCoa != "") {
String debitCoaVal = ""
List<String> items = Arrays.asList(it?.debitCoa?.split("\\s*~\\s*"))
items.each {
List itemList = new ArrayList()
List<String> subItems = Arrays.asList(it.split("\\^"))
subItems.each {
itemList.add(it)
}
itemList.add("false")
itemList.add("0")
itemList.each {
debitCoaVal += it.toString() + "^"
}
debitCoaVal += "~"
}
it?.debitCoa = debitCoaVal
debitCoaVal = ""
}
if (it?.creditCoa != null && it?.creditCoa != "") {
String creditCoaVal = ""
List<String> items = Arrays.asList(it?.creditCoa?.split("\\s*~\\s*"))
items.each {
List itemList = new ArrayList()
List<String> subItems = Arrays.asList(it.split("\\^"))
subItems.each {
itemList.add(it)
}
itemList.add("false")
itemList.add("0")
itemList.each {
creditCoaVal += it.toString() + "^"
}
creditCoaVal += "~"
}
it?.creditCoa = creditCoaVal
creditCoaVal = ""
}
}
[accTxnMstInstance: accTxnMstInstance]
}
You can see that I am not saving after assigning the value just passing to view.
Grails uses the Open Session In View (OSIV) pattern, where at the beginning of the web request a Hibernate session is opened (and stored in a thread-local to make it easily accessible) and at the end of the request as long as there wasn't an exception, the Hibernate session is flushed and closed. During any flush, Hibernate looks at all "active" object instances and loops through each persistent property to see if it is "dirty". If so, even though you didn't explicitly call save(), your changes will be pushed to the database for you. This is possible because when Hibernate creates an instance from a database row it caches the original data to compare later to the potentially-changed instance properties.
A lot of the time this is helpful behavior, but in cases like this it gets in the way. There are lots of fixes though. One drastic one is to disable OSIV, but this is generally a bad idea unless you know what you're doing. In this case there are two things you can try that should work.
One is to change AccTxnMst.get(params.id) to AccTxnMst.read(params.id). This will not cause the instance to be strictly "read-only" because you can still explicitly call save() and if something was modified, all of the instance changes will be persisted. But the caching of the original data isn't done for instances retrieved using read(), and there's no dirty checking during flush for these instances (which isn't possible anyway since there's no cached data to compare with).
Using read() is a good idea in general when retrieving instances that are not going to be updated (whether you make property changes or not), and makes the code more self-documenting.
Another option is to call discard() on the instance before the controller action finishes. This "detaches" the instance from the Hibernate session, so when the OSIV filter runs at the end of the request and flushes the Hibernate session, your instance won't be considered dirty since Hibernate won't have access to it.
read() only makes sense for individual instances retrieved by id, whereas discard() is useful for any instance, e.g. if they're in a mapped collection or were retrieved by a non-id query (e.g. a dynamic finder, criteria query, etc.)

GORM instance auto persist to database on update issue

I have a custom validation in controller because I need to validate with database.
def update(Object instance) {
if (instance == null) {
notFound()
return
}
if (instance.hasErrors()) {
//redirect code here
return
}
def obj = objService.someMethod()
//some validation code here
if(someCheck){
// if i discard it wont work
instance.discard()
flash.error = message(code: 'message.code')
//render code here
return
}
In the above code even the instance.discard() does not work after the database access is performed. The changed data is automatically saved even there is no save method call. The same instance.discard() will work if there is no database access is performed. How do I discard the changed value to be persisted to the database when there is a validation failure.
Add the annotation #Transactional(readonly = true) above your update action. That should do the trick.
This is the in built feature of Grails that automatically persist the instance to the database.
Please read my answer here for more detail
https://stackoverflow.com/a/32659803/2405040

Grails-null id in com.easytha.Student entry (don't flush the Session after an exception occurs)

I have already seen several threads for this issue and none could rescue
I have the following in my DomainClass
def afterInsert() {
elasticSearchService.index(this)
}
Where elasticsaerch is a service and I have added it to the static transient list. It seems that after calling the index method successfully it throws this exception
Message: null id in com.easytha.Student entry (don't flush the Session after an exception occurs)
This is the code of index method
def index(object) {
try {
if(object==null) {
throw new NullPointerException()
}
if(!(object instanceof Collection || object instanceof Object[])) {
IndexResponse response = client.prepareIndex(grailsApplication.config.esIndexName, object.getClass().getName(),object.id.toString())
.setSource((object as JSON).toString() )
.execute().actionGet()
println "object indexed successfully"
}else if(object instanceof Collection || object instanceof Object[]) {
for (var in object) {
index(var)
}
}
}catch(e) {
e.printStackTrace();
}
}
"object indexed successfully" is printed in the console.
The bootstrap.groovy has the following
Student student4 = new Student(firstName:'Sapan',lastName:'Parikh',email:'sapan.parikh#eclinicalworks.com',password:'test123')
student4.save(failOnError : true)
UPDATE
I tried Student.withNewSession { elasticSearchService.index(this) } which worked.
It's stabbing at things but maybe shift the save to happening within a transaction:
Student.withTransaction {
student4.save()
}
I've seen this pop up unexpectedly when doing things outside of services (that should, imo, be in services).
Summing up some subsequent discussion:
The student model was saved throughout the application, so it wasn't suitable to shift all the saves to services or wrap in transaction blocks. The OP notes that moving the original reindexing code into a new session fixed it everywhere.
def afterInsert() {
elasticSearchService.index(this)
}

Do I need a double save() after a domain modification using afterInsert()?

I have a domain class that modifies one of its properties in the afterInsert event.
A small example:
class Transaction {
Long transactionId
static constraints = {
transactionId nullable: true
}
def afterInsert() {
// copy the record id to transactionId;
transactionId = id
}
}
Whenever I save the domain object (transaction.save(flush: true)) in
my unit tests, all is well, and the transactionId is updated. But when I try to find the saved record using Transaction.findByTransactionId(), I get no results:
// do something
transaction.save(flush: true)
Transaction transaction = Transaction.findByTransactionId(1)
// !! no results; transaction == null
And I have to do a double save() before I can find the record using findByTransactionId():
// do something
transaction.save(flush: true)
transaction.save(flush: true)
Transaction transaction = Transaction.findByTransactionId(1)
// !! it works....
The double save() seems awkward. Any suggestions on how to eliminate the need for it?
The call to save() will return the persisted entity if validation passes, so there isn’t any reason to look it up separately afterwards. I think that your problem is that you’re re-instantiating the transaction variable (using that same name). If you must look it up (I don’t suggest doing so), call it something else. Also, the 1 id that you’re looking up may not exist if the column is an AUTO-INCREMENT.
def a = a.save(flush: true)
a?.refresh() // for afterInsert()
Transaction b = (a == null) ? null : Transaction.findByTransactionId(a.id)
// (Why look it up? You already have it.)
Update:
Because you’re using afterInsert(), Hibernate may not realize that it needs to refresh the object. Try using the refresh() method after you call save().
This small piece of code makes it obviously work:
def afterInsert() {
transactionId = id
save() // we need to call save to persist the changens made to the object
}
So calling save in the afterInsert is needed to persist the changes made in afterInsert!

Resources