0

I'm using Glassfish Server 4.1 with bundled JSF. I've enabled JPA FINE logging in "persistence.xml".

I have this Facelets page:

<p:dataTable var="customer" value="#{customerService.customers}">
</p:dataTable>
<h:form>
    <h:commandButton value="Test"/>
</h:form>

(Note that that's really all there is: I haven't associated an action with the button, or added columns to the table, and if I do, the behaviour I'm about to describe doesn't change. Also recall that default render behaviour is @none.)

And this Stateless EJB (with @Named so that the page can refer to it directly):

@Stateless
@Named
public class CustomerService {

    @PersistenceContext
    private EntityManager em;

    public List<Customer> getCustomers() {
        return em.createNamedQuery("Customer.findAll", Customer.class).getResultList();
    }
    ...

When I load the page, the following message appears in the log:

Fine:   SELECT ID, CUSTOMERNAME, EMAIL, PAID, QUANTITY, TYPE FROM CUSTOMER

So far so good. When I click the button though, this appears in the logs:

Fine:   SELECT ID, CUSTOMERNAME, EMAIL, PAID, QUANTITY, TYPE FROM CUSTOMER
Fine:   SELECT ID, CUSTOMERNAME, EMAIL, PAID, QUANTITY, TYPE FROM CUSTOMER
... Same message appears five more times ...

The Customers table is empty.

Can anyone reproduce or explain this behaviour? One SELECT for page load makes sense, but seven SELECTS for form submit is confusing.

UPDATE

The behaviour is unchanged if I move the EJB into a ViewScoped JSF backing-bean and update the Facelets page to use it:

@ManagedBean // javax.faces.bean.
@ViewScoped
public class BackingBean {
    @EJB
    private CustomerService cs;
    public List<Customer> getCustomers() {
        return cs.getCustomers();
    } 
}
DavidS
  • 4,384
  • 2
  • 23
  • 51
  • 2
    Those layers do not follow the separation of concerns at first but it is a story apart which you already know about well. I hope you are merely playing around. Based on the snippets, you have implemented a critical business logic in a getter method which is directly bound to an iterating component `` which leads the getter method to invoke several times. It does not make sense whatsoever to call a service/EJB method from a getter method. Try moving the business logic in its own place to see a difference. – Tiny Apr 27 '15 at 04:04
  • Hi @Tiny. Yes, I'm just playing around. I didn't use a backing bean because I thought "What's the point of the backing bean if I'm just going to delegate the method call anyhow?" Design shouldn't be dogmatic, and I think that in some cases direct calls to business methods could be desirable. HOWEVER, I tried what you suggested and... (to follow) – DavidS Apr 27 '15 at 05:11
  • You are 100% right. I moved customer initialization to the `@PostConstruct` method of a backing bean, as is conventional, and the extra SELECT statements ceased. I was lead astray in thinking that a `dataTable` would behave like a Java `for each` or `for` loop, where the getter is called only once (e.g. `for (Customer c : cs.getCustomers())`). I am a bit surprised that the getter is invoked during the JSF lifecycle even with `render="@none"`, but my understanding of the JSF lifecycle is still pretty shallow, so I'm not _too_ surprised. Thanks a bunch, @Tiny. – DavidS Apr 27 '15 at 05:17

2 Answers2

0

I think the problem is in the scope you are using in the bean, let me quote this from other question:

If it's CDI via @Named, then it defaults to @Dependent, as mentioned in Weld documentation: Finally, CDI features the so-called dependent pseudo-scope. This is the default scope for a bean which does not explicitly declare a scope type.The effect is that the bean instance is newly created on every single EL expression. So, imagine a login form with two input fields referring a bean property and a command button referring a bean action, thus with in total three EL expressions, then effectively three instances will be created.

Now, how do we solve it? May be you should try another scope for your bean depending on your requirements, Check out this documentation about Scopes and context in JSF.

Community
  • 1
  • 1
gerosalesc
  • 2,613
  • 3
  • 21
  • 41
  • Thanks Chamanx. I updated my question. If I move the EJB into a ViewScoped JSF backing-bean, the behaviour is unchanged: 7 SELECT statement log messages are printed on form submit. – DavidS Apr 27 '15 at 00:44
  • Also the same behaviour if I don't even use the EJB and use the EntityManager in the ViewScoped bean. – DavidS Apr 27 '15 at 00:50
  • Hi David, now reading your update i think you should try a singleton in your EJB, let me know if that works, i will try to recreate the issue later, can you create a repo so i can quickly clone it? – gerosalesc Apr 27 '15 at 01:42
  • I will create a repo tonight. – DavidS Apr 27 '15 at 01:44
  • Hello Chamanx. Thanks for your help, but Tiny explained the issue in a comment on my question. I have written an answer to my question based on his explanation. I will not be creating that repo. – DavidS Apr 27 '15 at 05:24
0

As Tiny explained in a comment on my question, the extra logging is occurring because I placed business logic inside the getter method used by the Facelets page. It is usually frowned upon to put business logic in the accessors (getter and setter) methods used by Facelets pages, as these methods are used by the JSF framework and you have little control over when and how often they are called.

In this case the issue can be "fixed" by initializing customers in the @PostConstruct method of a JSF view-scoped backing-bean:

@PostConstruct
public void init() {
    customers = customerService.getCustomers();
}
public List<Customer> getCustomers() {
    return customers;
}

This is a more conventional set-up, but I was trying to get by without a JSF backing-bean.

If someone can explain why the JSF framework is calling the method seven times, which seems arbitrary, I would definitely mark that answer correct, but in the meantime I am satisfied with this.

UPDATE

The explanation for the exact number of calls, seven, is probably similar to this answer, Why is the getter called so many times by the rendered attribute?:

The getter calls are all done by rendered="#{selector.profilePage == 'some'}" during the render response phase. When it evaluates false the first time, in UIComponent#encodeAll(), then no more calls will be done. When it evaluates true, then it will be re-evaluated six more times in the following sequence...

DavidS
  • 4,384
  • 2
  • 23
  • 51
  • 2
    Getter methods are called several times by nature especially when used with iterating components or an EL expressions evaluating to a boolean value like those used in the `rendered` attribute. Any critical logic is not to be wrapped around a getter method unless absolutely needed. [Here](http://stackoverflow.com/a/12452144/1391249) is one of the cases where it makes sense to call a business service from within a getter method. This is covered at length [here](http://stackoverflow.com/a/2090062/1391249). If it is a PF data table, it offers a much more flexible way to load the table lazily – Tiny Apr 27 '15 at 05:58