Initialize model values on initial request only and not on postback request - jsf-2

I have a managedbean AddDeviceBean where in instantiating all domain objects used in screen in the constructor
public AddDeviceBean() {
device = new DeviceVO();
deviceacct = new DeviceAccountsVO();
deviceconfig = new DeviceConfigVO();
devicecurr =new DeviceCurrencyVO();
devicelink = new DeviceLinkVO();
devicetran = new DeviceTranVO();
devicecd = new DeviceCDVO();
deviceBlank = new DeviceBlankVO();
comments = new ArrayList<DeviceCommentsVO>();
}
I have a DB2 sequence whose next value has to be set for a property on pageload
I am using #PostConstruct annotation to generate the next value and setting the value.
Problem is I have commandButton on the screen which invokes some method in same bean and #PostConstruct is called twice after submitting and the DB2 next value is called
I need to get next value only once during page load and not during submit

That will indeed happen when your managed bean is request scoped. A request scoped bean is constructed on every single HTTP request. The initial request counts as one request. The form submit (the postback) counts as another request.
If you want to make the bean to live as long as you're interacting with the same view, then you should be making it a view scoped one instead.
#ManagedBean
#ViewScoped
public class AddDeviceBean {
// ...
}
See also:
How to choose the right bean scope?

Related

FacesContext.getCurrentInstance().getExternalContext().invalidateSession() calls posconstruct twice

I have managed bean by name studentManagedBean. In that bean I have used post construct to intialize studentsList. In another managed bean testbean I was using
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
and redirecting to page students.xhtml where I used to display students.
My question is when I used the FacesContext.getCurrentInstance().getExternalContext().invalidateSession(); and redirected to student.xtml page, init method(post construct) used to call two times. When I commented the above line, init method(post construct) now calls only one time.
can any one tell me what is this invalidate session will exactly do.
#ManagedBean(name = "studentManagedBean" )
#SessionScoped
public class StudentManagedBean implements Serializable {
private List<SBean> stud;
#PostConstruct
private void init(){
this.stud=dao.getAllStudInfo();
}
#ManagedBean(name = "testBean" )
#SessionScoped
public class TestBean implements Serializable {
public String navigate(String name){
if(name.equals("Add student")){
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "student";
}
Apparently, the session scoped bean StudentManagedBean is also referenced in the current view. When the view get built/restored, it may create the bean if it's referenced during view build time. But if you're invalidating the session thereafter, the session scoped bean get destroyed (obviously, since it's stored in the session scope) and will be re-created again when the target view still references it during rendering of the view.
This must make completely sense. If you don't want that the bean is created before you invalidate the session, simply don't reference it anywhere in the current view, either directly in the view, or indirectly as a managed property or a programmatic EL evaluation of another bean which is directly referenced in the current view.
If you can't immediately figure out where it's been referenced, just put a debug breakpoint in the bean's constructor and explore the call stack for the who/what/why.

How to prevent #PostConstruct from being called on postback

When the page loads for the first time, the #PostConstruct is called, but when I perform a postback on this page, the #PostConstruct is called again.
How can I make it to run only on the initial request and not on every postback?
#PostContruct
public void init() {
// charge combos....
}
public void submit() {
// action
}
Apparently your bean is request scoped and thus reconstructed on every HTTP request. I'm not exactly sure why you'd like to prevent the #PostConstruct from being called again, as you would otherwise end up with an "empty" bean state which might possibly lead to form submit errors, but okay, you could add a check if the current request is not a postback.
public void init() {
if (!FacesContext.getCurrentInstance().isPostback()) {
// charge combos....
}
}
This way the "charge combos" part won't be invoked on postbacks.
Or, maybe your actual question is not "How to prevent postconstruct from being called on postback?", but more "How to retain the same bean instance on postback?". In that case, you'd need to put the bean in the view scope instead of in the request scope.
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
// ...
}
As long as you return null from action methods, this way the same bean instance will live as long as you're interacting with the same view by postbacks. This way the #PostConstruct won't be invoked (simply because the bean isn't been reconstructed).
See also:
How to choose the right bean scope?
use this import:
import javax.faces.view.ViewScoped; for #ViewScoped

Cannot reference ViewScoped Managed bean from PhaseListener in JSF 2.1 app

I've tried every solution from a couple dozen google searches: getRequestMap, ElResolver, evaluateExpressionGet and so on. Nothing, absolutely nothing, works. I get null every time. Here is the relevant code:
#ManagedBean(name="readerBean")
#ViewScoped
public class ReaderBean implements Serializable {...
And in the PhaseListener:
public void beforePhase(PhaseEvent event) {
if (event.getPhaseId() == PhaseId.RESTORE_VIEW) {
ReaderBean r = null; //The Managed Bean
try {
FacesContext fctx = FacesContext.getCurrentInstance();
r=(ReaderBean) fctx.getExternalContext().getRequestMap().get("readerBean");
r=(ReaderBean) fctx.getELContext().getELResolver().getValue(fctx.getELContext(), null, "readerBean");
r=(ReaderBean) fctx.getApplication().getExpressionFactory().createValueExpression(fctx.getELContext(), "#{readerBean}", ReaderBean.class).getValue(fctx.getELContext());
r=(ReaderBean) fctx.getApplication().evaluateExpressionGet(fctx, "#{readerBean}", ReaderBean.class);
Nothing works!!!
As to the request map approach, it fails because a view scoped bean is not stored in the request scope. It's instead stored in the view scope. As to the other approaches, they fail because the view scoped bean is stored in the view and thus only available after the view has been restored. However, you're trying to get it before the view has been restored. This is a chicken-egg problem.
You need to revise your approach. Perhaps the bean needs to be a request scoped one? Perhaps the logic needs to be executed at a different phase? Perhaps the logic needs to be performed in the bean itself instead? Etc.
As the concrete functional requirement for which you thought that this is the right solution is unmentioned in the question, I can't point you in the right direction.

Setting ManagedBean values in another ManagedBean's method

If I am navigating from page one to page two, When I click on Page one's submit button, control goes to the managed bean written corresponding to page one, I want to fetch some result from database and show them to page two, If I set the Database values into the Managed bean corresponding to page two in the first Managed Bean's action method, then will I be able to get those on page two.
Please suggest if my approach is right?
You can inject let's say bean named Bean1 into Bean2 this way
#ManagedBean
#RequestScoped
public Bean2 {
#ManagedProperty(value="#{bean1}")//this is EL name of your bean
private Bean1 injectedBean;
public void setBean1(Bean1 value) {
injectedBean = value; //provide setter for it
}
#PostConstruct
private void init() {
List<YourData> list = injectedBean.getDBData(); //now you can fetch your list
}
Note that you have to provide setter for injection and scope of the injected bean should be always same or higher then the your beans scope (in this case Bean1 has to be at least RequestScope)
P.S. Also you could have some kind of DAO bean which could do all the database database operations for you (it can be EJB bean or even JSF Managed Bean) and then inject it to every ManagedBean which will require cooperation with your DB.

Exception while lazily loading a collection from entity

I fear that I miss a very basic point, but I'm stuck at the moment, hopefully someone can point my eyes starring to hard at it to the right one...
I try to go through my application setup step by step for make myself clear (hoping people won't get bored before they reach the end of the posting):
I have a session scoped CDI component serving as a handler for a JSF 2.0 view. It holds an entity manager, a list of objects and a special single object:
#Named
#SessionScoped
public class EventHandler implements Serializable {
#PersistenceContext
private EntityManager em;
private List<MyEvent> events;
private MyEvent currentEvent;
...
When the view requests the list for displaying a table, it fills the list by querying the database:
Query query = em.createQuery("select e from MyEvent e");
events = (ArrayList<MyEvent>) query.getResultList();
The view shows this in a data table and provides a link to an action method within the handler:
<h:dataTable value="#{eventHandler.events}" var="_var">
...
<h:commandLink action="#{eventHandler.linkAction(_var)}"> ... </h:commandLink>
The action method stores the reference:
public void setCurrentEvent(MyEvent currentEvent) {
this.currentEvent = currentEvent;
...
Within the entity used in the collection and references above, there'a relationship which gets loaded lazily by default:
#ManyToMany(mappedBy="supportedServices")
public Set<MyEntity> getSupportingEntities() {
...
Now, when being in the detail view and trying to access this relationship by doing:
#{eventHandler.currentEvent.supportingEntities...}
I get a org.hibernate.LazyInitializationException telling me that the session is closed.
Shouldn't the handler be able to load the requested relationship at a later point in the 2nd view, when the handler is session scoped and loaded the references via a query?
Hum, I think this is because the persistenceContext, event if it is a member of a session scoped bean, is not session scoped.
When the view is called the first time, a persistence context is created, the entities(events) are loaded and the datatable is populated. Then the persistence context is flushed, closed and the response is sent to the client. This all happen in the same http request.
But when you then call the details view, another http request is issued, and another persistence context is created, and your reference to "currentEvent" is no more attached to the last persistence context. Hence the LazyInitializationError (the entity must be managed to lazy load).
Solutions could be :
Use an extended persistence context (explained here).
Eager fetch the relation :
#ManyToMany(mappedBy="supportedServices", fetch=FetchType.EAGER)
Merge the detached instance of currentEvent before using it in the details view :
entityManager.merge(currentEvent);

Resources