2016-04-23 9 views
9

Sto costruendo una query usando l'API dei criteri JPA. Quando ho creato due predicati di restrizione utilizzando il metodo javax.persistence.criteria.Path#in(Collection<?>), la query SQL generata era leggermente diversa da quella che ho ipotizzato.Perché Hibernate inline L'elenco dei parametri integer è passato alla query dei criteri JPA?

Il primo predicato che è stato creato oltre l'attributo int ha prodotto SQL con tutti gli elementi della raccolta parametri in linea: in (10, 20, 30).

Il secondo predicato che è stato creato su String ha prodotto l'SQL parametrizzato: in (?, ?, ?).

Lasciami spettacolo:

Entity:

@Entity 
public class A { 
    @Id 
    private Integer id; 
    private int intAttr; 
    private String stringAttr; 
    //getter/setters 
} 

Query:

CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 
CriteriaQuery<A> q = cb.createQuery(A.class); 
Root<A> root = q.from(A.class); 
q.where(
    root.get("intAttr").in(Arrays.asList(10, 20, 30)), 
    root.get("stringAttr").in(Arrays.asList("a", "b", "c")) 
); 
entityManager.createQuery(q).getResultList(); 

Log:

select 
    a0_.id as id1_0_, 
    a0_.intAttr as intAttr2_0_, 
    a0_.stringAttr as stringAt3_0_ 
from 
    A a0_ 
where 
    (
     a0_.intAttr in (
      10 , 20 , 30 
     ) 
    ) 
    and (
     a0_.stringAttr in (
      ? , ? , ? 
     ) 
    ) 
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [a] 
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [b] 
org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [c] 

Le mie domande:

  1. Perché gli elementi dell'intero elenco inline direttamente in sql e gli elementi dell'elenco di stringhe vengono gestiti come parametri di istruzione preparati?
  2. Questa funzione è specifica di Hibernate o è garantita da JPA?
  3. Dal punto di vista del DB, quale dei due deve essere preferito?
  4. Questa stringa int-sì-no è inlining in qualche modo correlata all'iniezione di sql?
  5. È in qualche modo correlato alla limitazione del numero di valori nella clausola sql nella clausola che RDMBS può elaborare?
  6. Come scrivere una query di criteri che gestirà l'elenco di parametri Integer nello stesso modo dell'elenco di parametri String.
+0

Sembra davvero una domanda per i forum di Hibernate, non credi? –

risposta

5

letterali Perché sono stringhe legati e numerici non vincolati?

Uno dovrebbe sempre eseguire il binding di parametri per le stringhe (anziché mettere il letterale nella query) per evitare l'iniezione SQL.

Tuttavia, la vera domanda è il motivo per cui inserire il letterale direttamente nella query anziché utilizzare l'associazione. Il motivo originale era:

Così IIRC il problema che mi portano a utilizzare letterali qui aveva a che fare con scala e le operazioni. Significato (di nuovo, iirc) alcuni database necessari per sapere informazioni di tipo per essere in grado di gestire correttamente qualcosa come ... ? +? ..., ecc. Quindi la scelta era quella di avvolgere tutti questi parametri nelle chiamate alla funzione CAST e sperare/pregare che il db implementasse una funzione CAST corretta o usasse letterali. Alla fine ho optato per il percorso letterale perché, beh, questo è quello che l'utente ha chiesto in anticipo. Il blocco delle chiamate alle funzioni limiterà la capacità dei database di sfruttare gli indici nel numero di .

Quale è meglio per il db?

Dipende dal database e dalla query e probabilmente non farà una grande differenza. Ad esempio, Oracle può eseguire determinate partizioni solo quando il valore è letterale, altri database possono eseguire solo determinate ottimizzazioni quando il valore è un parametro associato. Se diventa un problema (ad esempio lo profili e sai che è ciò che ti rallenta) passa semplicemente all'altro metodo.

Si tratta della specifica JPA?

No.

È questo legato al # di valori consentiti in un a dichiarazione?

No.

Posso avere un letterale numerico vincolato anziché inserita direttamente nella query

Sì, ma è piuttosto dettagliata.

CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); 
CriteriaQuery<Foo> query = cb.createQuery(Foo.class); 
Root<Foo> root = query.from(Foo.class); 
ParameterExpression<Long> paramOne = cb.parameter(Long.class); 
Predicate versionPredicate = root.get("bar").in(paramOne); 
query.select(root).where(versionPredicate); 
TypedQuery<Foo> typedQuery = getEntityManager().createQuery(query); 
typedQuery.setParameter(paramOne, 1L); 

Questo utilizzerà il binding di parametri per il lungo.È solo un parametro, ma uno potrebbe facilmente estrapolare da qui per più parametri e metodi di aiuto potrebbero ripulire tutto.

Riferimenti:

La maggior parte del ragionamento è spiegato e discusso in HHH-6280. Il particolare metodo in questione che esegue questo rendering è LiteralExpression.render.

-1

Perché gli elementi dell'intero elenco inline direttamente in sql e gli elementi dell'elenco di stringhe vengono gestiti come parametri di istruzione preparati?

potrebbe essere perché il numero intero che avete usato è primitive ..

provare a utilizzare in questo modo

Arrays.asList (Integer.value (10), Integer.value (20), Integer.value (30))

+2

Questo è già il caso in quanto Java eseguirà automaticamente la casella di testo di Integers. https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#asList (T ...) – Niels

3
  1. Poiché le stringhe possono contenere SQL e gli integri non possono, non è necessario da un aspetto di sicurezza (SQL injection).
  2. Le specifiche JPA non lo specificano esplicitamente come vorreste che fosse. Sembra essere un dettaglio di implementazione.
  3. Parametri di istruzione preparati per i parametri String. Per i parametri int non importa in quanto non possono essere utilizzati in modo improprio dagli hacker.
  4. SI
  5. Si dovrebbe cercare nella documentazione del database specifico che si sta utilizzando. A JPA non importa di queste cose.
  6. Perché? Quali sono i vantaggi?Non cercare di migliorare le cose quando non sai cosa stai migliorando.
1

Concordo pienamente con Niels sul fatto che i parametri di stringa non devono essere inline per impedire l'iniezione SQL.

Ma l'ho controllato con DataNucleus 4.1.9 e Derby db, e con mia sorpresa il registro mostra anche l'inline per le stringhe. Inoltre mostra che DataNucleus implementa la query dei criteri "IN" con combinazioni di condizioni "OR". Probabilmente questo è inferiore a Hibernate e probabilmente un rischio per la sicurezza. Esempio per i possibili pericoli di astrazioni di livello sempre più elevato. Non puoi essere troppo prudente :-).

Il registro:

Begin compiling prepared statement: 
SELECT 'pack.entities.I' AS NUCLEUS_TYPE,DN_THIS.ID,DN_THIS.INTATTR,DN_THIS.STRINGATTR FROM I DN_THIS 
WHERE (((DN_THIS.INTATTR = 10) OR (DN_THIS.INTATTR = 20)) OR (DN_THIS.INTATTR = 30)) 
AND (((DN_THIS.STRINGATTR = 'a') OR (DN_THIS.STRINGATTR = 'b')) OR (DN_THIS.STRINGATTR = 'c')) :End prepared statement 

Tue Apr 26 15:46:01 CEST 2016 Thread[DRDAConnThread_3,5,derby.daemons] 
End compiling prepared statement: 
SELECT 'pack.entities.I' AS NUCLEUS_TYPE,DN_THIS.ID,DN_THIS.INTATTR,DN_THIS.STRINGATTR FROM I DN_THIS 
WHERE (((DN_THIS.INTATTR = 10) OR (DN_THIS.INTATTR = 20)) OR (DN_THIS.INTATTR = 30)) 
AND (((DN_THIS.STRINGATTR = 'a') OR (DN_THIS.STRINGATTR = 'b')) OR (DN_THIS.STRINGATTR = 'c')) :End prepared statement 

Tue Apr 26 15:46:01 CEST 2016 Thread[DRDAConnThread_3,5,derby.daemons] Executing prepared statement: 
SELECT 'pack.entities.I' AS NUCLEUS_TYPE,DN_THIS.ID,DN_THIS.INTATTR,DN_THIS.STRINGATTR FROM I DN_THIS 
WHERE (((DN_THIS.INTATTR = 10) OR (DN_THIS.INTATTR = 20)) OR (DN_THIS.INTATTR = 30)) 
AND (((DN_THIS.STRINGATTR = 'a') OR (DN_THIS.STRINGATTR = 'b')) OR (DN_THIS.STRINGATTR = 'c')) :End prepared statement 
Problemi correlati