使用Spring Roo jsf生成应用程序的LazyInitializationException

时间:2014-02-01 22:24:39

标签: spring hibernate jsf-2 lazy-loading spring-roo

我一直在寻找一段时间。我发现OpenSessionInViewFilter应该可以解决这个问题,但无论如何它似乎不适用于我的情况。

我也发现一些帖子说明手动初始化有帮助,但我不明白如何做到这一点,因为存储库是一个接口,它似乎是在我不知道的地方实现(我似乎无法找到从哪里开始寻找这一点。)

我正在使用Spring Roo-jsf-hibernate应用程序。在某些页面中,我在控制台中收到以下错误:

  

严重:javax.el.E​​LException:/pages/dossier.xhtml @ 76,72   value =“#{dossierDescription.documentTypesList}”:读取错误   'documentTypesList'en el tipo   co.qcsc.spatha.domain.dossier.DossierDescription feb 01,2014 4:50:43   下午   com.sun.faces.context.PartialViewContextImpl $ PhaseAwareVisitCallback   访问SEVERE:javax.el.E​​LException:/pages/dossier.xhtml @ 76,72   value =“#{dossierDescription.documentTypesList}”:读取错误   'documentTypesList'en el tipo   co.qcsc.spatha.domain.dossier.DossierDescription

搜索我到达此处的原因:

  

org.hibernate.LazyInitializationException:懒得初始化   角色集合:   co.qcsc.spatha.domain.dossier.DossierDescription.documentTypes,可以   不初始化代理 - 没有会话

由于这是Roo,它已经在web.xml中使用了OpenSessionInViewFilter:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>spatha</display-name>
  <description>Roo generated spatha application</description>
  <context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
  </context-param>
  <context-param>
    <param-name>primefaces.THEME</param-name>
    <param-value>cupertino</param-value>
  </context-param>
  <filter>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
  </filter-mapping>
  <filter>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
    <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>ReportServlet</servlet-name>
    <servlet-class>co.qcsc.spatha.web.servlet.ReportServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>ReportServlet</servlet-name>
    <url-pattern>/clients.pdf</url-pattern>
  </servlet-mapping>

  <session-config>
    <session-timeout>10</session-timeout>
  </session-config>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>

班级:

@RooJavaBean
@RooToString
@RooJpaEntity
public class DossierDescription {

    @NotNull
    @Size(min = 2)
    private String name;

    @ManyToOne
    private Client client;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "dossierDescription", fetch = FetchType.LAZY)
    private Set<DocumentType> documentTypes = new HashSet<DocumentType>();

    @NotNull
    private Boolean valid;

    public List<DocumentType> getDocumentTypesList() {
        List<DocumentType> list = new ArrayList<DocumentType>();
        list.addAll(getDocumentTypes());
        return list;
    }

}

由于我有存储库和服务层,我有,你可以想象:

public class DossierDescriptionServiceImpl implements DossierDescriptionService {
}

@RooJpaRepository(domainType = DossierDescription.class)
public interface DossierDescriptionRepository {
}

存储库的方面是(这个我根本不理解):

privileged aspect DossierDescriptionRepository_Roo_Jpa_Repository {

    declare parents: DossierDescriptionRepository extends JpaRepository<DossierDescription, Long>;

    declare parents: DossierDescriptionRepository extends JpaSpecificationExecutor<DossierDescription>;

    declare @type: DossierDescriptionRepository: @Repository;

}

在xhtml的相关部分我有:

  <h:panelGrid id="createPanelGrid" columns="2" styleClass="dialog" columnClasses="col1,col2">
    <h:outputText value="#{app.label_specialty}" />
    <p:autoComplete id="specialtyInput"
            value="#{dossierMB.dossier.specialty.specialty}"
            completeMethod="#{dossierMB.completeSpecialty}"
            dropdown="true" var="specialty" required="false"
            itemLabel="#{specialty.name}" itemValue="#{specialty}"
            converter="co.qcsc.spatha.web.mb.converter.SpecialtyConverter"/>
    <p:tabView id="dossierDescriptionsTabView" var="dossierDescription"
            value="#{dossierMB.client.dossierDescriptionsList}">
        <p:tab id="dossierDescriptionTab" title="#{dossierDescription.name}">
            <p:dataTable id="dossierList" value="#{dossierDescription.documentTypesList}"
                    var="documentType" resizableColumns="false" paginator="true"
                    paginatorTemplate=" {CurrentPageReport}  {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
                    rowsPerPageTemplate="5,10,25,50" rows="10">
               <p:column>
                   <f:facet name="header">
                       <h:outputText value="#{app.label_documentType}" />
                   </f:facet>
                    <h:outputText value="#{documentType.name}"/>
               </p:column>
            </p:dataTable>
        </p:tab>
    </p:tabView>
  </h:panelGrid>

对应的ManagedBean是:

@RooSerializable
@RooJsfManagedBean(entity = Dossier.class, beanName = "dossierMB")
public class DossierMB {

    @Autowired
    PurchaseOrderService poService;

    @Autowired
    ClientService clientService;

    @Autowired
    SpecialtyService specialtyService;

    private Client client;
    private List<PurchaseOrder> purchaseOrders;
    private PurchaseOrder purchaseOrder;
    private Dossier dossier;
    private OrderItem orderItem;

    public List<Client> getClients() {
        return clientService.findAllClients();
    }

    public String displayConsultPO() {
        return "consultPO";
    }

    public String findPurchaseOrders() {
        purchaseOrders = poService.findPurchaseOrderByClient(client);
        return "consultPO";
    }

    public String displayCreateDialog() {
        if (orderItem.getDossiers() == null){
            orderItem.setDossiers(new HashSet<Dossier>());
        }
        dossier = new Dossier();
        return "dossier";
    }

    public List<Specialty> completeSpecialty(String query) {
        List<Specialty> suggestions = new ArrayList<Specialty>();
        for (Specialty specialty : specialtyService.findAllSpecialtys()) {
            String specialtyStr = String.valueOf(specialty.getName());
            if (specialtyStr.toLowerCase().contains(query.toLowerCase())) {
                suggestions.add(specialty);
            }
        }
        return suggestions;
    }

    public Client getClient() {
        return client;
    }

    public void setClient(Client client) {
        this.client = client;
    }

    public List<PurchaseOrder> getPurchaseOrders() {
        return purchaseOrders;
    }

    public void setPurchaseOrders(List<PurchaseOrder> purchaseOrders) {
        this.purchaseOrders = purchaseOrders;
    }

    public PurchaseOrder getPurchaseOrder() {
        return purchaseOrder;
    }

    public void setPurchaseOrder(PurchaseOrder purchaseOrder) {
        this.purchaseOrder = purchaseOrder;
    }

    public OrderItem getOrderItem() {
        return orderItem;
    }

    public void setOrderItem(OrderItem orderItem) {
        this.orderItem = orderItem;
    }
}

完成后,这是applicationContext.xml的相关部分:

<bean class="org.springframework.orm.jpa.JpaTransactionManager"
    id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj"
    transaction-manager="transactionManager" />
<bean
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit" />
    <property name="dataSource" ref="dataSource" />
</bean>

stacktrace:

WARNING: #{dossierMB.displayCreateDialog}: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: co.qcsc.spatha.domain.dossier.DossierDescription.documentTypes, could not initialize proxy - no Session
javax.faces.FacesException: #{dossierMB.displayCreateDialog}: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: co.qcsc.spatha.domain.dossier.DossierDescription.documentTypes, could not initialize proxy - no Session
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
    at javax.faces.component.UICommand.broadcast(UICommand.java:315)
    at javax.faces.component.UIData.broadcast(UIData.java:1093)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:180)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)
Caused by: javax.faces.el.EvaluationException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: co.qcsc.spatha.domain.dossier.DossierDescription.documentTypes, could not initialize proxy - no Session
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
    ... 31 more
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: co.qcsc.spatha.domain.dossier.DossierDescription.documentTypes, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124)
    at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
    at co.qcsc.spatha.web.mb.DossierMB.displayCreateDialog(DossierMB.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.apache.el.parser.AstValue.invoke(AstValue.java:278)
    at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
    ... 32 more

感谢阅读,

2 个答案:

答案 0 :(得分:0)

这意味着某人正在关闭OpenEntityManagerInView创建的实体管理器,然后才能进入视图呈现阶段。

OpenEntityManagerInView仅与某些交易管理器兼容,javadoc提到JpaTransactionManagerJtaTransactionManager,请查看详细信息。

这是正常的,因为将实体管理器绑定到线程是不够的,事务管理器需要知道它应该在创建新EM之前先在线程中查找。

事务管理器和过滤器之间需要有一定程度的协调和兼容性。

因此,一种解释是使用的事务管理器与OpenEntityManagerInView不兼容。

另一种解释是,使用EntityManager.close()直接在应用程序代码中关闭实体管理器。

在所有情况下,有一件事似乎是肯定的:问题的原因是某人正在关闭实体经理。

修改

问题也可能是由会话范围的bean引起的,该bean在一个请求中初始化,留在会话中并在新的HTTP请求中访问。第一个请求的初始会话在第一个请求完成时关闭,导致第二个请求的延迟初始化异常。

您可以通过以下方式进一步排查:

  • 检查事务管理器是否与过滤器兼容

  • 确保未在应用程序中手动调用em.close()

  • EntityManagerImpl.close()上放置一个断点,当它被击中时向下滚动堆栈跟踪,看看谁正在关闭它。

答案 1 :(得分:0)

问题是当您尝试访问documentTypes时,没有来自DB的加载数据会话。所以,你有3个解决方案:

  1. documentTypes的加载模式更改为EAGER
  2. 使用@PostLoad加载列表(与以前的选项几乎相同)
  3. 强制在访问LAZY属性之前创建连接:
  4. 
    
        @Transactional
        public List getDocumentTypesList() {
            DossierDescription connected = DossierDescription.findDossierDescription(getId);
            List list = new ArrayList();
            list.addAll(connected.getDocumentTypes());
            return list;
        }