grails 2.4.4:如何在服务方法中立即提交每个插入(也就是保存)。

时间:2015-03-02 17:06:14

标签: grails transactions commit

  1. 如果我声明一个方法是非事务性的,那么我保存一条记录,做一些事情,保存另一条记录,即使第二次保存失败也会提交第一个保存并抛出异常,无论如何?
  2. 如果从IS事务的另一个服务方法调用NonTransactional serivce方法,会发生什么?它现在是否成为外部事务的一部分,因此如果SomeOtherdomainObject()。save()失败,第一个对象将被回滚?
  3. E.g。

    @Transactional
    class SomeService {
        @NotTransactional
        def someMethod() {
            new SomeDomainObject().save(failOnError:true, flush:true)
            // do stuff, possibly throw a RuntimeException
            new SomeOtherdomainObject().save(failOnError:true)
            // do more stuff, possibly throw a RuntimeException
        }
    }
    

    因此被调用(在非事务性调用案例中):

    class SomeControler{
       def someService
       def someControllerMethod() {
           someService.someMethod()
       }
    }
    

1 个答案:

答案 0 :(得分:2)

据我所知:

  1. 为什么不设置一些集成测试来确认这一点(让我们知道结果)?有a good guide here。请注意,测试类需要是非事务性的,以便测试事务功能。我链接到的示例页面使用JUnit,但这里有一些Spock代码可以满足你的需要。

    // MyDomainObject.groovy
    class MyDomainObject {
        String details
    
        static constraints = {
            // this is default anyway, but I want to make it obvious
            // not setting details and then calling save will cause an exception if
            // save's failOnError is true
            details nullable: false  
        }
    }
    
    // MyService.groovy
    class MyService {
        // only make methods transactional when we explicitly want them to be
        static transactional = false
    
        // create 2 objects and save, 1st should save ok and second should fail
        def nonTransactionalDoubleSave() {
            def objA = new MyDomainObject()
            objA.details = "This should save ok"
            objA.save(flush: true, failOnError: true)
    
            def objB = new MyDomainObject()
            objB.details = null  // null by default, but I'm just making the point
            objB.save(flush: true, failOnError: true)  // this will trigger an exception
        }
    
        def nonTransactionalSingleSave() {
            def objA = new MyDomainObject()
            objA.details = "This should save ok"
            objA.save(flush: true, failOnError: true)
        }
    
        @Transactional
        def transactionalSave() {
            nonTransactionalSingleSave()  // this should create 1 object
            // this should create 2 objects, but the 2nd will trigger an exception and rollback the transaction, meaning there should be no objects in the DB
            nonTransactionalDoubleSave()  
        }
    }
    
    
    import spock.lang.*
    import grails.test.spock.*
    
    class MyServiceIntegrationSpec extends IntegrationSpec {    
        static transactional = false  // the test case must not be transactional
    
        def myService = new MyService()
    
        def setup() {
            // remove all my domain objects from the database that might be in there
            MyDomainObject.where{}.deleteAll()
        }
    
        def cleanup() {
            // remove all my domain objects from the database that a test may have created
            MyDomainObject.where{}.deleteAll()
        }
    
        def "Question 1: nonTransactionalDoubleSave should create 1 object only"() {
            expect: "a clean database"
            MyDomainObject.count() == 0
    
            when: "nonTransactionalDoubleSave is called"
            myService.nonTransactionalDoubleSave()
    
            then: "we get an exception but still get one object in the database"
            thrown(Exception)
            MyDomainObject.count() == 1
            def obj = MyDomainObject.list().getAt(0)
            obj.details = "This should save ok"
        }
    
        def "Question 2: transactionalSave should create no objects"() {
            expect: "a clean database"
            MyDomainObject.count() == 0
    
            when: "transactionalSave is called"
            myService.transactionalSave()
    
            then: "we get an exception and no objects in the database"
            thrown(Exception)
            MyDomainObject.count() == 0
        }
    
    }