您误解了对象实际持久保存到数据库的时间。当您调用时,对象不会被持久化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
}
}