Využitie parametrov dotazu JPA

1. Úvod

Vytváranie dotazov pomocou JPA nie je ťažké; niekedy však zabúdame na jednoduché veci, ktoré majú obrovský rozdiel.

Jednou z týchto vecí sú parametre dotazu JPA, o ktorých si ešte povieme.

2. Čo sú parametre dotazu?

Na úvod si vysvetlíme, čo sú parametre dotazu.

Parametre dopytu sú spôsob, ako vytvoriť a vykonať parametrizované dotazy. Takže namiesto:

VYBERTE * OD zamestnancov e WHERE e.emp_number = '123';

Urobili by sme:

VYBERTE * OD zamestnancov e WHERE e.emp_number =?;

Pomocou príkazu pripraveného na JDBC musíme pred vykonaním dotazu nastaviť parameter:

pStatement.setString (1, 123);

3. Prečo by sme mali používať parametre dotazu?

Namiesto použitia parametrov dotazu sme sa mohli rozhodnúť použiť literály, nie je to však odporúčaný spôsob, ako to uvidíme teraz.

Prepíšme predchádzajúci dopyt, aby sme zamestnancov dostali emp_number pomocou API JPA, ale namiesto použitia parametra použijeme literál, aby sme mohli jasne ilustrovať situáciu:

Reťazec empNumber = "A123"; TypedQuery query = em.createQuery ("SELECT e FROM Employee e WHERE e.empNumber = '" + empNumber + "'", Employee.class); Zamestnanec zamestnanec = query.getSingleResult ();

Tento prístup má niektoré nevýhody:

  • Vloženie parametrov predstavuje bezpečnostné riziko, vďaka ktorému sme zraniteľní voči útokom JPQL Injection. Namiesto očakávanej hodnoty môže útočník vložiť akýkoľvek neočakávaný a pravdepodobne nebezpečný výraz JPQL
  • V závislosti od použitej implementácie JPA a heuristiky našej aplikácie sa môže vyrovnávacia pamäť dotazov vyčerpať. Nový dopyt sa môže vytvoriť, kompilovať a uložiť do medzipamäte zakaždým, keď ho použijeme s každou novou hodnotou / parametrom. Minimálne to nebude efektívne a môže to tiež viesť k neočakávaniu OutOfMemoryError

4. Parametre dotazu JPA

Podobne ako parametre príkazu pripraveného JDBC, JPA špecifikuje dva rôzne spôsoby zápisu parametrizovaných dotazov pomocou:

  • Pozičné parametre
  • Pomenované parametre

Môžeme použiť buď pozičné, alebo pomenované parametre, ale nesmieme ich miešať v rámci toho istého dotazu.

4.1. Pozičné parametre

Použitie pozičných parametrov je jedným zo spôsobov, ako sa vyhnúť vyššie uvedeným problémom uvedeným vyššie.

Pozrime sa, ako by sme taký dopyt napísali pomocou pozičných parametrov:

TypedQuery query = em.createQuery ("SELECT e FROM Employee e WHERE e.empNumber =? 1", Employee.class); Reťazec empNumber = "A123"; Zamestnanec zamestnanec = query.setParameter (1, empNumber) .getSingleResult ();

Ako sme videli v predchádzajúcom príklade, tieto parametre deklarujeme v rámci dotazu zadaním otáznika, za ktorým nasleduje kladné celé číslo. Začneme s 1 a posuňte sa vpred a zakaždým ich zvýšte o jednu.

Rovnaký parameter môžeme v jednom dotaze použiť viackrát, čím sa tieto parametre podobajú menovaným parametrom.

Číslovanie parametrov je veľmi užitočná funkcia, pretože zlepšuje použiteľnosť, čitateľnosť a údržbu.

Za zmienku to stojí viazanie pozičných parametrov podporujú aj natívne dotazy SQL.

4.2. Pozičné parametre zbierky

Ako už bolo uvedené, môžeme použiť aj parametre s hodnotou zbierky:

TypedQuery query = entityManager.createQuery ("SELECT e FROM Employee e WHERE e.empNumber IN (? 1)", Employee.class); Zoznam empNumbers = Arrays.asList ("A123", "A124"); Zoznam zamestnancov = query.setParameter (1, empNumbers) .getResultList ();

4.3. Pomenované parametre

Pomenované parametre sú dosť podobné pozičným parametrom; ich použitím však spresňujeme parametre a dopyt sa stáva čitateľnejším:

TypedQuery query = em.createQuery ("SELECT e FROM Employee e WHERE e.empNumber =: number", Employee.class); Reťazec empNumber = "A123"; Zamestnanec zamestnanec = query.setParameter ("number", empNumber) .getSingleResult ();

Predchádzajúci ukážkový dopyt je rovnaký ako prvý, ale použili sme ho : číslo, pomenovaný parameter, namiesto ?1.

Vidíme, že sme parameter deklarovali dvojbodkou, za ktorou nasleduje identifikátor reťazca (identifikátor JPQL), ktorý je zástupným symbolom skutočnej hodnoty, ktorá sa nastaví za behu programu. Pred vykonaním dotazu je potrebné nastaviť parameter alebo parametre vydaním setParameter metóda.

Jedna zaujímavá vec, ktorú treba poznamenať, je the TypedQuery podporuje reťazenie metód čo sa stáva veľmi užitočným, keď je potrebné nastaviť viac parametrov.

Poďme ďalej a vytvoríme variáciu predchádzajúceho dotazu pomocou dvoch pomenovaných parametrov na ilustráciu reťazenia metód:

TypedQuery query = em.createQuery ("SELECT e FROM Employee e WHERE e.name =: name AND e.age =: empAge", Employee.class); Reťazec empName = "John Doe"; int empAge = 55; Zoznam zamestnancov = dopyt .setParameter ("meno", empName) .setParameter ("empAge", empAge) .getResultList ();

Tu získavame všetkých zamestnancov s uvedeným menom a vekom. Ako jasne vidíme a možno očakávať, môžeme vytvárať dotazy pomocou viac parametrov a ich toľko výskytov, koľko sa vyžaduje.

Ak z nejakého dôvodu potrebujeme v rámci toho istého dotazu použiť ten istý parameter viackrát, stačí ho nastaviť raz vydaním „setParameter”Metóda. Počas behu nahradia zadané hodnoty každý výskyt parametra.

Na záver stojí za zmienku špecifikácia API Java Persistence nevyžaduje, aby boli pomenované parametre podporované natívnymi dotazmi. Aj keď ho niektoré implementácie ako Hibernate skutočne podporujú, musíme brať do úvahy, že ak ho skutočne použijeme, dopyt nebude taký prenosný.

4.4. Pomenované parametre s hodnotou do zbierky

Pre názornosť si ukážeme, ako to funguje s parametrami s hodnotou zbierky:

TypedQuery query = entityManager.createQuery ("SELECT e FROM Employee e WHERE e.empNumber IN (: numbers)", Employee.class); Zoznam empNumbers = Arrays.asList ("A123", "A124"); Zoznam zamestnancov = query.setParameter ("numbers", empNumbers) .getResultList ();

Ako vidíme, funguje to podobne ako pozičné parametre.

5. Parametre dotazu na kritériá

Dotaz JPA je možné vytvoriť pomocou rozhrania JPA Criteria API, ktoré veľmi podrobne vysvetľuje oficiálna dokumentácia Hibernate.

V tomto type dotazu reprezentujeme parametre tak, že namiesto názvov alebo indexov použijeme objekty.

Vytvorme znova ten istý dotaz, ale tentokrát s použitím rozhrania Criteria API, aby sme si ukázali, ako zaobchádzať s parametrami dotazu pri riešení CriteriaQuery:

CriteriaBuilder cb = em.getCriteriaBuilder (); CriteriaQuery cQuery = cb.createQuery (Employee.class); Koreň c = cQuery.from (Employee.class); ParameterExpression paramEmpNumber = cb.parameter (String.class); cQuery.select (c) .where (cb.equal (c.get (Employee_.empNumber), paramEmpNumber)); TypedQuery query = em.createQuery (cQuery); Reťazec empNumber = "A123"; query.setParameter (paramEmpNumber, empNumber); Zamestnanec zamestnanec = query.getResultList ();

Pre tento typ dotazu sa mechanika parametra trochu líši, pretože používame objekt parametra, ale v zásade neexistuje žiadny rozdiel.

V predchádzajúcom príklade môžeme vidieť použitie Zamestnanec_ trieda. Túto triedu sme vygenerovali generátorom metamodelov Hibernate. Tieto komponenty sú súčasťou statickej metamodely JPA, ktorá umožňuje zostavovať dotazy kritérií silným spôsobom.

6. Záver

V tomto článku sme sa zamerali na mechaniku vytvárania dotazov pomocou parametrov dotazu JPA alebo vstupných parametrov.

Dozvedeli sme sa, že máme dva typy parametrov dotazu, pozičné a pomenované. Je len na nás, ktorý z nich najlepšie vyhovuje našim cieľom.

Je tiež potrebné poznamenať, že všetky parametre dopytu musia mať jednu hodnotu, okrem v výrazy. Pre v výrazy, môžeme použiť vstupné parametre s hodnotou zbierky, napríklad polia alebo Zoznams, ako je uvedené v predchádzajúcich príkladoch.

Zdrojový kód tohto tutoriálu je ako obvykle k dispozícii na GitHub.