2013-05-08 10 views
8

Quando si utilizza l'API dei criteri JPA, qual è il vantaggio dell'utilizzo di ParameterExpression su una variabile direttamente? Per esempio. quando voglio cercare un cliente per nome in una variabile String, potrei scrivere qualcosa di simile autilizzando ParameterExpression rispetto a una variabile nell'API dei criteri JPA

private List<Customer> findCustomer(String name) { 
    CriteriaBuilder cb = em.getCriteriaBuilder(); 
    CriteriaQuery<Customer> criteriaQuery = cb.createQuery(Customer.class); 
    Root<Customer> customer = criteriaQuery.from(Customer.class); 
    criteriaQuery.select(customer).where(cb.equal(customer.get("name"), name)); 
    return em.createQuery(criteriaQuery).getResultList(); 
} 

Con parametri questo diventa:

private List<Customer> findCustomerWithParam(String name) { 
    CriteriaBuilder cb = em.getCriteriaBuilder(); 
    CriteriaQuery<Customer> criteriaQuery = cb.createQuery(Customer.class); 
    Root<Customer> customer = criteriaQuery.from(Customer.class); 
    ParameterExpression<String> nameParameter = cb.parameter(String.class, "name"); 
    criteriaQuery.select(customer).where(cb.equal(customer.get("name"), nameParameter)); 
    return em.createQuery(criteriaQuery).setParameter("name", name).getResultList(); 
} 

Per concisione io preferirei il primo modo, soprattutto quando la query si allunga con parametri opzionali. Ci sono degli svantaggi nell'utilizzare parametri come questo, come l'iniezione SQL?

+0

Non posso parlare per JPA in generale , ma ho scoperto che OpenJPA converte internamente una query Criteria in JPQL e questa può essere stampata usando la funzionalità specifica di OpenJPA (vedi htt p: //openjpa.apache.org/builds/2.1.1/apache-openjpa/docs/ch13s03.html). La prima query si traduce in "SELECT c FROM Customer c WHERE c.name = 'test Customer'". Ciò significa che NON usa un parametro, quindi se questo viene ulteriormente tradotto in SQL, l'istruzione preparata corrispondente NON utilizzerà un parametro. La seconda versione si traduce in JPQL "SELECT c FROM Customer c WHERE c.name =: name", quindi userò i parametri. –

+0

Dopo alcuni test ho scoperto che scrivere la stessa query con JPQL e usare il nome "'OR' x '=' x" inietta JPQL. Quando si utilizza l'API dei criteri, il JPQL generato che OpenJPA registra sembra esattamente lo stesso. Tuttavia, l'attuale SQL registrato da OpenJPA utilizza quindi un'istruzione preparata con un parametro di valore "'OR' x '=' x" invece di '' nel caso JPQL.Ciò significa che l'iniezione SQL non funziona qui! Sfortunatamente non ho idea di quanto sia affidabile. Sembra che questa sia una funzionalità non documentata. –

+0

Suggerimento: ho appena provato http://www.querydsl.com/ e la sintassi è molto più concisa e leggibile. Sembra difendersi dall'iniezione sql usando i parametri di default. –

risposta

0

Quando si utilizza un parametro, probabilmente (a seconda dell'implementazione JPA, datastore in uso e driver JDBC), l'SQL verrà ottimizzato su un parametro JDBC, quindi se si esegue la stessa operazione con un valore diverso del parametro utilizza il parametro stessa dichiarazione JDBC.

L'iniezione SQL è sempre a carico dello sviluppatore se convalida qualche input utente che viene utilizzato come parametro.

+0

Questa è una buona cosa sapere. Preferisco il codice più semplice sopra il codice ottimizzato finché non viene identificato come un problema. Il problema che ho con l'utilizzo dei parametri per i criteri facoltativi nella clausola where è che è necessario avere codice ripetuto come "if (optionalParameter! = Null)" per dichiarare e impostare il parametro. La tua seconda risposta mi confonde. Ho sempre pensato che i parametri (fino ai possibili bug di implementazione) fossero garantiti per non soffrire di attacchi SQL injection e mi stavo chiedendo se il mio modo più semplice potesse soffrire di SQL injection. A proposito, sto usando OpenJPA. –

0

è possibile utilizzare ParameterExpression come questo: supporre che hai qualche filtro d'ingresso, un esempio potrebbe essere questo:

  • nella query è necessario controllare il valore di un codice fiscale.

cominciamo: prima di tutto creare criteriaQuery e criteriaBuilder e radice

 CriteriaBuilder cb = _em.getCriteriaBuilder(); 
     CriteriaQuery<Tuple> cq = cb.createTupleQuery(); 
     Root<RootEntity> soggettoRoot = cq.from(RootEntity.class); 

1) inizialize un predicateList (uso per clausola where) e una paramlist (uso per param)

Map<ParameterExpression,String> paramList = new HashMap(); 
List<Predicate> predicateList = new ArrayList<>(); 

) controllare se l'input è n ull e creare predicateList e param

if(input.getFilterCF() != null){ 
      //create ParameterExpression 
      ParameterExpression<String> cf = cb.parameter(String.class); 


      //if like clause 
      predicateList.add(cb.like(root.<String>get("cf"), cf)); 
      paramList.put(cf , input.getFilterCF() + "%"); 

      //if equals clause 
      //predicateList.add(cb.equal(root.get("cf"), cf)); 
      //paramList.put(cf,input.getFilterCF()()); 
     } 

) creano la clausola where

cq.where(cb.and(predicateList.toArray(new Predicate[predicateList.size()]))); 
TypedQuery<Tuple> q = _em.createQuery(cq); 

) valore impostato param

 for(Map.Entry<ParameterExpression,String> entry : paramList.entrySet()) 
     { 
      q.setParameter(entry.getKey(), entry.getValue()); 
     } 
+0

L'iniezione SQL sarà possibile se i parametri non vengono utilizzati? – Alex78191

+0

sì, sarà possibile –

Problemi correlati