grails中的并发事务导致数据库失效状态异常

时间:2013-11-10 13:58:49

标签: hibernate grails gorm

以下是我在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)
                }
        }
    }

1 个答案:

答案 0 :(得分: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

你需要在阅读时锁定(这是悲观锁定),as documented

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

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

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