jay tai
jay tai

Reputation: 489

How to use JSF ListDataModel object in abstract JPA criteria query

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!

Upvotes: 0

Views: 675

Answers (1)

BalusC
BalusC

Reputation: 1109665

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?

Upvotes: 1

Related Questions