Grails 事务和 Session

Grails Transactions and the Session

假设您在 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);

        // MANUAL UPDATE OF DB HAPPENS HERE

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

并且您的数据库确实有一本 ID 为 4 的书,名为 "The Lord of The Rings"。

如果您随后在所有 System.out.println() 上设置断点,并且在执行第一个 findById 后,您手动将 object 的标题编辑为 "Harry Potter and the Goblet of Fire",我预计:

但是,输出将始终是 object 与开始时相同的状态。

更新:输出为:

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

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

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

}

结果还是一样

更新:输出为:

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

只有修改为:

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

}

接下来的行为是否正确。

更新:输出为:

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

谁能解释一下原因:

  1. 仅仅在某个状态下读取 object 的事务之外不足以读取新数据;
  2. 即使强制执行新事务也不足以读取 object 的最新状态;
  3. 为什么新 session 允许我们这样做。

首先Book bako = Book.findById(4) findById 应该在极少数情况下使用参考Book.get(1L) Book.load(1L) Book.read(1L)

您正在启动查询以查找 ID,而您本来可以 运行 .get

实际问题

说多了,再多的服务也是事务性的。如果您决定使用 mysql 手动更新数据库。您将破坏休眠缓存。您可以尝试禁用 - 首先 / second level cache。首先是如果您在域 class 映射中声明了缓存。

这真的很不明智,会影响应用。事实上,交易服务应该为您进行更新。如果需要手动更新数据库。停止应用程序更新/启动应用程序。真的就这么简单

我一直试图通过一个示例项目来推动您尝试此场景,这是有原因的。

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

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

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

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

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

在审查了很久以前的用户问题后,了解到他们正在手动更新数据库记录,然后期望屏幕显示相同的结果。

简而言之,如果您没有在应用程序中启用缓存,那么是的,它应该可以正常工作。如果您启用了某种形式的 Hibernate 缓存或 ehcache,那么您很可能会查看一些缓存对象。我曾建议重新启动应用程序以确保您拥有最新的。但如果你只是:

包装一个

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

这应该确保您从数据库中获得最新的,它不会提高速度,但如果您希望手动更新数据库,您始终可以确保最新的在上面..