>当我需要保存一个对象列表时,每个对象都应该保存在它自己的事务中(这样如果一个对象失败,它们不会全部失败),我这样做:
List<Book> books = createSomeBooks()
books.each { book ->
Book.withNewSession {
Book.withTransaction {TransactionStatus status ->
try {
book.save(failOnError: true)
} catch (ex) {
status.setRollbackOnly()
}
}
}
}
我使用Book.withNewSession
因为如果一本书保存失败并且事务回滚,会话将无效,这将阻止后续书籍保存。但是,这种方法存在一些问题:
- 有点啰嗦
- 将始终为每本书创建一个新会话,即使上一本书成功
有没有更好的方法?我想到的一种可能性是依赖注入休眠SessionFactory
并执行此操作
List<Book> books = createSomeBooks()
books.each { book ->
try {
Book.withTransaction {
book.save(failOnError: true)
}
} catch (ex) {
// use the sessionFactory to create a new session, but how....?
}
}
这应该可以做到:
List<Book> books = createSomeBooks()
books.each { book ->
Book.withNewTransaction {TransactionStatus status ->
try {
book.save(failOnError: true)
} catch (ex) {
status.setRollbackOnly()
}
}
}
如果回滚,会话不是无效的,只是被清除了。因此,任何访问从数据库读取的实体的尝试都会失败,但写入尚未持久化的实体就可以了。但是,您确实需要使用单独的事务来防止一个故障回滚所有内容,因此使用NewTransaction。
你能尝试先验证它们,然后保存所有通过的吗? 我不确定它的性能是否更高,但它可能会更干净一些。 像这样:
List<Book> books = createSomeBooks()
List<Book> validatedBooks = books.findAll { it.validate() }
validatedBooks*.save()
虽然我不确定.validate()
是否承诺保存不会因其他原因而失败,以及数据是否是独立的(即唯一约束会传递到下一本书也尝试保存)。
也许你可以使用时髦的元编程和grails动态域方法?
在引导中:
def grailsApplication
def init = {
List.metaClass.saveCollection = {
ApplicationContext context = (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT);
SessionFactory sf = context.getBean('sessionFactory')
Session hsession = sf.openSession()
def notSaved = []
delegate.each {
if(!it.trySave()) {
notSaved << it
hsession.close()
hsession = sf.openSession()
}
}
hsession.close()
return notSaved
}
grailsApplication.getArtefacts("Domain")*.clazz.each { clazz ->
def meta = clazz.metaClass
meta.trySave = {
def instance = delegate
def success = false
clazz.withTransaction { TransactionStatus status ->
try {
instance.save(failOnError: true) // ', flush: true' ?
success = true
} catch (ex) {
status.setRollbackOnly()
}
}
return success
}
}
}
然后:
class TheController {
def index() {
List<Book> books = createSomeBooks()
def notSaved = books.saveCollection()
books.retainAll { !notSaved.contains(it) }
println "SAVED: " + books
println "NOT SAVED: " + notSaved
}
}
当然,必须执行一些检查(例如,列表包含域类等)。您还可以将特定的参数传递给闭包,以使其更加灵活(例如 flush
、failOnError
、deepValidate
等)