My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians


duminică, 12 iunie 2016

PrimeFaces dynamic TabView and Hibernate lazy loading

The PrimeFaces TabView component supports a flag attribute named dynamic (defaults to false). When this attribute is set to true, PrimeFaces will load only the content of the initial opened tab, while the rest of content is loaded via AJAX only when the user clicks on tabs.

So, let's image that we have the following simple database with two tables:

Now, for each category, we want to create a tab, and each tab to be loaded dynamically. We can write something like below:

<h:form id="form">
 <p:tabView dynamic="true" cache="true">          
  <p:tab title="My Site">               
   Check out our categories
  <c:forEach items="#{dataBean.allCategories}" var="t">           
   <p:tab title="#{}">
    <p:dataList value="#{t.productsCollection}" var="q" type="ordered">
     <f:facet name="header">

Ok, so the first tab is just a dummy tab that doesn't contain data from the database. While the rest of tabs reflect the names and number of available categories. Notice that we have set dynamic="true", and moreover we have set cache="true". This means that caching only retrieves the tab contents
once and subsequent toggles of a cached tab does not communicate with server.

Now, let's take a quick look to our code used for querying database. We  can use Hibernate JPA, and the relevant part is:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "categories", fetch = FetchType.EAGER)
private Set<Products> productsCollection;

Notice that Hibernate is by default lazy, so we manually turned fetching to eager.

Now, if we run the application we see the following output:

Notice that our tab is not even containing data from database. Is just a dummy tab with some intros. But, since the rest of tab's names are actually our categories names, the application must hit the database. If we check the log we notice that even if load only the names of the categories, Hibernate will load the products from each category also. Obviously, this is the effect of eager loading.

Moreover, when we click on a tab of a category, this is happening again until all tabs are cached. Now, we may want to switch to the default Hibernate fetching style, which is LAZY (simply delete the fetch element, or manually set it):

@OneToMany(cascade = CascadeType.ALL, mappedBy = "categories", fetch = FetchType.LAZY)
private Set<Products> productsCollection;

When the application starts, everything looks fine. We query only the categories names. But, when we click on other tab, we get an error of type: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: javaee.entites.Categories.productsCollection. Well, this is a well-known Hibernate exception caused by the fact that Hibernate cannot hit the database to initialize the proxy objects since the connection is already closed.

There are a few approaches to solve this issue, like:

Use  PersistenceContextType.EXTENDED with @Stateful EJB - This works only in JEE and the container will manage the database transactions. But, you have to pay attention to the number of stateful beans because they may cause memory issues.  The N+1 issue is also a risk.

Load collection by join  - for example, in our case we can go for a left join fetch of type: SELECT c FROM Categories c LEFT JOIN FETCH c.productsCollection. But, this needs a new query to access each model class collection (lazy) attribute. Against this disadvantage, we have some advantages like the fact that we fire a single query in the database and will bring only the desired data. Moreover, the N+1 issue is not a risk.

Use the @NamedEntityGraph - provides a query independent way to define a graph of entities which will be fetched with the query.

EclipseLink - This is an alternative to Hibernate JPA that works via a LAZY strategy (hint) that can be set for the persistent provider. This way the persistent provider will know that you expect those data lazy.  Moreover, the N+1 issue is not a risk.

Load collection by Open Session in View - This is basically a solution that relies on keeping a database connection opened until the end of the user request. The implementation is based on a filter that handles the transactions. The N+1 is also a risk, and there are no really advantages of this approach.

We can chose here the first approach. So, we wrote a Stateful bean as below:

public class CategoriesAndProducts {

 @PersistenceContext(unitName = "javaee_PrimeFacesHibernateLazy_war_1.0PU", type=PersistenceContextType.EXTENDED)
 private EntityManager em;

 public List<Categories> getCategoriesAction() {
  TypedQuery<Categories> query = em.createNamedQuery("Categories.findAll", Categories.class);
  List<Categories> results = query.getResultList();

  return results;

Now, when we click on a tab, Hibernate will query only the products from the selected category. You can try yourself an implementation via left join also.

The complete example is available here. You can easily run it with MySQL 5 and WildFly 10.

Niciun comentariu :

Trimiteți un comentariu


Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors