Grails Transactions and the Session

懵懂的女人 提交于 2019-12-22 16:44:55

问题


Imagine you have the following controller in a Grails 2.5.5 application:

def index() {
        bookService.method()
        Book bako = Book.findById(4)
        System.out.println(bako.title);
}

And inside the bookService (with Grails default transaction management) you have the following method:

class BookService
    def method() {
        Book bako = Book.findById(4)
        System.out.println(bako.title);

        // MANUAL UPDATE OF DB HAPPENS HERE

        Book bako = Book.findById(4)
        System.out.println(bako.title);
    }
}

And that your db does have a Book with id 4 called "The Lord of The Rings".

If you then set breakpoints on all System.out.println() and, after the first findById has been executed, you manually edit the title of that object to "Harry Potter and the Goblet of Fire", I expected:

  • Both findById(4) in bookService.method() to read the same value, after all, they were performed in isolation in the same transaction, and if that transaction read it in that state, the second one should read it too.
  • The findById(4) performed in the controller to already see the new state of the object, after the manual update. After all, the first transaction has already commited, and this second find, even if done outside a service, should create a new transaction.

However, the output will always be the object in the same state it was at the beginning.

Update: The output is:

The Lord Of The Rings
The Lord Of The Rings
The Lord Of The Rings

If, in any case, you modifiy the controller, in order to:

def index() {
    bookService.method()
    Book.withNewTransaction {
        Book bako = Book.findById(4)
        System.out.println(bako.title);
    }

}

The result is still the same.

Update: The output is:

The Lord Of The Rings
The Lord Of The Rings
The Lord Of The Rings

Only if you modify it to:

def index() {
    bookService.method()
    Book.withNewSession {
        Book bako = Book.findById(4)
        System.out.println(bako.title);
    }

}

does the correct behavior ensue.

Update: The output is:

The Lord Of The Rings
The Lord Of The Rings
Harry Potter and the Goblet of Fire

Can someone explain me why:

  1. Just being outside the transaction that read an object in a certain state is not enough to read fresh data;
  2. Even forcing a new transaction is not enough to read the object with its most up to date state;
  3. Why is the new session what allows us to do so.

回答1:


Firstly Book bako = Book.findById(4) findById should be used in rare cases refer to Book.get(1L) Book.load(1L) Book.read(1L)

You are firing up a query to look for id when you could have just run .get

Actual issue

After much talk, no matter how much a service is transactional. If you decide to update the DB using mysql manually. You will break hibernate cache. You can try disabling - first / second level cache. First being if you declared caching in your domain class mapping.

This is really unwise and will have application impact. The reality is a transaction service should be doing the updates for you. If you need to manually update the database. Stop the app update / start the app. It is really that simple

There is a reason why I have been trying to push you down the route of attempting this scenario using an example project.

Why ? Because it helps answer any speculation. I have taken a copy of your sample project and added some actual record updates to the demo. https://github.com/vahidhedayati/grails-transactions

I have also made a pull request on your version so you can merge it and test it locally.

Basically flush:true not required. .get(id) not required.

As you can see from the results below In service after .save() on method 1 the results was updated. In controller it returned the correct result using method() after service returned it.

-- transactions.Book : 1 1 added
| Server running. Browse to http://localhost:8080/transactions
2016-09-05 18:12:48,520 [http-bio-8080-exec-4] DEBUG hibernate.SQL  - select book0_.id as id1_0_0_, book0_.version as version2_0_0_, book0_.title as title3_0_0_ from book book0_ where book0_.id=?
--method1: before update: ------------------------------> TITLE_SET_BY_BOOTSTRAP
--method1 before get: ---------------------------------> New title from method1
method1  after get: ----------------------------------> New title from method1
2016-09-05 18:12:48,618 [http-bio-8080-exec-4] DEBUG hibernate.SQL  - update book set version=?, title=? where id=? and version=?
After service1 call 1 New title from method1
--method2  update: ------------------------------> New title from method1
2016-09-05 18:12:48,687 [http-bio-8080-exec-4] DEBUG hibernate.SQL  - update book set version=?, title=? where id=? and version=?
--method2 before get: --------------------------> New title from method2
method2 after get:  ----------------------------> New title from method2
After service call 2 New title from method2
--method3 before update: ---------------------------> New title from method2
2016-09-05 18:12:48,795 [http-bio-8080-exec-4] DEBUG hibernate.SQL  - update book set version=?, title=? where id=? and version=?
--method3 updated before get: -------------------------> New title from method3
--method3 after get: -----------------------------------> New title from method3
After service call 3 New title from method3

After reviewing the user issue ages back and having understood that they were manually updating DB record then expecting screen to show the same result.

In short if you have no cache enabled in the application then yes it should all work. If you have some form of Hibernate cache or ehcache enabled then it is likely you will be looking at some cache object. I had suggested restarting application to ensure you have latest. But if you simply:

wrap a

DomainClass.withNewTransaction { 
  //get the latest copy
  DomainClass clazz = DomainClass.get(recordId)
  println "-- ${clazz.value}"
}

This should ensure you are getting the very latest from the DB, it isn't going to be speed efficient but if you are expecting manual db updates you could always ensure the latest is above..



来源:https://stackoverflow.com/questions/39296682/grails-transactions-and-the-session

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!