Grails REQUIRES_NEW和"非法尝试将一个集合与两个开放会话相关联#34;

时间:2015-04-07 06:38:00

标签: grails transactions gorm

当使用REQUIRES_NEW注释在服务方法上创建新事务时,会启动一个新的Hibernate会话,导致“非法尝试将集合与两个打开的会话相关联”错误。只有在保存包含集合的域对象时才会出现此错误。我正在使用Grails 2.3.11。

域对象:

class MyObject {
    String str
    Integer value = 1
    List container
    static hasMany = [container : MyCollection]
}

class MyCollection {
    String name
    static belongsTo = [obj: MyObject]
}

交易服务:

import grails.transaction.Transactional
import org.springframework.transaction.annotation.Propagation

@Transactional
class MyService {

    def getMyObjectById(Long id) {
        return MyObject.get(id)
    }

    def proxyStartStateMachine(MyObject obj) {
        doStateMachine(obj)

        if (obj.someInteger < 5) {
            triggerStateMachineReentry(obj)
        } else {
            //obj.attach()
            //obj.refresh()
            obj.discard()
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    def doStateMachine(MyObject obj) {
        updateStr(obj, "test${obj.version}")
    }

    def updateStr(MyObject obj, String str) {
        obj.str = str
        obj.save() // generates Hibernate error "Illegal attempt to associate a collection with two open sessions"
    }

    def triggerStateMachineReentry(MyObject obj) {
        obj.value++
        obj.save()

        proxyStartStateMachine(obj)
    }
}

控制器入口点:

class ApiController {
    def myService

    def index() { }

    def stuff(Long id) {
        def obj = myService.getMyObjectById(id)
        myService.proxyStartStateMachine(obj)

        render(status: 200, text: 'OK', contentType: "text/html")
    }
}

此服务的预期行为是有5个不同的事务,其中MyObject.value从1更新为2,从3更新为3到4和4到5。当从域中删除集合MyObject.container时对象,它工作正常。

MySQL日志:

2302 Query  SET autocommit=0
2302 Query  update my_object set version=1, str='test0', value=1 where id=1 and version=0
2302 Query  commit
2302 Query  SET autocommit=1
2302 Query  SET autocommit=0
2302 Query  update my_object set version=2, str='test1', value=2 where id=1 and version=1
2302 Query  commit
2302 Query  SET autocommit=1
2302 Query  SET autocommit=0
2302 Query  update my_object set version=3, str='test2', value=3 where id=1 and version=2
2302 Query  commit
2302 Query  SET autocommit=1
2302 Query  SET autocommit=0
2302 Query  update my_object set version=4, str='test3', value=4 where id=1 and version=3
2302 Query  commit
2302 Query  SET autocommit=1
2302 Query  SET autocommit=0
2302 Query  update my_object set version=5, str='test4', value=5 where id=1 and version=4
2302 Query  commit
2302 Query  SET autocommit=1

但是,只要将集合添加到域对象,它就会在MyService.updateStr()中进行第一次保存时在MyService中失败。

我知道这是因为新事务而发生的,但这是一个必需的功能,以确保提交更改,并且不会在状态机的每次迭代期间回滚。

1 个答案:

答案 0 :(得分:1)

我想我找到了解决方案。在创建新事务之前,需要使用object.discard()从Hibernate会话中删除该对象。

如果其他人有其他解决方案,请随时提出建议。