0

I'm Developing a JSF application using JPA 2 EclipseLink. I need to use a ListDataModel implementation of my list instead of the normal List implemettaion. I want to put this method in an abstract facade class so that it is available through the whole application. For a normal List implementation the abstract class

public List<T> findAll() {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    return getEntityManager().createQuery(cq).getResultList();
}

The abstract class implementation is:

@Stateless
public class ExportFacade extends AbstractFacade<Export> {

    @PersistenceContext(unitName = "GazpromModulePU")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public ExportFacade() {
        super(Export.class);
    }

This results from the method above are being correctly displayed and everything works fine. Now I want to do exactly the same thing but return the results in a ListDataModel. I tried:

public ListDataModel<T> findAllListDataModel() {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    return new ListDataModel<T>(getEntityManager().createQuery(cq).getResultList());
}

Using the same implementation (and the above abstract method), the list is not displayed and the error console is blank.I can manually hardcode the ListDataModel using a method like this:

public ListDataModel<Export> hardCodedMethod() {
    if(someList == null) {
         someList = makeModel();
    }
    return someList;
}

public ListDataModel<Export> makeModel() {
    List<Export> elist = myFacade.findAll();
    ListDataModel<Export> model = new ListDataMOdel<Export>(eList);
    return model;
}

I would like to implement the above code in an abstract class instead of hardcoding it throughout the application. Would appreciate any guidance at all . Thanks in advance!

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
jay tai
  • 419
  • 1
  • 17
  • 35

1 Answers1

1

You're in first place not supposed to have any JSF artifact in a service class. This tight-couples your business tier to the current web tier. In other words, your business tier is not reusable across different web tiers, such as RESTful, Websockets, plain JSP/Servlet, etc.

Don't change your service class. Keep returning List from it. Make sure that you don't have any import javax.faces.*** line in any service class. Solve your problem in the JSF side instead. E.g. create an abstract backing bean.

public abstract class ListDataModelBacking<T> {

    private transient ListDataModel<T> model;

    public abstract List<T> getListFromService();

    public ListDataModel<T> getModel() {
        if (model == null) {
            model = new ListDataModel<>(getListFromService());
        }

        return model;
    }

}

Note that the DataModel implementations are supposed to be not serializable.

Then you can use it as below in a regular backing bean:

@Named
@ViewScoped
public class FooBacking extends ListDataModelBacking<Foo> implements Serializable {

    @Inject
    private FooService fooService;

    @Override
    public List<Foo> getListFromService() {
        return fooService.listAll();
    }

}
<h:dataTable value="#{fooBacking.model}" ...>

Alternatively, punch the ListDataModel and look for a simpler solution to the concrete functional requirement you tried to solve with having a ListDataModel property in bean. Perhaps EL 2.2 capability of passing method arguments? See also a.o. How can I pass selected row to commandLink inside dataTable?

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thank you for the excellent tips. Actually this question shows how much I need to learn about the MVC structure of JSF and I now try implementing what you have suggested. – jay tai Jul 20 '15 at 09:35
  • 1
    You're welcome. Perhaps this is helpful: http://stackoverflow.com/questions/30639785/ – BalusC Jul 20 '15 at 09:39
  • Yeah of course it's helpful! I need to dissect what you wrote. My first concern however is about ViewScoped. Whenever i use anything other than Session scoped the bean loses focus with persistent methods. I'm used to the pre-CDI beans. Perhaps ViewScoped in CDI works better? – jay tai Jul 20 '15 at 15:26
  • 1
    Make sure you import `@ViewScoped` from right package. There are two; one for JSF managed beans and other for CDI managed beans. The wrong import would effectively make the bean behave like request/none scoped. – BalusC Jul 20 '15 at 15:30