2

以下是我在 api 调用上运行的代码。下面的代码在 grails 服务中,默认情况下是事务性的。但即使在锁定行之后,我也会收到这个错误:Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)。数据库中只有一个计划,所以 for 循环只运行一次。但是对 api 的并发调用会导致错误。请帮我解决这个问题

def updateAllPlans(def user,def percentComplete,def chapterId){
        def plans = Plan.findAllWhere(user:user)
        def chapter = Chapter.findById(chapterId)
        for(def plan:plans){
                def foundChapters = plan.psubject.ptopics.psubtopics.pchapters.chapter.flatten().contains(chapter)
                if(foundChapters){
                    plan.lock()
                    if(percentComplete=='0'){
                        plan.durationViewComplete = plan.durationViewComplete.plusMillis(chapter.duration.getMillisOfSecond())
                        plan.durationViewComplete = plan.durationViewComplete.plusSeconds(chapter.duration.getSecondOfMinute())
                        plan.durationViewComplete = plan.durationViewComplete.plusMinutes(chapter.duration.getMinuteOfHour())
                        plan.durationViewComplete = plan.durationViewComplete.plusHours(chapter.duration.getHourOfDay())
                    }else{
                        plan.durationComplete = plan.durationComplete.plusMillis(chapter.duration.getMillisOfSecond())
                        plan.durationComplete = plan.durationComplete.plusSeconds(chapter.duration.getSecondOfMinute())
                        plan.durationComplete = plan.durationComplete.plusMinutes(chapter.duration.getMinuteOfHour())
                        plan.durationComplete = plan.durationComplete.plusHours(chapter.duration.getHourOfDay())
                    }

                    plan.save(flush:true, failOnError:true)
                }
        }
    }
4

1 回答 1

3

您只有在阅读计划后才锁定计划。所以多个线程可以同时读取计划。然后一个线程锁定它并更新它,另一个线程锁定它并更新它。但他们都同时读取它,所以都读取相同的数据,内部具有相同的版本:

thread 1: read plan, with version = 3
thread 2: read plan, with version = 3
thread 1: lock plan
thread 1 : update and save plan. version becomes 4
thread 2 : lock plan
thread 2 : update and save plan. version in memory is 3, but version in database is 4, so an exception is thrown

阅读时需要锁定(这是悲观锁定),如文档所述:

def airport = Airport.findByName("Heathrow", [lock: true])

然后第二个线程必须等待第一个线程保存并提交其事务,然后才能读取相同的计划。

但是,这具有悲观锁定的所有缺点:吞吐量可能会降低,因为一次只有一个事务可以使用该计划,而这正是乐观锁定试图避免的。代价是您可以获得异常并且必须处理它们(通过重试、显示错误消息或根据情况提供任何最佳答案)

于 2013-11-10T15:11:16.623 回答