Grails Gorm:Object引用未保存的瞬态实例

时间:2011-01-26 22:04:34

标签: grails gorm

在Grails中保存Trip的实例时出现以下异常:

  

2011-01-26 22:37:42,801 [http-8090-5]   错误errors.GrailsExceptionResolver    - 对象引用未保存的瞬态实例 - 保存   冲洗前的瞬态实例:划船者   org.hibernate.TransientObjectException:   对象引用未保存的瞬态   instance - 保存瞬态实例   冲洗前:划船者

概念很简单:对于船只,你需要一些划船者,舵手(也是划船者)和船只:

旅行看起来像(缩短):

class Trip {
    Boat boat;
    Rower coxwain;

    static belongsTo = [Rower,Boat]
    static hasMany = [rowers:Rower]
}

和Rower(缩短)

class Rower { 
    String firstname;
    String name;
    Rower reference;

    static hasMany = [trips:Trip];
    static mappedBy = [trips:"rowers"]
}

然后行程保存在控制器中,如:

def save = {
        def trip = new Trip(params)

        // adding Rowers to Trip
        if (params.rower instanceof String) {
            def r = Rower.get(params?.rower)

            if (r != null) {
                trip.addToRowers(r)
            }
        } else {
            params?.rower?.each{
                rowerid ->
                def r = Rower.get(rowerid)
                log.info("rowerid (asList): " + rowerid)
                if (r != null) {
                    trip.addToRowers(r)
                }
            }
        } 

        // saving the new Trip -> EXCEPTION IN NEXT LINE
        if(!trip.hasErrors() && trip.save(flush:true)) {
          // ...
        }
        // ...
}

我认为我已经将域名之间的关系设置为正确。 在将Rower添加到Trip中时,Rower不会更改。为什么Grails希望它保存?为什么它是一个短暂的实例?

7 个答案:

答案 0 :(得分:7)

不幸的是,这是GORM处理事物的方式的问题,或者更具体地说是它期望您处理瞬态的方式。如果您不首先将包含的类保留在数据库中(在本例中为Rowers),则每次都会得到此异常。

使用GORM,您必须以自下而上的方式保存和附加,或者当grails用于刷新下一个实例的连接时,您将获得瞬态实例异常。该实例是“瞬态的”,因为它只是一个内存引用。要保留父级,GORM需要将父级链接到数据库中的子级。如果孩子没有坚持下去,就无法做到这一点,这就是异常的来源。

希望有更好的消息。并不是很难,但它会因复杂的层次结构而变得烦人。

答案 1 :(得分:1)

问题在某种程度上是不同的。 它在这里:

def trip = new Trip(params)

引用了一个未设置的coxwain(类Rower)(返回id = -1)。这构造了一个新的Rower而不是'null'值。这是'未保存的瞬态实例'。 如果我首先检查有效实例,那么它可以工作: - )

感谢您的帮助!

答案 2 :(得分:1)

对于任何处理具有相同名称的单个或多个参数的人来说,只需快速注释,使用params.list("parameterName")帮助程序,您始终可以返回列表

    ...

    // adding Rowers to Trip
    if (params.rower instanceof String) {
        def r = Rower.get(params?.rower)

        if (r != null) {
            trip.addToRowers(r)
        }
    } else {
        params?.rower?.each{
            rowerid ->
            def r = Rower.get(rowerid)
            log.info("rowerid (asList): " + rowerid)
            if (r != null) {
                trip.addToRowers(r)
            }
        }
    } 

    ...

可能会变得有点时髦

    ...

    // adding Rowers to Trip
    for(rower in params.list("rower") {
        def r = Rower.get(rower) 
        if(r) trip.addToRowers(r)
    } 

    ...

你可以在6.1.12 Simple Type Converters

下找到它

答案 3 :(得分:0)

我认为您必须先保存行程,然后再将行驶者添加到行程中。 在验证和/或保存之前检查行程是否有错也没有意义。

试试这个:

if(trip.validate() && trip.save(flush:true)) {
    if (r != null) {
       trip.addToRowers(r)
    }  
}

答案 4 :(得分:0)

首先,我认为这与级联保存和belongsTo有关,如The Grails Reference第5.2.1.3节和Gorm Gotchas part 2中所述。但是,由于Rowers已经在DB中,我认为它应该可行。域模型对我来说很复杂,您需要做的是简化它并使用Grails控制台运行一些测试(在项目目录中运行grails控制台)。首先在Trip和Rower之间创建一个基本的多对多,并让它执行所需的代码。然后逐位添加其他部分,如Rower对自身的引用。我不确定mappedBy部分是否必要。

答案 5 :(得分:0)

在许多使用案例中,您应该可以通过将cascade设置应用于您的收藏集来解决此问题:

static mapping = {
    rowers cascade: 'all-delete-orphan'
}

https://docs.grails.org/latest/ref/Database%20Mapping/cascade.html

答案 6 :(得分:0)

假设您使用Trip与Rower有很多关系

class Trip {
      static hasMany = [rowers:Rower]
      ...
    }

class Rower{
      static belongsTo =[trips:Trip]
      ...
   }

实际上,它正在寻找新的会议。所以我们需要停止/回滚当前事务,然后这个错误不会引发,为此 试试这个,

  def row = new Row()
  row.save()

注意:此保存不会影响您的数据库。它只是用于回滚事务。