休眠延迟加载的 JSF2 + Ajax EL 问题

Posted

技术标签:

【中文标题】休眠延迟加载的 JSF2 + Ajax EL 问题【英文标题】:JSF2 + Ajax EL issue with Hibernate Lazy Loading 【发布时间】:2012-10-26 10:49:07 【问题描述】:

我正在尝试将 spring、JSF2 和 hibernate 集成到一个新项目中,但是我遇到了延迟加载集合的问题。这是我的问题的摘要,我试图弄清楚如何通过来自 EL(JSF) 的 ajax/PPR 请求进行延迟加载(从带有 fetch = Lazy 的休眠)。

问题是我的视图正在尝试访问一个应该通过 EL 延迟加载的集合,但是由于请求已完成(我正在使用 opensessioninviewfilter),该对象被分离。那么,当使用 PPR 和许多简短的 ajax-y 请求时,有什么方法可以延迟加载数据?

在这种情况下,我试图列出多个域对象,并且在部分页面渲染中,使用视图范围的 bean,我想选择其中一个对象并显示有关它的详细信息并可能更新它。我的应用程序中的典型信息流如下:

*域对象列表被加载并填充到 p:datatable。域对象存储在 View 范围内的支持 bean 的列表中。 *当用户单击一个项目以对其进行编辑时,该项目被加载到支持 bean 中的一个名为 workingItem 的变量中,使用 ajax/PPR 和 viewscoped bean。 *通常,然后项目将被加载到对话框中(通常是 Primefaces p:dialog),这将再次在 ajax 中完成。 *这是事情发生故障的地方。如果域对象有一个延迟加载的集合(可能是为了填充 p:selectOneMenu),它总是会抛出一个 LazyLoadingException。

所以我的问题是,如何在 ajax 调用期间访问代理时加载延迟加载的集合(我没有机会重新附加分离的对象)。

*我不能使用 Eager fetch 来解决这个问题,对象图非常大,我需要将会话信息复制到其他服务器。 *我正在使用 Spring 的 opensessioninviewfilter,但我不相信它在这种情况下可以帮助我(对吗?) *我的根本问题是,当 UI 尝试拉取域对象上的属性时,我不知道如何将域对象附加到会话以执行延迟加载。

我发现在大量使用 ajax 的情况下,延迟加载(使用休眠)非常困难。任何建议,建议将不胜感激!

这是我的支持 bean(通用):

@Component
@Scope("view")
public abstract class CrudBean<T extends DomainObject,U extends CrudService> extends AgoraBean     implements Serializable

private static final Logger logger = LoggerFactory.getLogger(CrudBean.class);

protected U crudService;

protected T workingItem;
protected List<T> cachedItems;
protected List<T> filteredList;

public abstract String createWorkingItem();

public void setWorkingItem(T item)

    workingItem = item;


public T getWorkingItem()

    return workingItem;



public List<T> getAllItems()

    if ( cachedItems == null )
    
        cachedItems = getCrudService().getAllItems();
    
    return cachedItems;


/* Other crud-dy stuff removed */


这是我的一个域对象的示例:

@Entity
@Table(name = "sites" )
public class Site implements Serializable, DomainObject 

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(updatable = false, nullable = false)
private Long id; 

@Version
private Integer version;

@Column(length=255,nullable=false)
private String name;

@Column(length=25)
private String abbreviation;

@Column(length=100)
private String street;

@Column(length=100)
private String city;

@Column(length=2)
private String locationState;

@Column(length=10)
private String zip;

@Column(length=20)
private String phone;

@Column(length=20)
private String fax;

@Column(length=50)
private String contactEmail;

@Index(name = "idxSitePublished")
private boolean published;

@Index(name = "idxSiteDefault")
private boolean defaultSite;

@ManyToOne
private Header header;

@ManyToMany
@Fetch(FetchMode.SELECT)
@BatchSize(size=5)
@OrderBy("priority ASC")
private List<Script> scripts;

@ManyToMany
@BatchSize(size=5)
@OrderBy("priority ASC")
private List<Style> styles;

还有我的 Web.xml

 <?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/application-context.xml</param-value>
</context-param>    
<context-param>
    <param-name>primefaces.THEME</param-name>
    <param-value>home</param-value>
</context-param>
<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>*.html</url-pattern>
</servlet-mapping>
<session-config>
    <session-timeout>
        60
    </session-timeout>
</session-config>


<!-- Listeners - Spring should come first because we need them for our listener -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
    <listener-class>net.dupage88.www.servlet.SiteServletContextListener</listener-class>
</listener>

<welcome-file-list>
    <welcome-file>index.html</welcome-file>
</welcome-file-list>
<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/public/index.html</location>
</error-page>
<!--<error-page>
    <error-code>404</error-code>
    <location>/public/404.jsf</location>
</error-page>
<error-page>
    <error-code>401</error-code>
    <location>/public/401.jsf</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/public/500.jsf</location>
</error-page>-->

<!-- For Lazy Loading -->
<filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter> 
<filter-mapping> 
    <filter-name>hibernateFilter</filter-name> 
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>RootRedirectFilter</filter-name>
    <filter-class>net.dupage88.www.filters.RootRedirectFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>RootRedirectFilter</filter-name>
    <url-pattern>/index.html</url-pattern>
</filter-mapping>


<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

<listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>


<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>
</web-app>

任何帮助或指导将不胜感激,并感谢您提前提供的任何帮助!

谢谢, 查克

【问题讨论】:

【参考方案1】:

您已经回答了自己,您需要再次将实体重新附加到 Hibernete/JPA 会话。查看What is the proper way to re-attach detached objects in Hibernate?中的所有cmets

只需按 ID 加载实体

em.find(entity.getClass(), entity.getId());

您也可以使用合并方法,但如果同时更改实体,如果您使用乐观锁定(版本控制),合并可能会更改您的数据或抛出异常。所以我不推荐使用merge来重新附加。

【讨论】:

以上是关于休眠延迟加载的 JSF2 + Ajax EL 问题的主要内容,如果未能解决你的问题,请参考以下文章

休眠延迟加载不适用于多对一映射

休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话

如何在休眠/弹簧中更新多对多集合(延迟加载)?

休眠从对象中延迟获取嵌套列表

为纯 AJAX 构建 JSF 2 应用程序

从控制台应用程序使用带有休眠功能的spring-data-jpa时如何延迟加载收集