I am trying to find proper way to implement soft delete for number of my entities. The best way I found so far is by using beforeDelete() callback.
So my class looks like this:
class Goal {
String definition;
Account account;
boolean tmpl = false;
String tmplName;
Timestamp dateCreated
Timestamp lastUpdated
Timestamp deletedAt
...
def beforeDelete() {
if (deletedAt == null) {
deletedAt = new Timestamp(System.currentTimeMillis())
this.save()
}
return false
}
}
Deletion in my case is executed through transactional service class, as follows:
def deleteTemplate(Goal tmpl) {
if (tmpl.tmpl != true) {
throw new ValidationException("Provided object is not template!")
}
//delete related perceptions
for (perception in tmpl.perceptions) {
perception.delete()
}
tmpl.delete()
}
All I am getting is an error:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [triz.rrm.Perception#3]. Stacktrace follows:
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [triz.rrm.Perception#3]
Line | Method
->> 63 | beforeDelete in triz.rrm.Goal
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 153 | call in org.grails.datastore.gorm.support.EventTriggerCaller$MethodCaller
| 96 | call . . . . . . . in org.grails.datastore.gorm.support.EventTriggerCaller
| 47 | onApplicationEvent in org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
| 138 | deleteTmpl . . . . in triz.rrm.RrmTemplateController
| 198 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter . . . . . in grails.plugin.cache.web.filter.AbstractFilter
| 53 | doFilter in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
| 49 | doFilter . . . . . in grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter
| 82 | doFilter in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
| 1142 | runWorker . . . . in java.util.concurrent.ThreadPoolExecutor
| 617 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 745 | run . . . . . . . in java.lang.Thread
I've tried everything including wrapping it with new session and new transaction, nothing helps. What am I missing?
Thank you for your advice. Alex.
P.S. If I am marking the service as NotTransactional, no errors but nothing is getting updated.
I don't know if that helps, but maybe you could try using hql in your beforeDelete():
def beforeDelete() {
if (deletedAt == null) {
Goal.executeUpdate('update Goal set deletedAt = ? where id = ?', [new Timestamp(System.currentTimeMillis()), id])
}
return false
}
I was trying to get around similar problem once, and the problem is that when you do update your way, it's not being done at once, but it goes to hibernate's session and it is persisted on flush and in the mean time such exceptions may occur on accessing the object. Hql saves it straight away