Grails手动事务和回滚

时间:2014-02-19 19:09:59

标签: grails transactions gorm

我正在尝试实施以下方案:

class A {
    ...
    static hasMany = [bees: B]
}

class B {
    static belongsTo = [a: A]
}

现在,我们一次创建大约10,000个属于A的B(来自服务)实例,但是如果1失败,它应该回滚所有成功创建的实例。我尝试了一些方法,但都没有效果:

try {
    for (int i = 0; i < batch.numToMake; i++) {
        def b = new B().save(flush: i % 1000 == 0)

        if (i == 50) {
            throw new RuntimeException("simulate")
        }
    }

    batch.status = Constants.BATCH_STATUS_DONE
    batch.save(flush: true)
} catch (e) {
    // This was a last resort test and still did not work
    batch.status = Constants.BATCH_STATUS_FAILED
    batch.vouchers.each {
        batch.removeFromVouchers(it)
    }

    batch.save(flush: true)
}

// This did not work at all
B.withTransaction { status ->
    try {
        for (int i = 0; i < batch.numToMake; i++) {
            def b = new B().save(flush: i % 1000 == 0)

            if (i == 50) {
                throw new RuntimeException("simulate")
            }
        }
    } catch (e) {
        status.setRollbackOnly()
    }
}

任何人都可以帮助我如何在hasMany / belongsTo关系中创建大量项目,但是从服务类回滚1次失败的所有内容。


1 个答案:

答案 0 :(得分:0)

您的服务需要是交易性的(将grails @Transactional注释放在服务类之上),然后您不需要尝试/捕获。从该方法抛出的任何RuntimeException都将触发事务回滚。 所以你可以简单地做到以下几点:

def import() {
    def a = A.get(1L)
    for (int i = 0; i < 1000; i++) {
        new B(a: a).save()

        // runtime exception thrown here would rollback everything
    }
}

使用批处理时需要注意的是确保会话不会变得太大。您可以通过获取当前会话的句柄然后刷新并清除它来阻止它:

def import() {
    Parent.withSession { session ->
        def a = A.get(1L)
        for (int i = 0; i < 10000; i++) {
            new B(a: a).save()

            // runtime exception thrown here would rollback everything

            if (i % 100 == 0) {
                session.flush()
                session.clear()
            }

            // runtime exception thrown here would rollback everything
        }
    }