我已经搜索过了,但仍然无法找出我做错了什么。在调用save()
之后,域对象id
是null
。
我读过,如果保存对象时出现问题,就会发生这种情况,如果是这种情况,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时:真正的
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
正如您所看到的,创建对象没有任何错误,并且我应该能够在调用save()
之后获得对象的id
。有什么想法吗?
感谢
您误解了对象实际持久化到数据库的时间。当您调用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
}
}
,并且该save(flush:true)应该抛出错误
这不是真的。save(failOnError: true)
将导致引发异常。
您的代码没有任何问题,您看到的行为也没有任何问题。通过不调用flush;save()
,您正试图在实际插入发生之前访问生成的ID。这就是它为空的原因。
然而,强制刷新会(有时)强制hibernate进行写入,从而为您提供所需的ID。如果您在呼叫save()
后立即需要ID,请使用save(flush: true)
。这没什么错。