以编程方式处理 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因为如果一本书保存失败并且事务回滚,会话将无效,这将阻止后续书籍保存。但是,这种方法存在一些问题:

  1. 有点啰嗦
  2. 将始终为每本书创建一个新会话,即使上一本书成功

有没有更好的方法?我想到的一种可能性是依赖注入休眠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
    }
}

当然,必须执行一些检查(例如,列表包含域类等)。您还可以将特定的参数传递给闭包,以使其更加灵活(例如 flushfailOnErrordeepValidate等)

最新更新