我已经搜索过这个,但仍然无法弄清楚我做错了什么。致电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: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()
。有什么想法吗?
由于
答案 0 :(得分:7)
您误解了对象实际持久存储到数据库的时间。当您调用obj.save()
时,对象不会被持久化,当首先发生以下任何一种情况时,它会被持久化:
可以使用
显式启动事务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
继续发表评论
一旦我确定该对象已成功保留,我不想保存文件,但相反,我希望在文件成功保存后保留该对象。所以,我将把我的问题重新表述为“有没有办法配置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
}
}
答案 1 :(得分:2)
并且save(flush:true)应该抛出错误
那不是真的。 save(failOnError: true)
将导致抛出异常。
您的代码没有任何问题,您所看到的行为也没有任何问题。不要叫同花; save()
,您尝试在实际插入发生之前访问生成的ID。这就是它为空的原因。
然而,强制刷新将(有时)强制hibernate写入,然后为您提供您期望的ID。如果您在致电save()
后立即需要该ID,请使用save(flush: true)
。这没什么不对。