org.hibernate.StaleObjectStateException:行已被另一个事务更新或删除(或未保存的值映射不正确)[com#117591]

时间:2016-04-01 05:00:53

标签: hibernate grails

def saveProcessDetail = {

    //def employeeLoanInstance = EmployeeLoan?.findById(params?.long("id"),[lock: true])
    //lock(params?.long("id"))
    def employeeLoanInstance = EmployeeLoan?.get(params?.long("id"))
    if(employeeLoanInstance){
        def instance
        if(params?.modeOfPayment){
            if(params.modeOfPayment == 'DD'){
                params.ddDate = params.ddDate ? DateUtility?.parseDate(params.ddDate) : params.ddDate
                instance = new Dd()
            }
            if(params.modeOfPayment == 'Cheque'){
                params.chequeDate = params.chequeDate ? DateUtility?.parseDate(params.chequeDate) : params.chequeDate
                instance = new Cheque()
            }
            if(params.modeOfPayment == 'Cash'){
                params.paidDate = params.paidDate ? DateUtility?.parseDate(params.paidDate) : params.paidDate
                instance = new Cash()
            }
            instance?.properties = params
        }

        params.deductionDate = params.deductionDate ? DateUtility?.parseLoanDate(params.deductionDate) : params.deductionDate
        params.sanctionedDate = params.sanctionedDate ? DateUtility?.parseDate(params.sanctionedDate) : params.sanctionedDate

        //println " test : "+DateUtility.getFormattedMonthAndYear(params.deductionDate)
        //println " test zx : "+DateUtility.getFormattedMonthAndYear(params.sanctionedDate)
        def formattedDeductionDate = DateUtility.getFormattedMonthAndYear(params.deductionDate)
        def formattedSanctionedDate = DateUtility.getFormattedMonthAndYear(params.sanctionedDate)


        //println " less : "+(formattedDeductionDate < formattedSanctionedDate)

        //println " = : "+(formattedDeductionDate == formattedSanctionedDate)


        //println " greater : "+(formattedDeductionDate > formattedSanctionedDate)

    //  formattedDeductionDate.before(formattedSanctionedDate) && !(formattedDeductionDate.equals(formattedSanctionedDate))


        employeeLoanInstance?.properties = params
        instance?.validate()
        if(params.modeOfPayment == 'Cash' && employeeLoanInstance?.sanctionedAmount && instance?.cashAmount && (instance?.cashAmount != employeeLoanInstance?.sanctionedAmount))
            instance.errors.rejectValue ("cashAmount", "cash.cashAmount.invalid.message", [
                message(code : 'cash.cashAmount.label', default : 'Amount')]
            as Object[], message(code : 'cash.cashAmount.invalid.message'))
        if(!instance?.hasErrors())
            employeeLoanInstance.loanId = employeeLoanInstance.loanId ? employeeLoanInstance.loanId : loanService?.getLoanId(employeeLoanInstance?.employee,employeeLoanInstance?.createdBranch)
        employeeLoanInstance?.validate()
        /* Emi Amount Validation */
        if(employeeLoanInstance?.noOfInstallments && employeeLoanInstance?.emiAmount && employeeLoanInstance?.sanctionedAmount){
            def amt = (employeeLoanInstance?.noOfInstallments - 1) * (employeeLoanInstance?.emiAmount)
            def sanctionedAmt =  (employeeLoanInstance?.sanctionedAmount - amt)
            if(amt > employeeLoanInstance?.sanctionedAmount)
                employeeLoanInstance.errors.rejectValue ("emiAmount", "employeeLoan.emiAmount.invalid.message", [
                    message(code : 'employeeLoan.emiAmount.label', default : 'Emi Amount')]
                as Object[], message(code : 'employeeLoan.emiAmount.invalid.message'))
            if(sanctionedAmt > employeeLoanInstance?.emiAmount)
                employeeLoanInstance.errors.rejectValue ("emiAmount", "employeeLoan.emiAmount.invalid.message", [
                    message(code : 'employeeLoan.emiAmount.label', default : 'Emi Amount')]
                as Object[], message(code : 'employeeLoan.emiAmount.invalid.message'))
        }
        /* End */
        if(!(employeeLoanInstance?.hasErrors()) && !(instance?.hasErrors())){
            instance?.save(flush : true)
            employeeLoanInstance.paymentId = instance?.id
            employeeLoanInstance.outstandingAmount = employeeLoanInstance?.sanctionedAmount
            employeeLoanInstance?.save(flush : true)
            println"employeeLoanInstance?.noOfInstallments"+employeeLoanInstance?.noOfInstallments
            println "-------loan instance----------->"+employeeLoanInstance?.errors
            /* For saving EmiAmount Details */
            def emiMonth = params.deductionDate
            def amt = 0
            if(employeeLoanInstance?.noOfInstallments){
            for(int i = 0; i < Integer?.valueOf(employeeLoanInstance?.noOfInstallments); i++){
                amt = employeeLoanInstance?.emiAmount
                if(i > 0){
                    Calendar calendar = GregorianCalendar.getInstance()
                    Integer year = emiMonth?.year+1900
                    Integer month = (emiMonth?.month) + 1
                    Integer date = emiMonth.getAt(Calendar.DAY_OF_MONTH)
                    //calendar.set (year, month, date)
                    calendar.set(year, month, date, 0, 0, 0)
                    emiMonth = calendar?.getTime()
                }
                if(i == (employeeLoanInstance?.noOfInstallments - 1)){
                    amt = employeeLoanInstance?.sanctionedAmount - (employeeLoanInstance?.emiAmount * (employeeLoanInstance?.noOfInstallments - 1))
                }
                def emiAmountInstance = new EMIAmountDetails()
                emiAmountInstance?.emiMonth  = emiMonth
                emiAmountInstance?.emiAmount = amt
                emiAmountInstance?.loanId = employeeLoanInstance.loanId
                auditService.beforeSave(emiAmountInstance)
                emiAmountInstance?.save(flush : true)
            }
            }
            /* End */

            flash.message = "${message(code : 'loanApproval.created.message', args : [employeeLoanInstance?.loanType,loanService?.getName(employeeLoanInstance.employee.empPersonalDetails),message(code : 'employeeLoan.loanStatus.'+employeeLoanInstance?.loanStatus)])}"
            redirect(action : "list")
        }
        else{
            render(template : "processLoan", model : [screenName : params?.screenName,instance:instance,employeeLoanInstance : employeeLoanInstance, methodName : params?.methodName])
        }
    }
    else{
        flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'employeeLoan.label', default: 'EmployeeLoan'), params.id])}"
        redirect(action : "list")
    }
}

注意:我无法通过各种场景在本地重现此问题。但是当我查看上面的生产服务器日志文件时,问题反复发生。我将其作为同步的代码块或者用于事务的服务层management.line实例?.save(flush:true)导致问题的确切...请让你的想法继续前进

1 个答案:

答案 0 :(得分:1)

您绝对应该将数据库访问权限转移到事务性服务中。但这并不一定能阻止这个问题。此外,同步绝对不是这里的方式

这是乐观的锁定失败。 (见https://grails.github.io/grails-doc/latest/guide/GORM.html#locking

根据您提供的信息,我无法提供明确的答案,但根据我之前看到的情况,我怀疑您有双击问题:用户之前第二次触发操作第一个动作完成。第一个操作完成并更新受影响对象的版本。当第二个操作写入数据库时​​,它会失败,因为对象的版本自加载对象后已更新。

为了帮助验证是否存在问题,您应该将日志记录添加到生产系统,以便您可以查看此方法何时启动以及ID是什么。如果您看到同一对象的两个(或更多)日志条目具有几乎相同的时间戳,那么这就是您的问题。

要解决此问题,请修改网页以阻止双击或修改代码以接受StaleObjectStateException。