Grails独特的复合关键问题

Grails独特的复合关键问题,grails,gorm,Grails,Gorm,给定一个GORM类: class PriceSheet { Client client Population population Product product RevenueModelType modelType BigDecimal price static constraints = { client(unique: ['population', 'product', 'modelType']) } }

给定一个GORM类:

    class PriceSheet {

    Client client
    Population population
    Product product
    RevenueModelType modelType

    BigDecimal price

    static constraints = {
        client(unique: ['population', 'product', 'modelType'])
    }
}
我希望只有在客户、总体、产品和型号都是唯一的情况下,才能保存/更新价格表。(对于客户、总体、产品和型号组合,应该只有一个价格表项目)

密钥正在mySQL中创建

我的问题是grails验证通过了,但保存失败

    priceSheetInstance.validate()

    if (priceSheetInstance.hasErrors()) {
        respond priceSheetInstance.errors, view:'create'
        return
    }

    priceSheetInstance.save flush:true
有什么想法或建议吗?我在验证后将调试器放在断点上,并看到错误为空


Grails 2.3.10

首先,您需要告诉GORM哪些属性构成了复合主键,然后您的域类需要实现可序列化。最终的结果是这样的:

import org.apache.commons.lang.builder.HashCodeBuilder

class PriceSheet implements Serializable {
    Client client
    Population population
    Product product
    RevenueModelType modelType

    BigDecimal price

    static constraints = {        
    }

    static mapping = {
        id composite: ['client', 'population', 'product', 'modelType']
    }

    boolean equals(other) {
        if(!(other instanceof PriceSheet)) return false
        other.client == client && other.population == population && other.product == product && other.modelType == modelType
    }

    int hashCode() {
        def builder = new HashCodeBuilder()

        builder.with {
            append client
            append population
            append product
            append modelType
        }

        builder.toHashCode()
    }    
}

您可以阅读有关GORM复合键的更多信息。

首先,您需要告诉GORM哪些属性构成了复合主键,然后您的域类需要实现可序列化。最终的结果是这样的:

import org.apache.commons.lang.builder.HashCodeBuilder

class PriceSheet implements Serializable {
    Client client
    Population population
    Product product
    RevenueModelType modelType

    BigDecimal price

    static constraints = {        
    }

    static mapping = {
        id composite: ['client', 'population', 'product', 'modelType']
    }

    boolean equals(other) {
        if(!(other instanceof PriceSheet)) return false
        other.client == client && other.population == population && other.product == product && other.modelType == modelType
    }

    int hashCode() {
        def builder = new HashCodeBuilder()

        builder.with {
            append client
            append population
            append product
            append modelType
        }

        builder.toHashCode()
    }    
}

您可以阅读有关GORM复合键的更多信息。

最终解决方案是在控制器中使用NewSession进行保存:

    PriceSheet.withNewSession {

        priceSheetInstance.validate()

        // needed to call save in order to check the unique compound key
        // validate alone wasn't working
        // also needed to wrap withNewSession above
        // PriceSheet GORM object implements Serializable as well
        // spent way more time than expected on this.... wrestled with Grails

        if (priceSheetInstance.hasErrors() || (priceSheetInstance.save(failOnError: true) && priceSheetInstance.hasErrors())) {
            respond priceSheetInstance.errors, view:'create'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: 'priceSheet.label', default: 'Price Sheet'), priceSheetInstance?.id])
                redirect (action: 'index')
            }
            '*' { respond priceSheetInstance, [status: CREATED] }
        }
    }

我不必用NewSession包装更新。。。事实上,使用NewSession包装更新会导致过时的对象异常

最终解决方案是在controller中使用NewSession包装保存:

    PriceSheet.withNewSession {

        priceSheetInstance.validate()

        // needed to call save in order to check the unique compound key
        // validate alone wasn't working
        // also needed to wrap withNewSession above
        // PriceSheet GORM object implements Serializable as well
        // spent way more time than expected on this.... wrestled with Grails

        if (priceSheetInstance.hasErrors() || (priceSheetInstance.save(failOnError: true) && priceSheetInstance.hasErrors())) {
            respond priceSheetInstance.errors, view:'create'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: 'priceSheet.label', default: 'Price Sheet'), priceSheetInstance?.id])
                redirect (action: 'index')
            }
            '*' { respond priceSheetInstance, [status: CREATED] }
        }
    }

我不必用NewSession包装更新。。。事实上,使用NewSession包装更新会导致StaleObjectExceptions

感谢您的选择,但我不希望主键是复合键。根据文档,我应该能够在尝试使用unique Validator时这样做是的,但您必须记住这一点(直接从文档中):唯一性验证可能会通过,但随后的保存会失败(尽管在实践中不太可能)。如果在Grails检查和实例的实际保存之间发生另一次保存或更新来更新数据库,则调用将失败。防止这种情况的唯一方法是在可序列化隔离级别使用事务,但这对性能非常不利。您最好先调用save()然后检查错误。谢谢您的选择,但我不希望主键是复合键。根据文档,我应该能够在尝试使用unique Validator时这样做是的,但您必须记住这一点(直接从文档中):唯一性验证可能会通过,但随后的保存会失败(尽管在实践中不太可能)。如果在Grails检查和实例的实际保存之间发生另一次保存或更新来更新数据库,则调用将失败。防止这种情况的唯一方法是在可序列化隔离级别使用事务,但这对性能非常不利。最好先调用save(),然后检查错误。