简体   繁体   English

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

[英]JSF2 + Ajax EL issue with Hibernate Lazy Loading

I am attempting to intergrate spring, JSF2 and hibernate into a new project, but I am having an issue with lazy-loaded collections. 我正在尝试将spring,JSF2集成到一个新项目中,但是对于延迟加载的集合我遇到了问题。 Here is the summary of my issue, I am attempting to figure out how I can lazy load (from hibernate with fetch = Lazy) via an ajax/PPR request from EL(JSF). 这是我的问题的摘要,我试图弄清楚如何通过EL(JSF)的ajax / PPR请求来进行延迟加载(从休眠的fetch = Lazy)。

The problem is that my view is attempting to access a collection that should be lazy loaded via EL, but the object is detached because the request has finished (I am using opensessioninviewfilter). 问题是我的视图正在尝试访问应该通过EL延迟加载的集合,但是由于请求已完成而对象已分离(我正在使用opensessioninviewfilter)。 So is there any way to lazy load data when using PPR with many short ajax-y requests? 那么,在对许多简短的ajax-y请求使用PPR时,有什么方法可以延迟加载数据吗?

In this instance I am attempting to list multiple domain objects, and in a partial page render, using a view scoped bean, I want to pick one of the objects and display details about it and potentially update it. 在这种情况下,我试图列出多个域对象,并且在使用视图作用域的Bean进行部分页面渲染的过程中,我想选择一个对象并显示有关该对象的详细信息并可能对其进行更新。 The typical flow of information in my application would be as follows: 我的应用程序中的典型信息流如下:

*A list of domain objects is loaded and populated to ap:datatable. *域对象列表已加载并填充到ap:datatable中。 The domain objects are stored in a list in a backing bean that is View scoped. 域对象存储在View范围的后备bean的列表中。
*When the user clicks on an item to edit it, the item is loaded into a variable in the backing bean called workingItem using ajax/PPR with the viewscoped bean. *当用户单击某个项目进行编辑时,该项目将被加载到支持bean中的变量中,该变量使用带有viewscoped bean的ajax / PPR来命名为workingItem。
*Typically, then the item would be loaded into a dialog (typcially a Primefaces p:dialog), once again this would be done in ajax. *通常,然后将项目加载到对话框(通常是Primefaces p:dialog)中,再次以ajax完成。 *Here is where things break down. *这是发生故障的地方。 If the domain object has a collection that is lazy loaded (maybe to populate ap:selectOneMenu) it always throws a LazyLoadingException. 如果域对象具有延迟加载的集合(可能是填充ap:selectOneMenu),它将始终引发LazyLoadingException。

So my question is, how can I get the a lazy loaded collection to be loaded when the proxy is accessed during an ajax call (where I wouldn't have the opportunity to re-attached the detached object). 所以我的问题是,当在ajax调用期间访问代理时,如何才能获得一个惰性加载的集合(在这种情况下,我将没有机会重新附加分离的对象)。

*I CAN NOT use a eager fetch to work around this, the object graph is very large and I need to replicate the session information to other servers. *我无法使用紧急获取来解决此问题,对象图非常大,我需要将会话信息复制到其他服务器。
*I am using Spring's opensessioninviewfilter, but I don't believe it can help me in this instance (right?) *My root problem is that I don't know how to attach an domain object to a session to do the lazy-load when the UI attempting to pull a property on the domain object. *我正在使用Spring的opensessioninviewfilter,但我不认为它可以在这种情况下对我有帮助(对吗?)*我的根本问题是我不知道如何将域对象附加到会话上以进行延迟加载当UI尝试在域对象上提取属性时。

I am finding that lazy-loading (with hibernate) is very hard to do where ajax is used a lot. 我发现在经常使用ajax的情况下,很难进行延迟加载(使用休眠)。 Any recommendations, suggestions would be GREATLY appreciated! 任何建议,建议将不胜感激!

Here is my backing 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 */

}

Here is an example of one of my domain objects: 这是我的一个域对象的示例:

@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;
}

And My Web.xml 还有我的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>

Any help or guidance would be appreciated and thanks for any help you can provide in advance! 任何帮助或指导将不胜感激,并感谢您可以提前提供任何帮助!

Thanks, Chuck 谢谢,查克

You have answered yourself, you need to reattach entity again to Hibernete/JPA session. 您已经回答了自己,需要再次将实体重新连接到Hibernete / JPA会话。 See all comments in What is the proper way to re-attach detached objects in Hibernate? 在Hibernate中重新附着分离对象的正确方法是什么?

Just load the entity by ID 只需按ID加载实体

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

You can use merge method as well, but if the entity is changed meanwhile, the merge may change your data or throw exception if you use optimistic locking (versioning). 您也可以使用merge方法,但是如果同时更改实体,则在使用乐观锁定(版本控制)的情况下合并可能会更改数据或引发异常。 So I do not recommend to use merge for reattaching. 因此,我不建议使用合并进行重新连接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM