我如何在多租户的 gsp 中获得懒惰的孩子?

问题描述

上周我尝试从 gsp 中的一个域的懒惰孩子那里获取值,但我不知道如何使用多租户应用程序执行此操作。当应用程序具有认数据源时,此问题不会发生,但是当我将应用程序更改为多租户时,此问题就出现了。 有人知道我如何从gsp中的懒惰孩子那里获得值吗?

域名

import grails.gorm.MultiTenant;

class Pessoa implements MultiTenant<Pessoa>{
    String nome
    Endereco endereco

    static constraints = {
        nome(nullable:false);
        endereco(nullable:true);
    }

    static mapping = {
        enderecos cascade:"all-delete-orphan"
    }
}
import grails.gorm.MultiTenant;

class Endereco implements MultiTenant<Endereco> {
    String rua;
    Integer numero;

    static constraints = {
        rua(nullable:false);
        numero(nullable:false);
    }
}

控制器

package project_multitenancy

import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
import grails.gorm.multitenancy.*

@CurrentTenant
class PessoaController {

    static allowedMethods = [save: "POST",update: "PUT",delete: "DELETE"]

    def index(Integer max) {
        params.max = Math.min(max ?: 10,100)
        [pessoaList: Pessoa.list(),pessoaCount: Pessoa.count()]
    }

    def show(Long id) {
        [pessoa: Pessoa.get(id)]
    }

    def create() {
        params.enderecos = [new Endereco()]
        [pessoa: new Pessoa(params)]
    }

    def save() {
        def pessoa = new Pessoa(params)
        try {
            pessoa.save(flush: true)
        } catch (ValidationException e) {
            respond pessoa.errors,view:'create'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message',args: [message(code: 'pessoa.label',default: 'Pessoa'),pessoa.id])
                redirect pessoa
            }
            '*' { respond pessoa,[status: CREATED] }
        }
    }

    def edit(Long id) {
        def pessoa = Pessoa.get(id)
        println pessoa
        [pessoa: pessoa]
    }

    def update(Long id) {
        def pessoa = Pessoa.get(id)

        try {
            pessoa.save(flush: true)
        } catch (ValidationException e) {
            respond pessoa.errors,view:'edit'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message',pessoa.id])
                redirect pessoa
            }
            '*'{ respond pessoa,[status: OK] }
        }
    }

    def delete(Long id) {
        def pessoa = Pessoa.get(id)

        unidadeInstance.delete(flush: true)

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.deleted.message',id])
                redirect action:"index",method:"GET"
            }
            '*'{ render status: NO_CONTENT }
        }
    }
}

GSP 示例

<!DOCTYPE html>
<html>
    <head>
        <Meta name="layout" content="main" />
        <g:set var="entityName" value="${message(code: 'pessoa.label',default: 'Pessoa')}" />
        <title><g:message code="default.show.label" args="[entityName]" /></title>
    </head>
    <body>
        <a href="#show-pessoa" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
        <div class="nav" role="navigation">
            <ul>
                <li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
                <li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
                <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
            </ul>
        </div>
        <div id="show-pessoa" class="content scaffold-show" role="main">
            <h1><g:message code="default.show.label" args="[entityName]" /></h1>
            <g:if test="${flash.message}">
            <div class="message" role="status">${flash.message}</div>
            </g:if>
            <fieldset class="form">
                <div class="fieldcontain">
                    <label for="nome">Nome
                    </label>
                    ${this.pessoa.nome}
                </div>

                <div class="fieldcontain">
                    <label for="nome">Endereco
                    </label>
                    ${this.pessoa.endereco.rua}
                </div>
            </fieldset>
            <g:form resource="${this.pessoa}" method="DELETE">
                <fieldset class="buttons">
                    <g:link class="edit" action="edit" resource="${this.pessoa}"><g:message code="default.button.edit.label" default="Edit" /></g:link>
                    <input class="delete" type="submit" value="${message(code: 'default.button.delete.label',default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message',default: 'Are you sure?')}');" />
                </fieldset>
            </g:form>
        </div>
    </body>
</html>

当我尝试从 ${this.pessoa.endereco.rua} 获取值时,异常出现:

2020-12-21 15:20:30.353 ERROR --- [io-8080-exec-10] .a.c.c.C.[.[.[.[grailsdispatcherServlet] : Servlet.service() for servlet [grailsdispatcherServlet] in context with path [] threw exception [Request processing Failed; nested exception is org.grails.gsp.GroovyPagesException: Error evaluating expression [this.pessoa.endereco] on line [32]: Could not initialize proxy - no Session] with root cause

org.hibernate.LazyInitializationException: Could not initialize proxy - no Session
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:147)
        at org.grails.orm.hibernate.proxy.SimpleHibernateProxyHandler.unwrapProxy(SimpleHibernateProxyHandler.java:88)
        at org.grails.orm.hibernate.cfg.GrailsHibernateUtil.unwrapProxy(GrailsHibernateUtil.java:386)
        at org.grails.orm.hibernate.cfg.HibernateUtils$_handleLazyProxy_closure3.doCall(HibernateUtils.groovy:124)
        at C__Users_craw02_Documents_GitHub_multitenancy_grails_app_views_pessoa_show_gsp$_run_closure2$_closure26.doCall(C__Users_craw02_Documents_GitHub_multitenancy_grails_app_views_pessoa_show_gsp:61)
        at org.grails.gsp.GroovyPage.evaluate(GroovyPage.java:253)
        at C__Users_craw02_Documents_GitHub_multitenancy_grails_app_views_pessoa_show_gsp$_run_closure2.doCall(C__Users_craw02_Documents_GitHub_multitenancy_grails_app_views_pessoa_show_gsp:61)
        at org.grails.taglib.TagBodyClosure.executeClosure(TagBodyClosure.java:200)
        at org.grails.taglib.TagBodyClosure.captureClosureOutput(TagBodyClosure.java:102)
        at org.grails.taglib.TagBodyClosure.call(TagBodyClosure.java:213)
        at org.grails.plugins.web.taglib.SitemeshTagLib.captureTagContent(SitemeshTagLib.groovy:48)
        at org.grails.plugins.web.taglib.SitemeshTagLib$_closure3.doCall(SitemeshTagLib.groovy:156)
        at org.grails.gsp.GroovyPage.invokeTagLibClosure(GroovyPage.java:446)
        at org.grails.gsp.GroovyPage.invokeTag(GroovyPage.java:364)
        at C__Users_craw02_Documents_GitHub_multitenancy_grails_app_views_pessoa_show_gsp.run(C__Users_craw02_Documents_GitHub_multitenancy_grails_app_views_pessoa_show_gsp:78)
        at org.grails.gsp.GroovyPageWritable.doWriteto(GroovyPageWritable.java:162)
        at org.grails.gsp.GroovyPageWritable.writeto(GroovyPageWritable.java:82)
        at org.grails.web.servlet.view.GroovyPageView.renderTemplate(GroovyPageView.java:76)
        at org.grails.web.servlet.view.AbstractGrailsView.renderWithinGrailsWebRequest(AbstractGrailsView.java:71)
        at org.grails.web.servlet.view.AbstractGrailsView.renderMergedOutputModel(AbstractGrailsView.java:55)
        at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:304)
        at org.grails.web.sitemesh.GrailsLayoutView.renderInnerView(GrailsLayoutView.java:150)
        at org.grails.web.sitemesh.GrailsLayoutView.obtainContent(GrailsLayoutView.java:128)
        at org.grails.web.sitemesh.GrailsLayoutView.renderTemplate(GrailsLayoutView.java:63)
        at org.grails.web.servlet.view.AbstractGrailsView.renderWithinGrailsWebRequest(AbstractGrailsView.java:71)
        at org.grails.web.servlet.view.AbstractGrailsView.renderMergedOutputModel(AbstractGrailsView.java:55)
        at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:304)
        at org.springframework.web.servlet.dispatcherServlet.render(dispatcherServlet.java:1286)
        at org.springframework.web.servlet.dispatcherServlet.processdispatchResult(dispatcherServlet.java:1041)
        at org.springframework.web.servlet.dispatcherServlet.dodispatch(dispatcherServlet.java:984)
        at org.springframework.web.servlet.dispatcherServlet.doService(dispatcherServlet.java:901)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:54)
        at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
        at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

有人知道我如何从懒惰的孩子那里获得价值吗? 啊,差点忘了, 我在 3.3.11 版本中使用 grails,在 6.1.12 版本中使用 GORM

解决方法

不要在视图中使用域类,而是创建一个单独的 DTO 类来传递给模型。