Given below an exemplary <p:dataTable> using the PrimeFaces lazy data model. The row version field as marked by #javax.persistence.Version in the associated JPA entity is temporarily displayed in one of the columns in read-only mode.
Needless to mention that every action is Ajaxical.
Editing this <p:dataTable> using a <p:rowEditor/> as follows (the first row in the table. The row version is 1).
When a row being edited is updated by clicking the tick mark in the Edit column as above, the row version is incremented in the database to 2 on successful update but the row version held by the data table is still 1 (see the picture below). After update is completed, the status of the data table can be viewed as follows.
What happens now is if the same row is attempted again (without refreshing the page by sending a synchronous GET request), then it will not match the row version in the database which is currently 2 because the data table still supplies the old previous value of the version which is 1. This will cause the javax.persistence.OptimisticLockException to be thrown as if concurrent updates are detected which is evidently untrue.
Something may need to be changed drastically in the normal Ajaxical update facility provided by a <p:dataTable> (using a <p:rowEditor/>) in order for it to work correctly and synchronously with the optimistic locking strategy in JPA.
What would be the correct way to get around the situation? Dropping the <p:rowEditor/> in its entirely will need tremendous changes to existing applications, by the way which is quite undesired.
While editing/updating rows in a <p:dataTable> using a <p:rowEditor>, it is required to synchronize changes manually to the data model (LazyDataModel<T>) backed by the <p:dataTable> after the update operation finishes successfully.
One way to do so is to use the getWrappedData() method available in LazyDataModel<T> which returns the data currently held by the data model backed by the data table in the form of java.lang.Object. This is a basic usage.
#Named
#ViewScoped
public class Bean extends LazyDataModel<Entity> implements Serializable {
#Inject
private Service service;
private static final long serialVersionUID = 1L;
public Bean() {}
#Override
public List<Fruit> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) {
setRowCount(service.getRowCount());
// Do something, if necessary.
// Turn the List<SortMeta> into another type like a LinkedHashMap<String, String>.
return service.getList(first, pageSize, multiSortMeta, filters);
}
public void onRowEdit(RowEditEvent event) {
if (event.getObject() instanceof Entity) {
Entity entity = (Entity) event.getObject();
Entity newEntity = service.update(entity); // A new entity from the database.
if (newEntity != null) {
List<Entity> entities = (List<Entity>) getWrappedData();
int index = entities.indexOf(entity);
if (index >= 0) { // The test may be omitted.
entities.set(index, newEntity);
// Just replace the stale/old entity by a newly updated entity in the database.
}
// Add an appropriate FacesMessage to indicate a success.
} else {
// Add an appropriate FacesMessage to indicate a failure.
}
} else {
// Add an appropriate FacesMessage to indicate a failure.
}
}
}
The onRowEdit() method is bound to an Ajax listener inside a <p:dataTable>.
<p:ajax event="rowEdit" listener="#{bean.onRowEdit}"/>
I am not going to accept this answer as there could be a better way to do so which I am unaware of. More comprehensive answers are welcomed, if any.
Related
Let's say I specify an outputText component like this:
<h:outputText value="#{ManagedBean.someProperty}"/>
If I print a log message when the getter for someProperty is called and load the page, it is trivial to notice that the getter is being called more than once per request (twice or three times is what happened in my case):
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
If the value of someProperty is expensive to calculate, this can potentially be a problem.
I googled a bit and figured this is a known issue. One workaround was to include a check and see if it had already been calculated:
private String someProperty;
public String getSomeProperty() {
if (this.someProperty == null) {
this.someProperty = this.calculatePropertyValue();
}
return this.someProperty;
}
The main problem with this is that you get loads of boilerplate code, not to mention private variables that you might not need.
What are the alternatives to this approach? Is there a way to achieve this without so much unnecessary code? Is there a way to stop JSF from behaving in this way?
Thanks for your input!
This is caused by the nature of deferred expressions #{} (note that "legacy" standard expressions ${} behave exactly the same when Facelets is used instead of JSP). The deferred expression is not immediately evaluated, but created as a ValueExpression object and the getter method behind the expression is executed everytime when the code calls ValueExpression#getValue().
This will normally be invoked one or two times per JSF request-response cycle, depending on whether the component is an input or output component (learn it here). However, this count can get up (much) higher when used in iterating JSF components (such as <h:dataTable> and <ui:repeat>), or here and there in a boolean expression like the rendered attribute. JSF (specifically, EL) won't cache the evaluated result of the EL expression at all as it may return different values on each call (for example, when it's dependent on the currently iterated datatable row).
Evaluating an EL expression and invoking a getter method is a very cheap operation, so you should generally not worry about this at all. However, the story changes when you're performing expensive DB/business logic in the getter method for some reason. This would be re-executed everytime!
Getter methods in JSF backing beans should be designed that way that they solely return the already-prepared property and nothing more, exactly as per the Javabeans specification. They should not do any expensive DB/business logic at all. For that the bean's #PostConstruct and/or (action)listener methods should be used. They are executed only once at some point of request-based JSF lifecycle and that's exactly what you want.
Here is a summary of all different right ways to preset/load a property.
public class Bean {
private SomeObject someProperty;
#PostConstruct
public void init() {
// In #PostConstruct (will be invoked immediately after construction and dependency/property injection).
someProperty = loadSomeProperty();
}
public void onload() {
// Or in GET action method (e.g. <f:viewAction action>).
someProperty = loadSomeProperty();
}
public void preRender(ComponentSystemEvent event) {
// Or in some SystemEvent method (e.g. <f:event type="preRenderView">).
someProperty = loadSomeProperty();
}
public void change(ValueChangeEvent event) {
// Or in some FacesEvent method (e.g. <h:inputXxx valueChangeListener>).
someProperty = loadSomeProperty();
}
public void ajaxListener(AjaxBehaviorEvent event) {
// Or in some BehaviorEvent method (e.g. <f:ajax listener>).
someProperty = loadSomeProperty();
}
public void actionListener(ActionEvent event) {
// Or in some ActionEvent method (e.g. <h:commandXxx actionListener>).
someProperty = loadSomeProperty();
}
public String submit() {
// Or in POST action method (e.g. <h:commandXxx action>).
someProperty = loadSomeProperty();
return "outcome";
}
public SomeObject getSomeProperty() {
// Just keep getter untouched. It isn't intented to do business logic!
return someProperty;
}
}
Note that you should not use bean's constructor or initialization block for the job because it may be invoked multiple times if you're using a bean management framework which uses proxies, such as CDI.
If there are for you really no other ways, due to some restrictive design requirements, then you should introduce lazy loading inside the getter method. I.e. if the property is null, then load and assign it to the property, else return it.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
This way the expensive DB/business logic won't unnecessarily be executed on every single getter call.
See also:
Why is the getter called so many times by the rendered attribute?
Invoke JSF managed bean action on page load
How and when should I load the model from database for h:dataTable
How to populate options of h:selectOneMenu from database?
Display dynamic image from database with p:graphicImage and StreamedContent
Defining and reusing an EL variable in JSF page
Measure the render time of a JSF view after a server request
With JSF 2.0 you can attach a listener to a system event
<h:outputText value="#{ManagedBean.someProperty}">
<f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
</h:outputText>
Alternatively you can enclose the JSF page in an f:view tag
<f:view>
<f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
.. jsf page here...
<f:view>
I have written an article about how to cache JSF beans getter with Spring AOP.
I create a simple MethodInterceptor which intercepts all methods annotated with a special annotation:
public class CacheAdvice implements MethodInterceptor {
private static Logger logger = LoggerFactory.getLogger(CacheAdvice.class);
#Autowired
private CacheService cacheService;
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
String key = methodInvocation.getThis() + methodInvocation.getMethod().getName();
String thread = Thread.currentThread().getName();
Object cachedValue = cacheService.getData(thread , key);
if (cachedValue == null){
cachedValue = methodInvocation.proceed();
cacheService.cacheData(thread , key , cachedValue);
logger.debug("Cache miss " + thread + " " + key);
}
else{
logger.debug("Cached hit " + thread + " " + key);
}
return cachedValue;
}
public CacheService getCacheService() {
return cacheService;
}
public void setCacheService(CacheService cacheService) {
this.cacheService = cacheService;
}
}
This interceptor is used in a spring configuration file:
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<bean class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
<constructor-arg index="0" name="classAnnotationType" type="java.lang.Class">
<null/>
</constructor-arg>
<constructor-arg index="1" value="com._4dconcept.docAdvance.jsfCache.annotation.Cacheable" name="methodAnnotationType" type="java.lang.Class"/>
</bean>
</property>
<property name="advice">
<bean class="com._4dconcept.docAdvance.jsfCache.CacheAdvice"/>
</property>
</bean>
Hope it will help!
Originally posted in PrimeFaces forum # http://forum.primefaces.org/viewtopic.php?f=3&t=29546
Recently, I have been obsessed evaluating the performance of my app, tuning JPA queries, replacing dynamic SQL queries with named queries, and just this morning, I recognized that a getter method was more of a HOT SPOT in Java Visual VM than the rest of my code (or majority of my code).
Getter method:
PageNavigationController.getGmapsAutoComplete()
Referenced by ui:include in in index.xhtml
Below, you will see that PageNavigationController.getGmapsAutoComplete() is a HOT SPOT (performance issue) in Java Visual VM. If you look further down, on the screen capture, you will see that getLazyModel(), PrimeFaces lazy datatable getter method, is a hot spot too, only when enduser is doing a lot of 'lazy datatable' type of stuff/operations/tasks in the app. :)
See (original) code below.
public Boolean getGmapsAutoComplete() {
switch (page) {
case "/orders/pf_Add.xhtml":
case "/orders/pf_Edit.xhtml":
case "/orders/pf_EditDriverVehicles.xhtml":
gmapsAutoComplete = true;
break;
default:
gmapsAutoComplete = false;
break;
}
return gmapsAutoComplete;
}
Referenced by the following in index.xhtml:
<h:head>
<ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>
Solution: since this is a 'getter' method, move code and assign value to gmapsAutoComplete prior to method being called; see code below.
/*
* 2013-04-06 moved switch {...} to updateGmapsAutoComplete()
* because performance = 115ms (hot spot) while
* navigating through web app
*/
public Boolean getGmapsAutoComplete() {
return gmapsAutoComplete;
}
/*
* ALWAYS call this method after "page = ..."
*/
private void updateGmapsAutoComplete() {
switch (page) {
case "/orders/pf_Add.xhtml":
case "/orders/pf_Edit.xhtml":
case "/orders/pf_EditDriverVehicles.xhtml":
gmapsAutoComplete = true;
break;
default:
gmapsAutoComplete = false;
break;
}
}
Test results: PageNavigationController.getGmapsAutoComplete() is no longer a HOT SPOT in Java Visual VM (doesn't even show up anymore)
Sharing this topic, since many of the expert users have advised junior JSF developers to NOT add code in 'getter' methods. :)
If you are using CDI, you can use Producers methods.
It will be called many times, but the result of first call is cached in scope of the bean and is efficient for getters that are computing or initializing heavy objects!
See here, for more info.
You could probably use AOP to create some sort of Aspect that cached the results of our getters for a configurable amount of time. This would prevent you from needing to copy-and-paste boilerplate code in dozens of accessors.
If the value of someProperty is
expensive to calculate, this can
potentially be a problem.
This is what we call a premature optimization. In the rare case that a profiler tells you that the calculation of a property is so extraordinarily expensive that calling it three times rather than once has a significant performance impact, you add caching as you describe. But unless you do something really stupid like factoring primes or accessing a databse in a getter, your code most likely has a dozen worse inefficiencies in places you've never thought about.
I would also advice using such Framework as Primefaces instead of stock JSF, they address such issues before JSF team e. g in primefaces you can set partial submit. Otherwise BalusC has explained it well.
It still big problem in JSF. Fo example if you have a method isPermittedToBlaBla for security checks and in your view you have rendered="#{bean.isPermittedToBlaBla} then the method will be called multiple times.
The security check could be complicated e.g . LDAP query etc. So you must avoid that with
Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?
and you must ensure within a session bean this per request.
Ich think JSF must implement here some extensions to avoid multiple calls (e.g annotation #Phase(RENDER_RESPONSE) calle this method only once after RENDER_RESPONSE phase...)
Let's say I specify an outputText component like this:
<h:outputText value="#{ManagedBean.someProperty}"/>
If I print a log message when the getter for someProperty is called and load the page, it is trivial to notice that the getter is being called more than once per request (twice or three times is what happened in my case):
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
If the value of someProperty is expensive to calculate, this can potentially be a problem.
I googled a bit and figured this is a known issue. One workaround was to include a check and see if it had already been calculated:
private String someProperty;
public String getSomeProperty() {
if (this.someProperty == null) {
this.someProperty = this.calculatePropertyValue();
}
return this.someProperty;
}
The main problem with this is that you get loads of boilerplate code, not to mention private variables that you might not need.
What are the alternatives to this approach? Is there a way to achieve this without so much unnecessary code? Is there a way to stop JSF from behaving in this way?
Thanks for your input!
This is caused by the nature of deferred expressions #{} (note that "legacy" standard expressions ${} behave exactly the same when Facelets is used instead of JSP). The deferred expression is not immediately evaluated, but created as a ValueExpression object and the getter method behind the expression is executed everytime when the code calls ValueExpression#getValue().
This will normally be invoked one or two times per JSF request-response cycle, depending on whether the component is an input or output component (learn it here). However, this count can get up (much) higher when used in iterating JSF components (such as <h:dataTable> and <ui:repeat>), or here and there in a boolean expression like the rendered attribute. JSF (specifically, EL) won't cache the evaluated result of the EL expression at all as it may return different values on each call (for example, when it's dependent on the currently iterated datatable row).
Evaluating an EL expression and invoking a getter method is a very cheap operation, so you should generally not worry about this at all. However, the story changes when you're performing expensive DB/business logic in the getter method for some reason. This would be re-executed everytime!
Getter methods in JSF backing beans should be designed that way that they solely return the already-prepared property and nothing more, exactly as per the Javabeans specification. They should not do any expensive DB/business logic at all. For that the bean's #PostConstruct and/or (action)listener methods should be used. They are executed only once at some point of request-based JSF lifecycle and that's exactly what you want.
Here is a summary of all different right ways to preset/load a property.
public class Bean {
private SomeObject someProperty;
#PostConstruct
public void init() {
// In #PostConstruct (will be invoked immediately after construction and dependency/property injection).
someProperty = loadSomeProperty();
}
public void onload() {
// Or in GET action method (e.g. <f:viewAction action>).
someProperty = loadSomeProperty();
}
public void preRender(ComponentSystemEvent event) {
// Or in some SystemEvent method (e.g. <f:event type="preRenderView">).
someProperty = loadSomeProperty();
}
public void change(ValueChangeEvent event) {
// Or in some FacesEvent method (e.g. <h:inputXxx valueChangeListener>).
someProperty = loadSomeProperty();
}
public void ajaxListener(AjaxBehaviorEvent event) {
// Or in some BehaviorEvent method (e.g. <f:ajax listener>).
someProperty = loadSomeProperty();
}
public void actionListener(ActionEvent event) {
// Or in some ActionEvent method (e.g. <h:commandXxx actionListener>).
someProperty = loadSomeProperty();
}
public String submit() {
// Or in POST action method (e.g. <h:commandXxx action>).
someProperty = loadSomeProperty();
return "outcome";
}
public SomeObject getSomeProperty() {
// Just keep getter untouched. It isn't intented to do business logic!
return someProperty;
}
}
Note that you should not use bean's constructor or initialization block for the job because it may be invoked multiple times if you're using a bean management framework which uses proxies, such as CDI.
If there are for you really no other ways, due to some restrictive design requirements, then you should introduce lazy loading inside the getter method. I.e. if the property is null, then load and assign it to the property, else return it.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
This way the expensive DB/business logic won't unnecessarily be executed on every single getter call.
See also:
Why is the getter called so many times by the rendered attribute?
Invoke JSF managed bean action on page load
How and when should I load the model from database for h:dataTable
How to populate options of h:selectOneMenu from database?
Display dynamic image from database with p:graphicImage and StreamedContent
Defining and reusing an EL variable in JSF page
Measure the render time of a JSF view after a server request
With JSF 2.0 you can attach a listener to a system event
<h:outputText value="#{ManagedBean.someProperty}">
<f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
</h:outputText>
Alternatively you can enclose the JSF page in an f:view tag
<f:view>
<f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
.. jsf page here...
<f:view>
I have written an article about how to cache JSF beans getter with Spring AOP.
I create a simple MethodInterceptor which intercepts all methods annotated with a special annotation:
public class CacheAdvice implements MethodInterceptor {
private static Logger logger = LoggerFactory.getLogger(CacheAdvice.class);
#Autowired
private CacheService cacheService;
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
String key = methodInvocation.getThis() + methodInvocation.getMethod().getName();
String thread = Thread.currentThread().getName();
Object cachedValue = cacheService.getData(thread , key);
if (cachedValue == null){
cachedValue = methodInvocation.proceed();
cacheService.cacheData(thread , key , cachedValue);
logger.debug("Cache miss " + thread + " " + key);
}
else{
logger.debug("Cached hit " + thread + " " + key);
}
return cachedValue;
}
public CacheService getCacheService() {
return cacheService;
}
public void setCacheService(CacheService cacheService) {
this.cacheService = cacheService;
}
}
This interceptor is used in a spring configuration file:
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<bean class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
<constructor-arg index="0" name="classAnnotationType" type="java.lang.Class">
<null/>
</constructor-arg>
<constructor-arg index="1" value="com._4dconcept.docAdvance.jsfCache.annotation.Cacheable" name="methodAnnotationType" type="java.lang.Class"/>
</bean>
</property>
<property name="advice">
<bean class="com._4dconcept.docAdvance.jsfCache.CacheAdvice"/>
</property>
</bean>
Hope it will help!
Originally posted in PrimeFaces forum # http://forum.primefaces.org/viewtopic.php?f=3&t=29546
Recently, I have been obsessed evaluating the performance of my app, tuning JPA queries, replacing dynamic SQL queries with named queries, and just this morning, I recognized that a getter method was more of a HOT SPOT in Java Visual VM than the rest of my code (or majority of my code).
Getter method:
PageNavigationController.getGmapsAutoComplete()
Referenced by ui:include in in index.xhtml
Below, you will see that PageNavigationController.getGmapsAutoComplete() is a HOT SPOT (performance issue) in Java Visual VM. If you look further down, on the screen capture, you will see that getLazyModel(), PrimeFaces lazy datatable getter method, is a hot spot too, only when enduser is doing a lot of 'lazy datatable' type of stuff/operations/tasks in the app. :)
See (original) code below.
public Boolean getGmapsAutoComplete() {
switch (page) {
case "/orders/pf_Add.xhtml":
case "/orders/pf_Edit.xhtml":
case "/orders/pf_EditDriverVehicles.xhtml":
gmapsAutoComplete = true;
break;
default:
gmapsAutoComplete = false;
break;
}
return gmapsAutoComplete;
}
Referenced by the following in index.xhtml:
<h:head>
<ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>
Solution: since this is a 'getter' method, move code and assign value to gmapsAutoComplete prior to method being called; see code below.
/*
* 2013-04-06 moved switch {...} to updateGmapsAutoComplete()
* because performance = 115ms (hot spot) while
* navigating through web app
*/
public Boolean getGmapsAutoComplete() {
return gmapsAutoComplete;
}
/*
* ALWAYS call this method after "page = ..."
*/
private void updateGmapsAutoComplete() {
switch (page) {
case "/orders/pf_Add.xhtml":
case "/orders/pf_Edit.xhtml":
case "/orders/pf_EditDriverVehicles.xhtml":
gmapsAutoComplete = true;
break;
default:
gmapsAutoComplete = false;
break;
}
}
Test results: PageNavigationController.getGmapsAutoComplete() is no longer a HOT SPOT in Java Visual VM (doesn't even show up anymore)
Sharing this topic, since many of the expert users have advised junior JSF developers to NOT add code in 'getter' methods. :)
If you are using CDI, you can use Producers methods.
It will be called many times, but the result of first call is cached in scope of the bean and is efficient for getters that are computing or initializing heavy objects!
See here, for more info.
You could probably use AOP to create some sort of Aspect that cached the results of our getters for a configurable amount of time. This would prevent you from needing to copy-and-paste boilerplate code in dozens of accessors.
If the value of someProperty is
expensive to calculate, this can
potentially be a problem.
This is what we call a premature optimization. In the rare case that a profiler tells you that the calculation of a property is so extraordinarily expensive that calling it three times rather than once has a significant performance impact, you add caching as you describe. But unless you do something really stupid like factoring primes or accessing a databse in a getter, your code most likely has a dozen worse inefficiencies in places you've never thought about.
I would also advice using such Framework as Primefaces instead of stock JSF, they address such issues before JSF team e. g in primefaces you can set partial submit. Otherwise BalusC has explained it well.
It still big problem in JSF. Fo example if you have a method isPermittedToBlaBla for security checks and in your view you have rendered="#{bean.isPermittedToBlaBla} then the method will be called multiple times.
The security check could be complicated e.g . LDAP query etc. So you must avoid that with
Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?
and you must ensure within a session bean this per request.
Ich think JSF must implement here some extensions to avoid multiple calls (e.g annotation #Phase(RENDER_RESPONSE) calle this method only once after RENDER_RESPONSE phase...)
In a project i need to lazy load object from database, and for each element i will put a link to redirect to a specific page.
The lazy loading is working. and when a click on the link for the first element it's ok, the problem is after scrolling, the next element don't call the listner listOi.editer().
<p:dataScroller value="#{listOi.lazyOi}" var="oi" id="OisChoser" widgetVar="scroller"
chunkSize="5" mode="inline" scrollHeight="531" lazy="true" style="width: 597px;" rows="5" >
<h:panelGroup id="info_OI" class="info_OI" align="center" >
...
<h:commandLink actionListener="#{listOi.editer()}" immediate="true" >
<f:param name="selectedoiId" value="#{oi.id}" />
<span class="crayon" style='cursor: pointer;'></span>
</h:commandLink>
...
</h:panelGroup
</p:dataScroller>
The problem is that PrimeFaces' LazyDataModel does not keep a complete model of the data displayed in the view. It only keeps track of the most recently loaded items and discards older items. This makes that these items are no longer accessible from JSF.
But since you are subclassing that class anyway (it's abstract), it's pretty easy to alter that behavior. Basically what you want to do is keep track of all the data you loaded so far and return that data as asked for. At the minimum, you need to override setRowIndex(), getRowData(), isRowAvailable() and load(). Below is an example that works for me.
public class MyLazyModel extends LazyDataModel<SomeType> implements Serializable {
private final List<SomeType> data;
private int rowIndex;
public MyLazyModel() {
super();
data = new ArrayList<>();
}
#Override
public List<SomeType> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<SomeType> retData;
if (first >= data.size()) {
retData = ... //Get the data from datasource
data.addAll(retData);
return retData;
} else {
return data.subList(first, Math.min(first + pageSize, data.size()));
}
}
#Override
public void setRowIndex(int index) {
if (index >= data.size()) {
index = -1;
}
this.rowIndex = index;
}
#Override
public SomeType getRowData() {
return data.get(rowIndex);
}
#Override
public boolean isRowAvailable() {
if (data == null) {
return false;
}
return rowIndex >= 0 && rowIndex < data.size();
}
}
Because you can never be sure that setWrappedData() (which is called after load() with the return data from that method) is called only once, you need to guard against adding doubles to your data model. Here I do this by only loading data that has never been loaded before and storing that data in the load() method, ignoring setWrappedData() completely. It's quite ugly and will lead to synchronization problems if your model is never invalidated, but it works. Anyway, you could circumvent this by always loading data and replacing old content with new, but that's not the core of the issue.
Also, because you now keep track of the data in the method itself, you need to override all methods in LazyDataModel that rely on LazyDataModel.data being correct (or at least that subset of them that you are using).
Final note: you of course have to make sure that your model returned to the JSF page is always the same, as discussed here.
I really would need your help in solving this problem am having with my project. Anyway, am new to JSF. Am working on a project and there is this module where examination records are displayed to students from a database upon successful authentication. This would be easy if all students are in the same class and offer the same number of courses, as you can create a dataTable model and then use it in your view. But in my case, the database contains tables for student’s exam records in different departments and classes like Computer Science 100,200 and 300 levels, Geology 100,200 and 300 levels and so on. These students also offer entirely different courses. Some offer physics, GST, computer and some don’t.
My problem is how do I display to different students their grades and course names in a dataTable dynamically. Let me try and picture it.
100 level Computer Science table has
CS101 CS102 GST101 MATH101 MATH102 PHY101 CHM101
200 level Computer Science table has
CS201 CS202 CS203 MATH201 CHM202
Please note the difference in the number of columns and names of column headers.
Then a Computer Science 100 level student wants to view his or her exam records, how do I display these records with the column names in a dataTable dynamically and not first creating a dataTable model with predefined column names. Not knowing the number of columns or names in advance. How do you do this for different students in different classes? I want a good code that will enable students from any department or level to login and then retrieve his exam records. Am not asking for the authentication code as that has been achieved, the code will display the names of the Database Table column headers and the records they hold for a particular 100 or 200 level. I need this because I don’t have to create a dataTable model containing specific column headers as i have many tables in the Database with different numbers of columns and names for different students departments and classes. Please I need your help and I hope I’ve been able to communicate my problem clearly. Am using JSF 2.1 (Facelets and Manage Bean), MySQL Database Server on NetBeans 7.2.1.
I was in a similar situation and I am afraid that there's not possible way to fill a Datatable with unknown columns. You have to have the numbre in advance.
In my case, I found it easier to shift to JSP for this particular need and keep JSF for more "static" usage.
In my understanding, the only dynamic aspect of a Datatable is the numbre of rows.
One way to turn around this problem is using the rendered attribute of columns, in this case you'll put in every possible column and decide (via a bean, for example) to show or not some of them and hide some others. This is semi-dynamic at best, but still is a livable solution.
Best of luck.
P.S : if wrong, please inform me of the solution because I would be curious to know it though unable to use it.
How about creating a two columned table
First column will be named: Course Code/Name
Second column will be name: Grade
Thats how it is being displayed in the Uni/College anyway...
To implement it just create an object with 2 Strings and populate a list of such objects... and that will be your h:datatable value attribute...
You can use List<Map<String, Object>> to get hold of the data in a generic manner. Then you can use <c:forEach> to dynamically build columns (please note that <ui:repeat> is insuitable as it doesn't run during view build time, but during view render time).
Here's a concrete kickoff example, assuming that your environment supports EL 2.2:
<h:dataTable value="#{bean.listOfMaps}" var="map">
<c:forEach items="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
<h:column>
#{map[key]}
</h:column>
</c:forEach>
</h:dataTable>
If you need the column labels in a separate collection as well (a LinkedHashMap<String, String> is recommended for that as it maintains ordering), then instead do so:
<h:dataTable value="#{bean.listOfMaps}" var="map">
<c:forEach items="#{bean.mapOfColumns}" var="column">
<h:column>
<f:facet name="header">#{column.value}</f:facet>
#{map[column.key]}
</h:column>
</c:forEach>
</h:dataTable>
you can create column at runtime using backing bean method
public void loadDynamicList() throws Exception {
// Set headers (optional).
//dynamicHeaders = new String[] {"ID", "Name", "Value","delete"};
// Set rows. This is a stub example, just do your dynamic thing.
dynamicList = new ArrayList<String>();
/*dynamicList.add("One");
dynamicList.add("Two");
dynamicList.add("Three");
dynamicList.add("Four");
dynamicList.add("Five");
dynamicList.add("Six");*/
/*dynamicList.add(Arrays.asList(new String[] { "ID1","ID2" }));
dynamicList.add(Arrays.asList(new String[] { "ID1","ID2" }));
dynamicList.add(Arrays.asList(new String[] { "ID1","ID2" }));
dynamicList.add(Arrays.asList(new String[] { "ID1","ID2" }));
dynamicList.add(Arrays.asList(new String[] { "ID1","ID2" }));*/
existingCountryList = new ArrayList<Country>();
String countryCode="SL";
existingCountryList.add(getCountryService().getCountryByCode(countryCode));
Country country=getCountryService().getCountryByCode(countryCode);
countryLanguageSet=country.getCountryLanguage();
int languageSize=country.getCountryLanguage().size();
dynamicHeaders = new String[languageSize+1] ;
int counter=0;
for (CountryLanguage count: countryLanguageSet) {
System.out.println(count.getLanguage().getLanguageName());
dynamicHeaders[counter]=count.getLanguage().getLanguageName();
counter++;
}
dynamicHeaders[counter]="Delete";
System.out.println("header list "+dynamicHeaders.toString());
System.out.println("size"+dynamicHeaders.length);
}
public void populateDynamicDataTable() {
debugLogger.debug("populateDynamicDataTable:Enter");
// Create <h:dataTable value="#{myBean.dynamicList}" var="dynamicItem">.
HtmlDataTable dynamicDataTable = new HtmlDataTable();
dynamicDataTable.setValueExpression("value", createValueExpression("# {relationBean.dynamicList}", List.class));
dynamicDataTable.setVar("dynamicItem");
for (int count = 0; count < dynamicHeaders.length; count++) {
HtmlColumn column = new HtmlColumn();
HtmlOutputText header = new HtmlOutputText();
header.setValue(dynamicHeaders[count]);
column.setHeader(header);
if(dynamicHeaders[count].equals("Delete")){
HtmlCommandButton commandButton=new HtmlCommandButton();
commandButton.setValue("Delete"+count);
commandButton.setActionExpression(createActionExpression("#{relationBean.deleteRow}", String.class));
column.getChildren().add(commandButton);
}else{
HtmlInputText input=new HtmlInputText();
//List<String[]> ls = new ArrayList<String[]>();
input.setValueExpression("value",createValueExpression("#{dynamicItem}", String.class));
column.getChildren().add(input);
}
dynamicDataTable.getChildren().add(column);
}
dynamicDataTableGroup = new HtmlPanelGroup();
dynamicDataTableGroup.getChildren().add(dynamicDataTable);
debugLogger.debug("populateDynamicDataTable:Exit");
}
public HtmlPanelGroup getDynamicDataTableGroup() throws Exception {
// This will be called once in the first RESTORE VIEW phase.
if (dynamicDataTableGroup == null) {
loadDynamicList(); // Preload dynamic list.
populateDynamicDataTable(); // Populate editable datatable.
}
return dynamicDataTableGroup;
}
public List<String> getDynamicList() {
return dynamicList;
}
public void setDynamicList(List<String> dynamicList) {
this.dynamicList = dynamicList;
}
public void setDynamicDataTableGroup(HtmlPanelGroup dynamicDataTableGroup) {
this.dynamicDataTableGroup = dynamicDataTableGroup;
}
public ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory().createValueExpression(
facesContext.getELContext(), valueExpression, valueType);
}
public MethodExpression createActionExpression(String actionExpression, Class<?> returnType) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory().createMethodExpression(
facesContext.getELContext(), actionExpression, returnType, new Class[0]);
}
private MethodExpression createMethodExpression(String valueExpression, Class<?> valueType, Class<?>[] expectedParamTypes) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory().createMethodExpression(
facesContext.getELContext(), valueExpression, valueType, expectedParamTypes);
}
I have a jsf page with multiple radiobutton groups (dynamically generated) on it. I need to retrieve the values from it in a backing bean, but fails to do so.
The business: a user wants to subscribe to a course that consists of multiple groups of coursedays. The user can choose the coursedays. So if a course consists of for example 4 coursedays, organised in 3 different groups, the user can choose from 12 coursedays, in blocks of 3.
The relevant part of the xhtml-page:
<c:forEach var="cd1" items="#{coursedayBean.getCoursedays(groupBean.getFirstGroup}">
<h:selectOneRadio value="#{subscriptionBean.selectedCoursedays[cd1.sequenceNr]}" >
<f:selectItems value="#{coursedayBean.getCoursedaysSelectItems}"/>
</h:selectOneRadio>
</c:forEach>
This results in a n*m matrix of radiobuttons where I want to retrieve n values.
The selectItems are of type <Long, String>.
In my backing bean, I declared the following:
public List<String> getSelectedCoursedays() {
return selectedCoursedays;
}
public void setSelectedCoursedays(List<String> selectedCoursedays) {
this.selectedCoursedays = selectedCoursedays;
}
I tried with a Map, List, but none of them worked. The setSelectedCoursedays is never called.
How do I declare the array/list/map to get the values in my backing bean?
#{subscriptionBean.selectedCoursedays[cd1.sequenceNr]}
doesn't do the trick.
This construct should work just fine. The setter will indeed never be called. JSF/EL just calls the setter on ArrayList itself by the add(index, object) method. I.e. it does basically:
subscriptionBean.getSelectedCoursedays().add(cd1.sequenceNr, selectedItem);
I'm not sure how you observed the concrete problem of "it doesn't work". Perhaps you were firing an ajax request and putting a breakpoint on the setter method and didn't read the server logs. There are two possible cases where this construct will fail.
If you don't prepare the selectedCoursedays with new ArrayList(), then you will get PropertyNotFoundException: Target Unreachable, 'selectedCoursedays' returned null.
If you don't fill the selectedCoursedays with the same amount of null items as the course days, then you will get an ArrayIndexOutOfBoundsException
So, you should prepare the selectedCoursedays as follows:
#PostConstruct
public void init() {
selectedCoursedays = new ArrayList<String>();
for (int i = 0; i < coursedays.size(); i++) {
selectedCoursedays.add(null);
}
}
Easier alternative is to make it a String[].
private String[] selectedCoursedays;
#PostConstruct
public void init() {
selectedCoursedays = new String[coursedays.size()];
}
It is miss understanding between c:forEach and ui:repeat. c:forEach will not build UI component tree nodes. Firstly, you have to reference difference between them here.