سؤال

I spent entire evening try to figure it out and I am desperate at this point. I have service class in my Grails 2.1.0 project. It performs queries against web-service - queries are threaded. Once thread got the data it calls synchronized method WriteToDB, which in turn takes a name (String field) and checks against domain via findByName. If a record exists then do nothing else create a new record.

Now I checked and method indeed allows only one thread at the time to be called, here is log sample:

Error 2013-01-02 21:29:47,408 [Thread-21] ERROR - WriteToDB START

Error 2013-01-02 21:29:47,474 [Thread-21] ERROR - WriteToDB FINISHED

Error 2013-01-02 21:29:47,475 [Thread-20] ERROR - WriteToDB START

Error 2013-01-02 21:29:47,571 [Thread-20] ERROR - WriteToDB FINISHED

Error 2013-01-02 21:29:47,581 [Thread-49] ERROR - WriteToDB START

Error 2013-01-02 21:29:47,866 [Thread-49] ERROR - WriteToDB FINISHED

Error 2013-01-02 21:29:47,867 [Thread-45] ERROR - WriteToDB START

Error 2013-01-02 21:29:48,202 [Thread-45] ERROR - WriteToDB FINISHED

Error 2013-01-02 21:29:48,203 [Thread-48] ERROR - WriteToDB START

Error 2013-01-02 21:29:48,320 [Thread-48] ERROR - WriteToDB FINISHED

However, I get duplicated records!!! Every time I save new record I do flush:true on it. I don't understand why is it keep happening? I tested manually - added one known record to DB prior to the test and it never got duplicated, however, new ones that are being saved get to be duplicated up-to 6 times.

Please help. Do I need to index something? Or flush something in a special way? Why is it happening?

[Update 2/1/2013]:

Here is a code that I am using to save the record:

      if(!f) { // Check if record doesn't exist then save, else nothing
          f = new Feature(name: featureName)
          if( !f.save(flush: true) ) {
              f.errors.each {
                  log.error it
              }
          }
      }

Also Error sample:

Error 2013-01-02 22:25:58,868 [Thread-20] ERROR util.JDBCExceptionReporter - Duplicate entry 'Automatic transmission' for key 'name'

Error 2013-01-02 22:25:58,873 [Thread-20] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) Message: null id in Project2.Feature entry (don't flush the Session after an exception occurs)

I can interpret this way (I got FindByName before 'save' part of the code): I lookup record and it doesn't exist so I am creating it, now it complains about Duplicate entry and I am getting null for variable 'f' which I later insert into another table as a relation.

I think I might try to fix this code with try catch block... ideas?

هل كانت مفيدة؟

المحلول

Okay, after some tests and research I found an issue.

Domain.save(flush:true) - didn't work because of threading.

Here is example of my thread creation:

Thread.start {
    Domain.withTransaction {
         // Doing stuff and calling synchronization method to write data to DB
    }
}

Fix was found here: Grails, GPars and data persistence

I replaced Domain.withTransaction with Domain.withNewSession:

Thread.start {
    Domain.withNewSession {
         // Doing stuff and calling synchronization method to write data to DB
    }
}

and save(flush: true) start writing into mySQL. Since data is written to mySQL, findBy... start returning proper results and therefore I application doesn't try to create duplicated record anymore. Issue solved!

Thank you, Jinzhao Wu, your suggestions helped a lot!

نصائح أخرى

Here is a simple solution: just add

static constraints = {
    name unique: true
}

to your domain class, then catch related exception in the service method instead of check validity with findByName.

Or you may need to post some code from your service method to find out the reason of duplication.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top