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",我预计:
- bookService.method()中的findById(4)都是读取同一个值,毕竟是在同一个事务中隔离执行的,如果那个事务是那个状态读取的,第二个也应该读一读。
- 在控制器中执行的 findById(4) 已在手动更新后看到 object 的新状态。毕竟,第一个事务已经提交,第二个查找,即使在服务之外完成,也应该创建一个新事务。
但是,输出将始终是 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
谁能解释一下原因:
- 仅仅在某个状态下读取 object 的事务之外不足以读取新数据;
- 即使强制执行新事务也不足以读取 object 的最新状态;
- 为什么新 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}"
}
这应该确保您从数据库中获得最新的,它不会提高速度,但如果您希望手动更新数据库,您始终可以确保最新的在上面..
假设您在 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",我预计:
- bookService.method()中的findById(4)都是读取同一个值,毕竟是在同一个事务中隔离执行的,如果那个事务是那个状态读取的,第二个也应该读一读。
- 在控制器中执行的 findById(4) 已在手动更新后看到 object 的新状态。毕竟,第一个事务已经提交,第二个查找,即使在服务之外完成,也应该创建一个新事务。
但是,输出将始终是 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
谁能解释一下原因:
- 仅仅在某个状态下读取 object 的事务之外不足以读取新数据;
- 即使强制执行新事务也不足以读取 object 的最新状态;
- 为什么新 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}"
}
这应该确保您从数据库中获得最新的,它不会提高速度,但如果您希望手动更新数据库,您始终可以确保最新的在上面..