Grails 2.5.6事务回滚

时间:2017-11-02 10:13:14

标签: hibernate grails

我想要做的主要事情是,如果发生错误或域类无效,则返回编辑页面。 我有一个User对象,其中包含Person对象作为属性。

我的控制器看起来像这样:

Object update(){
     User.withTransaction{status ->
        try {
            personDaoService?.bindObject(person, params)
            person.validate()
        } catch(Exception e){
            log.error(e.printStackTrace()) 
            status.setRollbackOnly()
        }

        if(person.hasErrors()){ 
            status.setRollbackOnly()
        }
        if(status.isRollbackOnly()){
            person.discard()
            person.refresh()
            return render(view: 'edit', model:[user:user, person:person], params: params)
        }

       try {
            userDaoService?.bindObject(person, params)
            user.validate() 
        } catch(Exception e){
            log.error(e.printStackTrace()) 
            status.setRollbackOnly()
        } 

        if(status.isRollbackOnly()){
            user.discard()
            user.refresh()
            return render(view: 'edit, model:[user:user, person:person], params: params)
        }       
    }
}

返回编辑页面时,我总是得到一个异常,告诉我没有打开会话来加载人物对象。

为了快速解决我在用户对象的映射中添加了lazy:false但是可疑的结果是整个项目运行得非常慢,所以我不得不删除lazy:false映射选项。

现在我正试图找到让它重新运转的方法。

为什么hibernate会在回滚时关闭会话?

如何重新开启会话?

修改

我尝试在设置回滚之前呈现视图:

if(person.hasErrors()){                        
    person.errors.allErrors.each{user.errors.rejectValue("person", "", "${validationTagLib.message(error: it)}")}
    render(view: 'edit', model: [user: user], params: params)
    status.setRollbackOnly()
    return
}

这会产生以下异常: org.hibernate.LazyInitializationException: could not initialize proxy - no Session

2 个答案:

答案 0 :(得分:2)

我在DomainClasses中创建了一个方法来加载所需的所有对象,并在返回编辑页面时调用此方法:

 Object update(){
     User.withTransaction{status ->
        try {
            personDaoService?.bindObject(person, params)
            person.validate()
        } catch(Exception e){
            log.error(e.printStackTrace()) 
            status.setRollbackOnly()
        }

        if(person.hasErrors()){ 
            status.setRollbackOnly()
        }
        if(status.isRollbackOnly()){
            person.discard()
            person.refresh()
            loadObject(person)
            return render(view: 'edit', model:[user:user, person:person], params: params)
        }

       try {
            userDaoService?.bindObject(person, params)
            user.validate() 
        } catch(Exception e){
            log.error(e.printStackTrace()) 
            status.setRollbackOnly()
        } 

        if(status.isRollbackOnly()){
            user.discard()
            user.refresh()
            loadObject(user)
            return render(view: 'edit', model:[user:user, person:person], params: params)
        }       
    }
}

加载所有类型属于domainClass的属性的方法如下所示:

void loadObject(Object object, List<Object> loadedObjects = []){
    loadedObjects.add(object)
    for(MapEntry propertyEntryMap in object.properties){
        Object property = propertyEntryMap?.value
        if(grailsApplication.isDomainClass(property.getClass())){
            if(!loadedObjects.contains(property))
                loadObject(property, loadedObject)
        }
    }
    if(object.hasProperty("hasMany")){  
        for(def manyProperty in object.hasMany){
            for(Object property in object."${manyProperty.key}"){
                if(grailsApplication.isDomainClass(property.getClass())){
                   if(!loadedObjects.contains(property))
                       loadObject(property, loadedObject)
                }
            }
        } 
    }
}

唯一可以解决的问题是,hasMany关系可能会很大并且会减慢它的速度。

为了解决这个问题,我在DomainClass中添加了一个属性,其中包含应该加载的hasMany关系:

static lazyProperties = ['addresses']

此属性会影响将遍历该列表中属性的loadObject方法:

void loadObject(Object object, List<Object> loadedObjects = []){
    loadedObjects.add(object)
    for(MapEntry propertyEntryMap in object.properties){
        Object property = propertyEntryMap?.value
        if(object.hasProperty('lazyProperties')){ // check if object contains the property 'lazyProperties'
            if(object?.lazyProperties?.contains(propertyEntryMap.key))
                continue
        }
        if(grailsApplication.isDomainClass(property.getClass())){
            if(!loadedObjects.contains(property))
                loadObject(property, loadedObject)
        }
    }
    if(object.hasProperty("hasMany")){  
        for(def manyProperty in object.hasMany){
            if(object.hasProperty('lazyProperties')){ // check if object contains the property 'lazyProperties'
                 if(object?.lazyProperties?.contains(manyProperty.key))
                     continue
            }
            for(Object property in object."${manyProperty.key}"){
                if(grailsApplication.isDomainClass(property.getClass())){
                   if(!loadedObjects.contains(property))
                       loadObject(property, loadedObject)
                }
            }
        } 
    }
}

答案 1 :(得分:0)

尝试扩展上述评论。

错误表明某些事情严重错误,因此休眠必须关闭会话。

考虑这个控制器:

 def doSomething() {

        SomeBean bean = new SomeBean(params.long('id'))
        bean.updateUser=userService.currentUser
        try {
            serice.foSomething(bean)
        } catch (ValidationException ve) {
            flash.message=message(error:ve.errors.getGlobalError())?:ve.errors
        } catch (DataIntegrityViolationException e) {
        } catch (HibernateObjectRetrievalFailureException e) {

        } catch (Throwable t) {
            flash.message= t.toString()
        }
        redirect(controller:'someController', action:'list')
    }

它有如此之多的尝试捕获的原因是因为我找到了例如我正在获得HibernateObjectRetrievalFailureException的一些争吵的代码,但实际的工作已经完成 - 这使得你概​​述的那种情况发生了 - 或者可以发生

我的意思是尝试使用try catch来解决服务方法并找出最初的实际情况。

最重要的是,前3次尝试捕获实际上告诉控制器继续 - 只是忽略那些问题(我认为它们都在这种情况下)但如果它不是重定向你可以在throwable中添加一些子句或重定向因此你可以去其他地方或者简短地忽略它

要完成上述所有操作,this may help you可以解决您的问题

import grails.validation.ValidationException
class AuthorService {

    void updateAge(id, int age) {
        def author = Author.get(id)
        author.age = age
        if (!author.validate()) {
            throw new ValidationException("Author is not valid", author.errors)
        }
    }
}

基本上,当您最终捕获到问题并找出导致问题的原因时,您可以在问题发生之前将其捕获并返回自己的自定义验证异常。

然后,您的标准故障就会被接收,其他一切都会顺利进行。

你有你的图层验证并确保一切正常 - 你显示的当前验证图层 - 问题实际上并不在这里 - 当你问Hibernate并做一些无效的事情时 - 删除可能的事情'被删除 - 做一些它不能做的事情 - 然后打破它并引起你当前的头痛 - 所以通过在它发生之前捕获它们并做上述将是你如何解决这些问题