Grails用于withTransaction和悲观锁定

时间:2015-01-26 12:11:18

标签: grails gorm

我正在使用Grails应用程序并使用控制台插件在运行时执行代码。并且在尝试保存对象时遇到异常HibernateOptimisticLockingFailureException。

然后谷歌搜索后知道使用了运作正常的交易。

Show.withTransaction {
   Show s=Show.get(1111) // any show id
   s.name=s.name+'1'
   s.save()
}

我也尝试过锁(悲观锁定),但没有工作并抛出乐观锁定的异常:

Show s=Show.lock(1111)
s.name=s.name+'1'
s.save(flush:true)

为什么悲观的锁定片段没有用?

更多详情:

class Content {

   User createdBy
   User lastUpdatedBy
   Date dateCreated
   Date lastUpdated

   static constraints = {
      createdBy nullable: true
      lastUpdatedBy nullable: true
   }

   static mapping = {
      tablePerHierarchy(false)
   }

}

class Show extends Content {

   String name
   ShowCategory category
   ShowType showType

   static hasMany = [images: Image]

   static constraints = {
      name blank: false, unique: true
      showType nullable: true
      images nullable: true, blank: true
      category nullable: true
   }

   static mapping = {
      table("some_table_show")
      images cascade: 'all-delete-orphan'
      name index: 'name_idx'
      images cache: true
   }

   def afterInsert() {
      Plug.cacheService.deleteCache() // some redis cache usage
   }

   def afterUpdate() {
      Plug.cacheService.deleteCache() // some redis cache usage
   }

}

Exception after using lock (pessimistic locking):
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [com.myApplication.Show] with identifier [1111]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.myApplication.Show#1111]
    at com.myApplication.cacheService.deleteCache(CacheService.groovy:286)
    at com.myApplication.Show.afterUpdate(Show.groovy:161)
    at org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener.onApplicationEvent(AbstractPersistenceEventListener.java:46)
    at Script1.run(Script1.groovy:17)
    at org.grails.plugins.console.ConsoleService.eval(ConsoleService.groovy:57)
    at org.grails.plugins.console.ConsoleService.eval(ConsoleService.groovy:37)
    at org.grails.plugins.console.ConsoleController$_closure2.doCall(ConsoleController.groovy:61)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:198)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.myApplication.Show#1111]
    ... 12 more

2 个答案:

答案 0 :(得分:2)

一般来说,withTransaction是一种黑客攻击,应该避免使用,主要是因为它允许您在代码库中执行交易代码而不是使用层并分离关注点。改为使用事务性服务,并根据哪些方法执行操作来注释类或单个方法。由于在withTransaction和事务服务中使用了相同的基础Spring功能,因此净效果是相同的。

如果没有关于代码外观的更具体信息以及您看到的错误消息,很难提供帮助。如果你可以重现它会有很大的帮助。

如果并发更新的可能性不高,您应该尝试使用乐观锁定,并在发生碰撞时处理偶然的​​冲突。当然需要使用悲观锁,但是使用它们会有偏执,因为它们会影响可伸缩性。正如我在另一个答案的评论中提到的,所有锁定必须在事务中完成,否则锁定将立即被释放。

使用悲观锁定时,它很可能会看到Hibernate乐观锁定错误/异常。如果你看一下SQL Hibernate为更新生成的内容,它通常就像update <tablename> set foo=?, bar=?, ... where id=? and version=?。 where子句是超定的 - 只要在那里有id就足够了,因为它既可以是对应于现有行的id,也可能不是。但它会在运行该SQL后查看JDBC更新计数,如果它为零,则version值必须已关闭,并且它假定这必须是由其他人编辑该文件引起的递增版本。

但奇怪的事情会影响版本。 Hibernate认为集合是一种属性,因此如果您将一个项目添加到集合中,该集合将增加所有者的版本。这可能是意外的,例如为作者添加新书将更新作者的版本。 Weirder仍在,添加一个新的多对多映射,例如,授予用户角色,版本角色和用户。这是因为用户的角色集合属性已更改,但角色的用户集合也已更改。这可能造成严重破坏,因为您不必锁定用户和角色以向用户授予角色,并且您无法锁定集合的元素。

答案 1 :(得分:-2)

悲观锁定依赖于数据库来实现锁定行为。我观察到它与MySQL和Microsoft SQL Server一样有效,但与Grails开发环境中使用的H2数据库无关。