Grails的。调用保存后Id为空 [英] Grails. Id is null after calling save

查看:133
本文介绍了Grails的。调用保存后Id为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经查过这个,但仍然无法弄清楚我做错了什么。在调用 save()后,域对象 id null



我读过它会在保存对象时发生问题,并且 save(flush:true)应该抛出一个错误,但事实并非如此。看看我的代码和输出:

  def pic = new图片(标题:'XX',路径:XXX) 
album.addToPictures(pic).save()
if(pic.validate())
printlnno errors。New id:+ pic.id
else


$ p
$ b $ p









$

 没有错误。 New id:null 

使用flush时:true

  def pic = new图片(标题:'XX',路径:XXX)
album.addToPictures(pic).save(flush:true)
if(pic.validate())
printlnno errors。New id:+ pic.id
else
printlnwith errors


 没有错误。新的ID:17 

正如您所看到的,创建对象时没有任何错误,应该可以在调用 save()后获得对象的 id 。任何想法?



谢谢

解决方案

您误解了时机当一个对象实际上被持久化到数据库时。调用 obj.save()时,对象不会持久保存,当以下任一情况首先发生时,该对象将持续保留:




  • 调用save()的事务被提交

  • 调用save()的Hibernate会话被关闭


  • 一个事务可以用

      SomeDomainClass .withTransaction {
    //此处的代码在事务中运行
    }

    通常,每次调用服务方法时都会隐式地启动一个事务。

      class MyService {

    void doSomething(){
    //此处的代码在事务中运行
    }
    }

    如果您没有明确或隐含地使用事务,则在Hibernate会话关闭时保存的对象会保留下来,这在HTTP请求完成时(大体上)是这样。



    然而,如果你打电话给 someObject.save(flush:true)你告诉Hibernate立即保存对象,这就是为什么

      album.addToPictures(pic).save(flush:true)

    一个ID到图片实例,但是

      album.addToPictures(pic ).save()

    只会在封闭会话/事务关闭/提交时分配ID

    更新



    进一步发表您的评论


    问题是我想使用该id作为我需要保存的文件名称的一部分。如果我在保存文件时遇到错误怎么办?我应该使用明确的交易并将其回滚?

    是的,使用一个明确的事务,并且在确定对象已成功保存后保存该文件,将事务回滚如果持续失败

      def pic = new图片(标题:'XX',路径:XXX)

    Picture.withTransaction {TransactionStatus状态 - >

    try {
    album.addToPictures(pic).save()

    } catch(ex){
    status.setRollbackOnly()
    throw ex



    //此时你可以确定pic已经被保存了,所以用pic.id来保存文件
    code>



    更新2



    更多评论


    一旦确定对象已成功保存,我不想保存该文件,但相反,我想在文件保存后保留该对象已成功保存。因此,我将重新描述我的问题:是否有一种方法可以配置Grails,以便在对象被有效保存到数据库之前知道将分配给新对象的id?


    您已经知道

      album.addToPictures (pic).save(flush:true)

    会为您提供 Picture 实例,所以如果你在一个事务中这样做,你可以得到这个ID而不需要实际提交事务。但是,我认为这只适用于使用使用序列的数据库(Oracle,Postgres)的情况。类似以下内容应该可以工作

      Picture.withTransaction {TransactionStatus status  - > 
    $ b $尝试{
    def pic = new图片(标题:'XX',路径:XXX)
    album.addToPictures(pic).save(flush:true)

    // pic.id现在应该被分配,所以保存文件。我假设一个
    //如果保存文件失败,会抛出一个异常

    } catch(ex){
    //你也可以试着回滚文件保存在这里,即删除它
    status.setRollbackOnly()
    throw ex
    }
    }


    I've already searched about this, but still cannot figure out what I'm doing wrong. After calling save() the domain object id is null.

    I've read it'll happen if there's a problem when saving the object, and that save(flush:true) should throw an error if that's the case, but it's not. Look at my code and the output:

    def pic = new Picture(title:'XX', path:"XXX")
    album.addToPictures(pic).save()
    if(pic.validate())
       println "no errors. New id: " + pic.id
    else
       println "with errors"
    

    Output:

    no errors. New id: null
    

    And when using flush:true

    def pic = new Picture(title:'XX', path:"XXX")
    album.addToPictures(pic).save(flush:true)
    if(pic.validate())
       println "no errors. New id: " + pic.id
    else
       println "with errors"
    

    Output:

    no errors. New id: 17
    

    As you can see, there aren't any errors creating the object, and I should be able to get the id of the object after just calling save(). Any ideas?

    Thanks

    解决方案

    You are misunderstanding the timing of when an object actually gets persisted to the database. An object does not get persisted when you call obj.save(), it gets persisted when whichever of the following happens first:

    • the transaction in which save() is called is committed
    • the Hibernate session in which save() is called is closed

    A transaction can be started explicitly with

    SomeDomainClass.withTransaction {
      // code in here runs within a transaction
    }
    

    Normally, a transaction is also implicitly started for each call to a service method

    class MyService {
    
      void doSomething () {
        // code in here runs within a transaction
      }  
    }
    

    If you don't explicitly or implicitly use transactions, saved objects get persisted when the Hibernate session closes, which is (roughly) when the HTTP request completes.

    However, if you call someObject.save(flush: true) you are telling Hibernate to persist the object immediately, which is why

    album.addToPictures(pic).save(flush: true)
    

    assigns an ID to the Picture instance, but

    album.addToPictures(pic).save()
    

    will only assign the ID when the enclosing session/transaction is closed/committed

    Update

    Futher to your comment

    The problem is that I want to use the id as part of the name of a file I need to save. What about if I get an error saving the file? Should I use a explicit transaction and roll it back?

    Yes, use an explicit transaction, and save the file once you're sure the object has been successfully persisted, roll the transaction back if persistence fails

    def pic = new Picture(title:'XX', path:"XXX")
    
    Picture.withTransaction { TransactionStatus status ->        
    
      try {
        album.addToPictures(pic).save()
    
      } catch(ex) {
        status.setRollbackOnly()
        throw ex
      }
    }
    
    // At this point you can be sure pic has been persisted, so use pic.id to save the file
    

    Update 2

    Further to your comment

    I don't want to save the file once I'm sure the object has been successfully persisted, but the opposite, I want to persist the object once the file has been successfully saved. So, I'm going to reformulate my questions as "Is there a way to configure Grails so that I can know the id that's going to be assigned to the new object before the object is effectively saved in the database?"

    You already know that

    album.addToPictures(pic).save(flush:true)
    

    will provide you with the ID of the Picture instance, so if you do this within a transaction you can get the ID without actually committing the transaction. However, I think this will only work if you're using a database that uses sequences (Oracle, Postgres). Something like the following should work

    Picture.withTransaction { TransactionStatus status ->        
    
      try {
        def pic = new Picture(title:'XX', path:"XXX")  
        album.addToPictures(pic).save(flush: true)
    
        // pic.id should now be assigned, so save the file. I'm assuming an
        // an exception will be thrown if saving the file fails
    
      } catch(ex) {
        // you may also want to try rolling back the file save here, i.e. delete it
        status.setRollbackOnly()
        throw ex
      }
    }
    

    这篇关于Grails的。调用保存后Id为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆