【问题标题】:Grails Gorm : Object references an unsaved transient instanceGrails Gorm:对象引用未保存的瞬态实例
【发布时间】:2011-01-26 22:04:34
【问题描述】:

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

2011-01-26 22:37:42,801 [http-8090-5] 错误错误。GrailsExceptionResolver - 对象引用了一个未保存的瞬态实例 - 保存 刷新前的瞬态实例:Rower org.hibernate.TransientObjectException: 对象引用了一个未保存的瞬态 instance - 保存瞬态实例 冲水前:赛艇

这个概念很简单:乘船旅行需要一些划船者、一名舵手(也是一名划船者)和一艘船:

行程看起来像(缩短):

class Trip {
    Boat boat;
    Rower coxwain;

    static belongsTo = [Rower,Boat]
    static hasMany = [rowers: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)) {
          // ...
        }
        // ...
}

我认为我已经正确设置了域之间的关系。 划船者在添加到行程时不会更改。为什么 Grails 要保存它?为什么是瞬态实例?

【问题讨论】:

  • 这是否发生在两个控制器块中(即 if 和 else)?
  • 您收到任何日志消息了吗?

标签: grails grails-orm


【解决方案1】:

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

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

希望有更好的消息。并不难,但复杂的层次结构会让人讨厌。

【讨论】:

    【解决方案2】:

    问题在某种程度上有所不同。 在这里:

    def trip = new Trip(params)
    

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

    感谢您的帮助!

    【讨论】:

      【解决方案3】:

      对于任何处理具有相同名称的单个或多个参数的人来说,使用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下找到它

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

          试试这个:

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

          【讨论】:

            【解决方案6】:

            在许多用例中,您应该能够通过将cascade 设置应用于您的集合来解决此问题:

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

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

            【讨论】:

              【解决方案7】:

              假设,你正在使用 Trip is hasMany 与 Rower 的关系

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

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

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

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

              【讨论】:

                【解决方案8】:

                我遇到了这个问题,但对我来说答案要简单得多——我的第一个对象没有正确保存,因为它没有成功验证。 我不知何故没有注意到第一个对象没有保存,所以这个错误让我很困惑。 在调试问题时,我最终注意到第一个对象没有正确保存,当我修复它时,对象引用了一个未保存的瞬态实例问题就消失了。

                【讨论】:

                  猜你喜欢
                  • 2010-12-20
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-06-02
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-11-08
                  相关资源
                  最近更新 更多