Grails交易和会话 [英] Grails Transactions and the Session

查看:75
本文介绍了Grails交易和会话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您在Grails 2.5.5应用程序中拥有以下控制器:

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

在bookService中(使用Grails默认事务管理),您有以下方法:

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

//手动更新DB HAPPENS HERE

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




$ b $ p
$ b

而且你的db有一本书id为4称为指环王。



如果您在所有System.out.println()中设置了断点,并且在第一次执行findById之后,你手动编辑该对象的标题哈利波特与火焰杯,我预料:


  • 在bookService.method()中findById(4)读取相同的值,毕竟它们是在同一个事务中单独执行的,如果该事务在该状态下读取它,则第二个应该读取它。

  • 手动更新后,控制器中执行的findById(4)已经可以看到对象的新状态。毕竟,第一笔交易已经提交,而第二笔交易即使在服务外完成,也应该创建一个新交易。


但是,输出始终是处于开始时处于相同状态的对象。



更新:输出结果为:

 指环王
指环王
指环王

如果在任何情况下您修改了控制器,以便:

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

}

结果仍然是一样的。



更新:输出结果为:

 $ 

仅当您将其修改为:

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

}

更新:输出结果为:



<$ p $ 指环王
指环王
哈利波特与火焰杯

有人可以解释为什么:


  1. 只是在读取对象的事务之外某些状态不足以读取新数据; 甚至强制一个新事务不足以读取其最新状态的对象

  2. 为什么新会话允许我们这样做。 首先 code> Book bako = Book.findById(4) findById应该在少数情况下使用,参考 Book.get(1L)Book.load(1L)Book。阅读(1L)



    当您可以运行 .get时,您正在启动查询以查找id



    实际值起诉



    经过多番讨论,无论服务是多少交易。如果您决定手动更新数据库。你会打破休眠缓存。您可以尝试禁用 - 首先/ second级高速缓存。首先,如果你在你的域类映射中声明了缓存。



    这是非常不明智的,会对应用程序产生影响。现实是交易服务应该为你做更新。如果您需要手动更新数据库。停止应用程序更新/启动应用程序。这真的很简单



    我之所以试图用示例项目来试图推断这种情况,有一个原因。



    为什么?
    因为它有助于回答任何猜测。我已将您的示例项目的副本添加到演示中并添加了一些实际记录更新。
    https://github.com/vahidhedayati/grails-transactions



    我也对您的版本提出了拉取请求,因此您可以合并并在本地进行测试。



    基本上 flush:true 不需要。 .get(id)不是必需的。



    正如您从方法1中的.save()之后服务中的结果中看到的那样,结果已更新。在控制器中,它在服务返回后使用方法()返回了正确的结果。

       -  transactions.Book:1 1添加了
    |服务器运行。浏览到http:// localhost:8080 / transactions
    2016-09-05 18:12:48,520 [http-bio-8080-exec-4] DEBUG hibernate.SQL - 选择book0_.id作为id1_0_0_,book0_。 version2_0_0_,book0_.title as title3_0_0_ from book book0_ where book0_.id =?
    --method1:更新前:------------------------------> TITLE_SET_BY_BOOTSTRAP
    - 获得之前的方法1:--------------------------------->从method1获得新标题
    method1获取后:---------------------------------->方法1新标题
    2016-09-05 18:12:48,618 [http-bio-8080-exec-4] DEBUG hibernate.SQL - 更新书集版本=?,title =?其中id =?和版本=?
    service1调用后1方法1的新标题
    --method2更新:----------------------------- - >方法1的新标题
    2016-09-05 18:12:48,687 [http-bio-8080-exec-4] DEBUG hibernate.SQL - 更新书集版本=?,title =?其中id =?和版本=?
    --method2获得之前:-------------------------->从method2获得新标题
    method2获得后:---------------------------->来自method2的新标题
    服务调用后2来自method2的新标题
    - 更新前的方法3:------------------------ --->从method2新标题
    2016-09-05 18:12:48,795 [http-bio-8080-exec-4] DEBUG hibernate.SQL - 更新书集版本=?,title =?其中id =?和版本=?
    --method3更新之前获取:------------------------->从method3获得新标题
    --method3获得后:-----------------------------------> ;来自method3的新标题
    服务电话之后3来自method3的新标题

    查看用户问题并且了解到他们手动更新数据库记录,然后期待屏幕显示相同的结果。



    总之,如果您在应用程序中没有启用缓存,那么是的应该都可以工作。如果您启用了某种形式的Hibernate缓存或ehcache,那么很可能您会查看某个缓存对象。我曾建议重新启动应用程序以确保您拥有最新版本。但如果你只是简单地说:

    换行

      DomainClass.withNewTransaction { 
    //获取最新副本
    DomainClass clazz = DomainClass.get(recordId)
    println - $ {clazz.value}
    }

    这应该可以确保您从数据库中获得最新的信息,但这不会提高速度,但如果您期待手动数据库更新,你总是可以确保最新的是高于..


    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.

    解决方案

    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..

    这篇关于Grails交易和会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆