7

我已经对此进行了搜索,但仍然无法弄清楚我做错了什么。调用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"

输出:

no errors. New id: null

并且在使用 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"

输出:

no errors. New id: 17

如您所见,创建对象没有任何错误,我应该能够id在调用save(). 有任何想法吗?

谢谢

4

2 回答 2

7

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

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

可以使用显式启动事务

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

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

class MyService {

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

如果您没有显式或隐式使用事务,则保存的对象会在 Hibernate 会话关闭时(大致)在 HTTP 请求完成时持久化。

但是,如果您调用someObject.save(flush: true)您是在告诉 Hibernate 立即持久化该对象,这就是为什么

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

Picture为实例分配一个 ID ,但是

album.addToPictures(pic).save()

仅在封闭会话/事务关闭/提交时分配 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

更新 2

根据您的评论

一旦确定对象已成功保存,我就不想保存文件,但相反,我想在文件成功保存后保存对象。因此,我将把我的问题重新表述为“有没有办法配置 Grails,以便在对象有效保存在数据库中之前,我可以知道将分配给新对象的 id?”

你已经知道了

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

将为您提供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
  }
}
于 2012-11-28T00:55:16.593 回答
2

那 save(flush:true) 应该抛出一个错误

这不是真的。 save(failOnError: true)将导致抛出异常。

您的代码没有任何问题,您看到的行为也没有任何问题。通过不调用flush;save(),您试图在实际插入发生之前访问生成的 ID。这就是为什么它是空的。

但是,强制刷新将(有时)强制休眠写入,然后为您提供您期望的 ID。如果您在呼叫后立即需要 ID save(),请使用save(flush: true). 没有什么不妥。

于 2012-11-28T00:44:46.210 回答