hibernate - org.hibernate.StaleObjectStateException : Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) [com#117591]

标签 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")
    }
}

注意:我无法通过各种场景在本地重现此问题。但是当我查看生产服务器日志文件时,上述问题反复发生。我是否应该将该代码块同步为事务管理的服务层或其他服务层。 instance?.save(flush : true) 正是导致问题的原因..请给出你的想法继续前进

最佳答案

您绝对应该将您的数据库访问转移到事务服务中。不过,这不一定能防止这个问题。此外,同步绝对不是这里的方法

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

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

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

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

关于hibernate - org.hibernate.StaleObjectStateException : Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) [com#117591],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36348391/

相关文章:

java - 为什么 HQL-Query 中的参数替换失败但 native 类型成功?

grails - 在grails中设置一个ini文件

grails - 如何在Grails CRUD动态脚手架的 View 部分隐藏字段?

java - 为什么 Hibernate session 不反射(reflect)在应用程序外部所做的更改

java - 内存泄漏随着 T4CPreparedStatement 的增加

grails - Grails和ActiveMQ:如何管理DefaultMessageListenerContainer的连接

angularjs - AngularJs 组件内无法访问 Groovy 代码

grails - 为Grails功能测试禁用 Spring 安全性吗?

java - Hibernate 一对多到 JSON 给出 : 500 internal server error

sql - Grails 中的非平凡数据查询