【问题标题】:Grails unique compound key issuesGrails 独特的复合键问题
【发布时间】:2015-08-07 23:57:14
【问题描述】:

给定一个 GORM 类:

    class PriceSheet {

    Client client
    Population population
    Product product
    RevenueModelType modelType

    BigDecimal price

    static constraints = {
        client(unique: ['population', 'product', 'modelType'])
    }
}

我希望仅当客户、人口、产品和模型类型唯一时才保存/更新 PriceSheet。 (客户、人口、产品和模型类型的组合应该只有一个价格表项。

正在 mySQL 中创建密钥。

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

    priceSheetInstance.validate()

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

    priceSheetInstance.save flush:true

有什么想法或建议吗?我在验证后将调试器置于断点上,发现错误为空。

Grails 2.3.10

【问题讨论】:

    标签: grails grails-orm


    【解决方案1】:

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

    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 复合键的更多信息here

    【讨论】:

    • 感谢您的选择,但我不想让主键成为复合键。根据文档,我应该能够在尝试使用唯一验证器时做到这一点
    • 是的,但您必须牢记这一点(直接来自文档):唯一性验证有可能(尽管在实践中不太可能)通过但随后的保存失败。如果在 Grails 检查和实例的实际保存之间发生另一个保存或更新来更新数据库,则调用将失败。防止这种情况的唯一方法是使用 SERIALIZABLE 隔离级别的事务,但这对性能非常不利。你最好调用 save() 然后检查错误。
    【解决方案2】:

    最终解决方案是在控制器中包装 withNewSession 以进行保存:

        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] }
            }
        }
    

    我不必为更新包装 withNewSession...事实上,为更新包装 withNewSession 会导致 StaleObjectExceptions

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 1970-01-01
      • 2017-07-24
      • 1970-01-01
      相关资源
      最近更新 更多