Grails事务和会话

时间:2016-09-02 16:07:30

标签: grails gorm

想象一下,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);
    }
}

并且你的数据库确实有一本名为4的书叫做#34;指环王"。

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

  • bookService.method()中的findById(4)读取相同的值,毕竟,它们是在同一个事务中独立执行的,如果该事务在该状态下读取它,那么第二个也应该读取它
  • 在手动更新后,控制器中执行的findById(4)已经看到了对象的新状态。毕竟,第一个事务已经提交,第二个事务发现,即使在服务之外完成,也应该创建一个新事务。

但是,输出将始终是处于开头状态的对象。

更新:输出为:

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. 只是在读取某个状态的对象的事务之外,不足以读取新数据;
  2. 即使强制新事务也不足以读取具有最新状态的对象;
  3. 为什么新会议允许我们这样做。

1 个答案:

答案 0 :(得分:1)

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

当您刚刚运行.get

时,您正在启动查询以查找ID

实际问题

经过多次讨论,无论服务多么具有交易性。如果您决定手动使用mysql更新数据库。你将破坏hibernate缓存。您可以尝试停用 - 首先/ second level cache。首先,如果您在域类映射中声明了缓存。

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

有一个原因让我试图让你沿着尝试使用示例项目尝试此场景的路线。

为什么? 因为它有助于回答任何猜测。我已经获取了您的示例项目的副本,并在演示中添加了一些实际的记录更新。 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

在审核了用户问题的年龄,并了解到他们手动更新了DB记录,然后期望屏幕显示相同的结果。

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

换一个

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

这应该确保您从数据库中获取最新信息,它不会提高速度,但是如果您期望手动数据库更新,您可以始终确保最新的更新...