2
votes

we have been testing a different way of saving. However, the results weren't as we expected. We have create-survey method, and each survey has multiple questions. We tested few cases and they all committed the queries the same way.

@Transactional class Service {
      Survey createNewSurvey(NewSurveyCommand command) {
       Survey survey = new Survey()
       survey.properties[] = command.properties
       survey.save(flush: true, failOnError: true)  //save survey and flush
       for (NewQuestionCommand questionCommand : command.questions) {
           Question question = new Question()
           question.properties[] = questionCommand.properties
           question.save(flush: true, failOnError: true)  // save each questions and flush
       }
       return survey    } }

The second removing transactional and saving without flush

 class Service {
      Survey createNewSurvey(NewSurveyCommand command) {
       Survey survey = new Survey()
       survey.properties[] = command.properties
       survey.save()  //save survey and flush
       for (NewQuestionCommand questionCommand : command.questions) {
           Question question = new Question()
           question.properties[] = questionCommand.properties
           question.save()  // save each questions and flush
       }
       return survey    } }

The 3th and 4th, once with transactional and once without transactional.

class Service {
          Survey createNewSurvey(NewSurveyCommand command) {
           Survey survey = new Survey()
           survey.properties[] = command.properties
           survey.save()  //save survey and flush
           for (NewQuestionCommand questionCommand : command.questions) {
               Question question = new Question()
               question.properties[] = questionCommand.properties
              survey.addToQuestions()
    }
           survey.save(flush: true, failOnError: true)
           return survey    } }

In the end from MySQL log, we checked that no matter we did all the inserting happened within one commit.

    Query    SET autocommit=0
    Query    insert into survey (version, brand ,...)
    Query    insert into question (version,..d)
    Query    insert into question (version,..d)
    Query    commit
    Query    SET autocommit=1

In the end we didn't see any difference between .save(flush: true, failOnError: true), save() ( with or without Transactional).

Could anybody explain how are save with flush and without flush working.?

Grails doc says that flush (optional) - When set to true flushes the persistence context, persisting the object immediately. However, in our case we saw, it didn't happen as in doc says. Or did I misunderstand it?

1
In short, GORM/Hibernate attempts to save all your INSERT/UPDATE statements until the session flushes. This is either until you manually call .save(flush: true) or the session goes out of scope (e.g. final controller renders/responds). That's the default behavior. Everything above seems to be normal. What did you expect to happen? - Joshua Moore
@JoshuaMoore, when I put .save(flush: true) after each save, I expect database logs to be as following. paste.ofcode.org/L8fndE865TA8htymJar82J. For each save(flush: true), one commit - Etibar - a tea bar
You missed the keyword in both the docs and my reply ATTEMPTS. You won't be able to control the level of commits to the database using flush: true in such a precise manner. There are a lot of factors involved and most of the time GORM/Hibernate is going to make the best choice for you. - Joshua Moore
so you are saying that each session is not creating a new commit in the database. Commits are handled by hibernate. - Etibar - a tea bar
You tried any without even issuing a save ? Or even a multi layered transactional where 1 calls multiple other transactionals ? typically save() is sufficient. In specific places where you want to ensure semi segment of change has gone through use .withNewTransaction and .save(flush:true) to ensure it has gone through. You obviously have not had the requirement to use flush:true so in your current scope of tests it is no different github.com/vahidhedayati/grails-queuekit-plugin/… you can see in these cases I had to - to ensure it got picked up - V H

1 Answers

4
votes

save() without flush: true doesn't start the database connection. After calling save() data is only persisted in Hibernate session. So in your case, you would not find any related lines in MYSQL log file.

save(flush: true) starts the transaction on database level immediately. So after calling save(flush: true) for the first time you should already see some lines in your MYSQL log file, probably sth like:

Query    SET autocommit=0
Query    insert into survey (version, brand ,...)

After calling save(flush: true) for the second time the transaction is being continued (not started again, so no COMMIT will happen between two saves) on database level. You can also see lines added to your MYSQL log file.