Gebruik van JPA-queryparameters

1. Inleiding

Query's maken met JPA is niet moeilijk; Soms vergeten we echter simpele dingen die een enorm verschil maken.

Een van deze dingen zijn de JPA-queryparameters, en dit is waar we het over gaan hebben.

2. Wat zijn queryparameters?

Laten we beginnen met uit te leggen wat queryparameters zijn.

Queryparameters zijn een manier om geparametriseerde query's te maken en uit te voeren. Dus in plaats van:

SELECTEER * VAN medewerkers e WAAR e.emp_number = '123';

We zouden doen:

SELECTEER * VAN medewerkers e WAAR e.emp_number =?;

Door een door JDBC voorbereide instructie te gebruiken, moeten we de parameter instellen voordat de query wordt uitgevoerd:

pStatement.setString (1, 123);

3. Waarom moeten we queryparameters gebruiken?

In plaats van queryparameters te gebruiken, hadden we ervoor kunnen kiezen om literals te gebruiken, maar het is niet de aanbevolen manier om het te doen, zoals we nu zullen zien.

Laten we de vorige query herschrijven om medewerkers langs te krijgen emp_number met behulp van de JPA API, maar in plaats van een parameter te gebruiken, gebruiken we een letterlijke waarde, zodat we de situatie duidelijk kunnen illustreren:

String empNumber = "A123"; TypedQuery query = em.createQuery ("SELECTEER e UIT Werknemer e WAAR e.empNumber = '" + empNumber + "'", Werknemer.klasse); Medewerker werknemer = query.getSingleResult ();

Deze aanpak heeft enkele nadelen:

  • Het insluiten van parameters introduceert een beveiligingsrisico dat ons kwetsbaar maakt voor JPQL-injectie-aanvallen. In plaats van de verwachte waarde kan een aanvaller een onverwachte en mogelijk gevaarlijke JPQL-expressie injecteren
  • Afhankelijk van de JPA-implementatie die we gebruiken en de heuristiek van onze applicatie, kan de querycache uitgeput raken. Er kan een nieuwe query worden gemaakt, gecompileerd en in de cache worden opgeslagen elke keer dat we deze gebruiken met elke nieuwe waarde / parameter. Het zal op zijn minst niet efficiënt zijn en het kan ook tot onverwachte gebeurtenissen leiden Onvoldoende geheugen fout

4. Parameters voor JPA-query's

Net als bij door JDBC voorbereide instructieparameters, specificeert JPA twee verschillende manieren om geparametriseerde query's te schrijven met behulp van:

  • Positionele parameters
  • Benoemde parameters

We kunnen positionele of benoemde parameters gebruiken, maar we mogen ze niet in dezelfde query combineren.

4.1. Positionele parameters

Het gebruik van positionele parameters is een manier om de eerder genoemde problemen te vermijden.

Laten we eens kijken hoe we zo'n query zouden schrijven met behulp van positionele parameters:

TypedQuery query = em.createQuery ("SELECTEER e VAN Werknemer e WAAR e.empNumber =? 1", Werknemer.klasse); String empNumber = "A123"; Werknemer werknemer = query.setParameter (1, empNumber) .getSingleResult ();

Zoals we in het vorige voorbeeld hebben gezien, we declareren deze parameters binnen de query door een vraagteken te typen gevolgd door een positief geheel getal. We beginnen met 1 en ga vooruit, waarbij je het elke keer met één verhoogt.

We kunnen dezelfde parameter meer dan eens gebruiken binnen dezelfde query, waardoor deze parameters meer lijken op benoemde parameters.

Parameternummering is een zeer nuttige functie omdat het de bruikbaarheid, leesbaarheid en onderhoud verbetert.

Dat is het vermelden waard positionele parameterbinding wordt ook ondersteund door native SQL-query's.

4.2. Door collectie gewaardeerde positionele parameters

Zoals eerder vermeld, kunnen we ook parameters met verzamelingswaarde gebruiken:

TypedQuery query = entityManager.createQuery ("SELECTEER e VAN Werknemer e WAAR e.empNumber IN (? 1)", Werknemer.class); Lijst empNumbers = Arrays.asList ("A123", "A124"); Lijst werknemers = query.setParameter (1, empNumbers) .getResultList ();

4.3. Benoemde parameters

Benoemde parameters lijken veel op positionele parameters; Door ze te gebruiken, maken we de parameters echter explicieter en wordt de zoekopdracht beter leesbaar:

TypedQuery query = em.createQuery ("SELECTEER e UIT Werknemer e WAAR e.empNumber =: nummer", Werknemer.klasse); String empNumber = "A123"; Werknemer werknemer = query.setParameter ("nummer", empNumber) .getSingleResult ();

De vorige voorbeeldquery is dezelfde als de eerste, maar we hebben deze gebruikt :aantal, een benoemde parameter, in plaats van ?1.

We kunnen zien dat we de parameter hebben gedeclareerd met een dubbele punt gevolgd door een string-ID (JPQL-ID) die een tijdelijke aanduiding is voor de werkelijke waarde die tijdens runtime wordt ingesteld. Voordat de query wordt uitgevoerd, moeten de parameter of parameters worden ingesteld door de setParameter methode.

Een interessant ding om op te merken is dat de TypedQuery ondersteunt methodeketting wat erg handig wordt als er meerdere parameters moeten worden ingesteld.

Laten we doorgaan en een variatie op de vorige query maken met behulp van twee benoemde parameters om de methodeketting te illustreren:

TypedQuery query = em.createQuery ("SELECTEER e UIT Werknemer e WAAR e.name =: naam EN e.age =: empAge", Werknemer.klasse); String empName = "John Doe"; int empAge = 55; Lijst werknemers = query .setParameter ("naam", empName) .setParameter ("empAge", empAge) .getResultList ();

Hier halen we alle medewerkers op met de opgegeven naam en leeftijd. Zoals we duidelijk zien en mag verwachten, kunnen we queries bouwen met meerdere parameters en zo vaak als nodig is.

Als we om de een of andere reden dezelfde parameter vaak binnen dezelfde query moeten gebruiken, hoeven we deze slechts één keer in te stellen door de "setParameterā€¯Methode. Tijdens runtime zullen de opgegeven waarden elk exemplaar van de parameter vervangen.

Ten slotte is het de moeite waard om dat te vermelden de Java Persistence API-specificatie schrijft niet voor dat benoemde parameters worden ondersteund door native queries. Zelfs als sommige implementaties zoals Hibernate het ondersteunen, moeten we er rekening mee houden dat als we het gebruiken, de query niet zo draagbaar zal zijn.

4.4. Benoemde parameters met verzamelingswaarde

Laten we voor de duidelijkheid ook demonstreren hoe dit werkt met parameters met verzamelingswaarde:

TypedQuery query = entityManager.createQuery ("SELECTEER e VAN Werknemer e WAAR e.empNumber IN (: nummers)", Werknemer.klasse); Lijst empNumbers = Arrays.asList ("A123", "A124"); Lijst werknemers = query.setParameter ("numbers", empNumbers) .getResultList ();

Zoals we kunnen zien, werkt het op dezelfde manier als positionele parameters.

5. Parameters voor criteriaquery's

Een JPA-query kan worden gebouwd met behulp van de JPA Criteria API, die in de officiële documentatie van Hibernate gedetailleerd wordt uitgelegd.

In dit type query vertegenwoordigen we parameters door objecten te gebruiken in plaats van namen of indices.

Laten we dezelfde query opnieuw bouwen, maar deze keer met behulp van de Criteria API om te demonstreren hoe queryparameters moeten worden afgehandeld bij het omgaan met CriteriaQuery:

CriteriaBuilder cb = em.getCriteriaBuilder (); CriteriaQuery cQuery = cb.createQuery (Employee.class); Root 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); String empNumber = "A123"; query.setParameter (paramEmpNumber, empNumber); Medewerker werknemer = query.getResultList ();

Voor dit type query is de mechanica van de parameter een beetje anders omdat we een parameterobject gebruiken, maar in wezen is er geen verschil.

In het vorige voorbeeld kunnen we het gebruik van de Werknemer_ klasse. We hebben deze klasse gegenereerd met de Hibernate-metamodel-generator. Deze componenten maken deel uit van het statische JPA-metamodel, waarmee criteriaquery's op een sterk getypeerde manier kunnen worden gebouwd.

6. Conclusie

In dit artikel hebben we ons gericht op de werking van het maken van query's met behulp van JPA-queryparameters of invoerparameters.

We hebben geleerd dat we twee soorten queryparameters hebben, positioneel en benoemd. Het is aan ons welke het beste bij onze doelstellingen past.

Het is ook vermeldenswaard dat alle queryparameters een enkele waarde moeten hebben, met uitzondering van in uitdrukkingen. Voor in expressies, kunnen we invoerparameters met verzamelingswaarde gebruiken, zoals arrays of Lijsts zoals getoond in de vorige voorbeelden.

De broncode van deze tutorial is, zoals gewoonlijk, beschikbaar op GitHub.