在呈现具有多对多关系的复杂表单时,休眠未保存的瞬态错误

时间:2011-07-13 02:08:46

标签: hibernate grails gorm

基本问题:我与一个名为ProductOffering的查找实体有很多关系(商店,产品)。在我有一个持久的StoreLocation并试图附加瞬态产品的情况下,hibernate抱怨即使我不想保存它。

问题:

  1. 为什么grails / gorm / Hibernate试图保存我的实体?
  2. 有没有更好的方法来解决这个问题?
  3. 我的域类看起来像这样:

    class StoreLocation {
        String name
        List offerings
        static hasMany = [offerings:ProductOffering]
        static constraints = {
        }
    }
    
    class Product {
        String name
        static hasMany = [offeredBy:ProductOffering]
        static constraints = {
            name(unique:true)
            offeredBy(nullable:true)
        }
    }
    
    class ProductOffering {
        static belongsTo = [product:Product, store:StoreLocation]
        static constraints = {
        }
    }
    

    我的StoreLocationController“编辑”操作看起来像这样。

    def edit = {
        //get the store we want to edit
        def storeLocation = StoreLocation.get(params.id)
        //create a transient product and add it to the store
        // by attaching it to a transient productOffering
        def product = new Product()
        def offering = new ProductOffering()
        offering.product = product
        storeLocation.addToOfferings(offering)
        //render the edit page
        render(view:"edit", model:[storeLocation:storeLocation])
    }
    

    假设我有一个ID = 1的storeLocation。我去编辑StoreLocation 本地主机:8080 / MyApp的/ storeLocation /编辑/ 1

    我的观点看起来并不重要。它可以是

    hello

    我收到以下错误。

    错误500:not-null属性引用null或transient值:ProductOffering.product;嵌套异常是org.hibernate.PropertyValueException:not-null属性引用null或transient值:ProductOffering.product

    堆栈跟踪没有多大帮助:

    org.springframework.dao.DataIntegrityViolationException:not-null属性引用空值或瞬态值

1 个答案:

答案 0 :(得分:3)

Grails在所有请求周围注册一个OpenSessionInView拦截器。这可确保Hibernate会话在每个请求的持续时间内打开,并在请求结束时刷新和关闭会话。这样可以避免延迟加载异常。

由于会话在请求结束时被刷新,因此任何脏持久化实例都会被推送到数据库,这就是这里发生的事情。您加载storeLocation实例并进行更改,以便刷新更改。您不需要保存ProductOffering,因为在storeLocation被刷新时会传递保存,但Product实例不会,因此您会收到此异常。

如果您不希望保留任何更改,那么简单修复就是从持久化上下文中删除已更改的实例 - 通过在呈现调用之前调用storeLocation.discard()来执行此操作。有关discard方法的信息,请参阅http://grails.org/doc/latest/ref/Domain%20Classes/discard.html