使用Hibernate Lazy Loading的JSF2 + Ajax EL问题

时间:2012-11-06 13:10:42

标签: ajax hibernate jsf lazy-loading

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

问题是我的视图正在尝试访问应该通过EL延迟加载的集合,但该对象已分离,因为请求已完成(我正在使用opensessioninviewfilter)。那么在使用带有许多短ajax-y请求的PPR时,有没有办法延迟加载数据?

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

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

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

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

我发现懒惰加载(使用hibernate)非常难以在很多地使用ajax。任何建议,建议都会非常感激!

这是我的支持bean(Generic):

@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 个答案:

答案 0 :(得分:0)

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

中的所有评论

只需按ID加载实体

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

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