JPA Query - Parâmetros Opcionais

Sabem se existe uma forma mais elegante de tratar parametros opcionais do que repetir o mesmo if duas vezes?

                        String myQuery;
                        Query query;
                        myQuery = "SELECT r FROM Writings r Where ";
                        if(page.getPagePosition() != null)
                                myQuery +=" r.pagePosition =?1 ";
                        if(page.getPageType() != null)
                                myQuery +=" and r.pageType =?2 ";
                        if(page.getLanguageType() != null)
                                myQuery +=" and r.languageType =?3 ";

                        query = em.createQuery(myQuery);

                        if(page.getPagePosition() != null)
                                query.setParameter(1, page.getPagePosition());
                        if(page.getPageType() != null)
                                query.setParameter(2, page.getPageType());
                        if(page.getLanguageType() != null)
                                query.setParameter(3, page.getLanguageType());

Se puder usar Criteria… faça assim…


Criteria criteria = ((Session) em.getDelegate()).createCriteria(Writings.class);
if(page.getPagePosition() != null)  
       criteria.add(Restrictions.eq("pagePosition", page.getPagePosition()))
if(page.getPageType() != null)  
       criteria.add(Restrictions.eq("pageType", page.getPageType()));
if(page.getLanguageType() != null)  
       criteria.add(Restrictions.eq("languageType", page.getLanguageType()))

criteria.getSingleResult(); // Para o resultado.

Voce pode usar usar operador ternario ou fazer um método para substituir o if, algo como isso:

String useFilter( String filterJPQL, Object filterValue ) { if ( /*verifica se é um filtro valido*/ ) { return filterJPQL; } else { return ""; } }

No seu exemplo eu faria as seguintes alterações:

Usaria StringBuilder ao inves de String.
Definiria os parametros da query utilizando um alias (acho mais legivel), assim:

myQuery +=" r.pagePosition = :position "; query.setParameter( "position", page.getPagePosition() );

JPA 1 ou JPA 2?
Se for a JPA 2 a melhor opção é utilizar Criteria. Devemos utilizar criteria principalmnte em queries dinâmicas, que é o seu caso.

Agora se for JPA 1 (que não tem criteria), eu partiria para a idéia do amigo em usar StringBuffer.

Acredito que seu código ficará mais elegante dessa forma.

[]'s

List<Object> parameters = new ArrayList<Object>();

String conector = "Where";

StringBuilder myQuery = new StringBuilder();
myQuery.append("SELECT r FROM Writings r ");  

if(page.getPagePosition() != null)  {
     myQuery.append(conector + " r.pagePosition =? ";
     conector = "and";
     parameters.add(page.getPagePosition());
}

if(page.getPageType() != null)  {
     myQuery.append(conector + " r.pageType =? ";
     conector = "and";
     parameters.add(page.getPageType());
}

if(page.getLanguageType() != null)  {
     myQuery.append(conector + " r.languageType =? ";
     conector = "and";
     parameters.add(page.getLanguageType());
}

Query query = em.createQuery(myQuery.toString());  

for (int i=0;i<parameters.size();i++){
     query.setParameter(i+1, parameters.get(i));
}