1

Grails (2.3.7) 中的并发问题似乎没有明确的解决方案。我已经尝试了所有建议,但是当我推送并发线程数时,以下代码总是失败:

package simpledb

import grails.transaction.Transactional
import groovy.transform.Synchronized
import org.apache.commons.logging.LogFactory

@Transactional
class OwnerService {
    private static final myLock1 = new Object()
    private static final myLock2 = new Object()

    @Synchronized('myLock1')
    static public saveOwner(def ownerName) {
        def ownerInstance = null
        Owner.withNewTransaction {
            ownerInstance = Owner.findOrCreateByName(ownerName)
            ownerInstance.save(failOnError: true, flush: true)
        }
        ownerInstance
    }

    @Synchronized('myLock2')
    static public associateDog(def ownerId, def dogId) {
        def lockedOwnerInstance
        Owner.withNewTransaction {
            lockedOwnerInstance = Owner.lock(ownerId)
            def lockedDogInstance = Dog.lock(dogId)
            lockedOwnerInstance.addToDogs(lockedDogInstance)
            lockedOwnerInstance.save(failOnError: true, flush: true)
        }
        lockedOwnerInstance
    }
}

它在“deflockedDogInstance = Dog.lock(dogId)”行上失败:

Error 500: Internal Server Error    

URI
      /simpledb/JsonSlurper/api
Class
      org.hibernate.StaleObjectStateException
Message
      Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [simpledb.Dog#111]

上面的设计非常简单,在 Owner 和 Dog 之间存在多对多关系:

狗类:

package simpledb

class Dog {
    String name
    Breed breed = null
    Integer age = null
    static hasMany = [owners: Owner]
    static belongsTo = Owner
    static mapping = { owners lazy: false }
    static constraints = {
        name blank: false, nullable: false, unique: true
        breed nullable: true
        age nullable: true
    }
}

业主等级:

package simpledb

class Owner {
    String name;
    static hasMany = [dogs: Dog]
    static mapping = { dogs lazy: false }
    static constraints = {
    }
}

仅供参考 - 数据库是 MySQL。

有什么建议吗?

4

2 回答 2

1

好的,这里有很多事情要做,我打赌你可以处理掉大部分。因此,与其尝试修复它,不如将其分解到最低限度并从那里开始:

  1. 您的服务方法不应该是静态的。
  2. 您的服务已经是事务性的,所以withNewTransaction()可以继续。你也不需要冲洗。
  3. 无需同步服务方法。
  4. 您不需要锁定它,Dog因为您没有更改它(添加它Owner.dogs只会在连接表中创建一条记录)。

通过这些更改,您的服务最终看起来像这样:

package simpledb

import grails.transaction.Transactional
import org.apache.commons.logging.LogFactory

@Transactional
class OwnerService {

    def saveOwner(def ownerName) {
        def ownerInstance = Owner.findOrCreateByName(ownerName)

        ownerInstance.save(failOnError: true)
        ownerInstance
    }

    def associateDog(def ownerId, def dogId) {
        def ownerInstance = Owner.lock(ownerId)
        def dogInstance = Dog.read(dogId)

        ownerInstance.addToDogs(dogInstance)
        ownerInstance.save(failOnError: true)
        ownerInstance
    }
}

看看这能带你走多远。您甚至可以删除所有者锁定。

于 2016-03-02T03:25:24.083 回答
0

除了@Emmanuel-Rosa 所说的之外,如果发生太多并发更新,您是否还可以确保在保存之前调用“刷新”(在所有者上)?(可重复读取方法)。

不过,加入表的添加不应该受到这些影响。只有当一些狗被试图“重新添加”给同一所有者时,它可能会导致问题。

另一种方法(不是在这种情况下,而是)如果只更新一两列,您可以使用纯 SQL。

于 2019-02-04T21:12:00.680 回答