Программная обработка транзакций Grails
когда мне нужно сохранить список объектов, и каждый объект должен быть сохранен в его собственной транзакции (так что, если один терпит неудачу, они не все терпят неудачу), я делаю это так:
List<Book> books = createSomeBooks()
books.each { book ->
Book.withNewSession {
Book.withTransaction {TransactionStatus status ->
try {
book.save(failOnError: true)
} catch (ex) {
status.setRollbackOnly()
}
}
}
}
Я использую Book.withNewSession
потому что, если одна книга не удается сохранить и транзакция откатывается, сеанс будет недействительным, что предотвратит сохранение последующих книг. Тем не менее, есть несколько проблем с этим подходом:
- это немного многословно
- новый сеанс всегда будет создаваться для каждой книги, даже если предыдущая книга удалась
есть ли лучший способ? Одна из возможностей, которая пришла мне в голову, - это зависимость-ввести Hibernate 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....?
}
}
3 ответов
Это должно сделать это:
List<Book> books = createSomeBooks()
books.each { book ->
Book.withNewTransaction {TransactionStatus status ->
try {
book.save(failOnError: true)
} catch (ex) {
status.setRollbackOnly()
}
}
}
сеанс не является недопустимым, если вы откат, он просто очищается. Таким образом, любые попытки доступа к объектам, считанным из БД, потерпят неудачу, но записи еще не сохраненных объектов будут просто прекрасны. Но вам нужно использовать отдельные транзакции, чтобы удержать один сбой от отката всего, следовательно, withNewTransaction.
не могли бы вы сначала проверить их, а затем сохранить все те, которые прошли? Я не уверен, что это более эффективно, но это может быть немного чище. Что-то вроде:
List<Book> books = createSomeBooks()
List<Book> validatedBooks = books.findAll { it.validate() }
validatedBooks*.save()
хотя я не уверен, если .validate()
обещает, что сохранение не завершится неудачей по другим причинам, и если данные независимы (т. е. уникальное ограничение проходит, пока следующая книга не попытается сохранить).
может быть, вы могли бы использовать методы динамического домена groovy meta-programming & grails?
В Bootstrap:
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
, etc.)