Grails的.调用save后,Id为null

man*_*war 7 null grails flush save

我已经搜索过这个,但仍然无法弄清楚我做错了什么.打完电话后save()的域对象idnull.

我已经读过如果在保存对象时出现问题就会发生这种情况,如果是这样的话,save(flush:true)应该抛出错误,但事实并非如此.看看我的代码和输出:

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"
Run Code Online (Sandbox Code Playgroud)

输出:

no errors. New id: null
Run Code Online (Sandbox Code Playgroud)

当使用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"
Run Code Online (Sandbox Code Playgroud)

输出:

no errors. New id: 17
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,创建对象时没有任何错误,我应该能够id在调用之后获取对象save().有任何想法吗?

谢谢

Dón*_*nal 7

您误解了对象实际持久存储到数据库的时间.调用时obj.save(),对象不会被持久化,当先发生以下任何一种情况时,它会被持久化:

  • 提交调用save()的事务
  • 调用save()的Hibernate会话已关闭

可以使用显式启动事务

SomeDomainClass.withTransaction {
  // code in here runs within a transaction
}
Run Code Online (Sandbox Code Playgroud)

通常,每次调用服务方法时也会隐式启动事务

class MyService {

  void doSomething () {
    // code in here runs within a transaction
  }  
}
Run Code Online (Sandbox Code Playgroud)

如果您没有显式或隐式地使用事务,则在Hibernate会话关闭时保存的对象会被持久化,这大概是在HTTP请求完成时.

但是,如果你打电话,someObject.save(flush: true)你告诉Hibernate立即坚持对象,这就是原因

album.addToPictures(pic).save(flush: true)
Run Code Online (Sandbox Code Playgroud)

Picture实例分配ID ,但是

album.addToPictures(pic).save()
Run Code Online (Sandbox Code Playgroud)

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

更新

还有你的评论

问题是我想将id用作我需要保存的文件名的一部分.如果我在保存文件时出错,怎么办?我应该使用显式交易并将其回滚吗?

是的,使用显式事务,并在确定对象已成功保留后保存文件,如果持久性失败则回滚事务

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
Run Code Online (Sandbox Code Playgroud)

更新2

继续你的评论

一旦我确定该对象已成功保留,我不想保存文件,但相反,我希望在文件成功保存后保留该对象.所以,我打算将我的问题重新表述为"有没有办法配置Grails,这样我才能知道在对象被有效保存到数据库之前将被分配给新对象的id?"

你已经知道了

album.addToPictures(pic).save(flush:true)
Run Code Online (Sandbox Code Playgroud)

将为您提供Picture实例的ID ,因此如果您在事务中执行此操作,则可以在不实际提交事务的情况下获取ID.但是,我认为只有在使用使用序列的数据库(Oracle,Postgres)时才会有效.像下面这样的东西应该工作

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
  }
}
Run Code Online (Sandbox Code Playgroud)