I am attempting to intergrate spring, JSF2 and hibernate into a new project, but I am having an issue with lazy-loaded collections. 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).
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). So is there any way to lazy load data when using PPR with many short ajax-y requests?
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. The typical flow of information in my application would be as follows:
*A list of domain objects is loaded and populated to a p:datatable. The domain objects are stored in a list in a backing bean that is View scoped.
*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.
*Typically, then the item would be loaded into a dialog (typcially a Primefaces p:dialog), once again this would be done in ajax.
*Here is where things break down. If the domain object has a collection that is lazy loaded (maybe to populate a p:selectOneMenu) it always throws a 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).
*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.
I am finding that lazy-loading (with hibernate) is very hard to do where ajax is used a lot. 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
<?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