Verwendung von JPA-Abfrageparametern

1. Einleitung

Das Erstellen von Abfragen mit JPA ist nicht schwierig. Manchmal vergessen wir jedoch einfache Dinge, die einen großen Unterschied machen.

Eines dieser Dinge sind JPA-Abfrageparameter, und darüber werden wir sprechen.

2. Was sind Abfrageparameter?

Beginnen wir mit der Erläuterung der Abfrageparameter.

Abfrageparameter sind eine Möglichkeit, parametrisierte Abfragen zu erstellen und auszuführen. Also statt:

SELECT * FROM employees e WHERE e.emp_number = '123';

Wir würden tun:

SELECT * FROM employees e WHERE e.emp_number = ?;

Wenn Sie eine von JDBC vorbereitete Anweisung verwenden, müssen Sie den Parameter festlegen, bevor Sie die Abfrage ausführen:

pStatement.setString(1, 123);

3. Warum sollten wir Abfrageparameter verwenden?

Anstatt Abfrageparameter zu verwenden, hätten wir auch Literale verwenden können. Dies ist jedoch nicht die empfohlene Methode, wie wir jetzt sehen werden.

Schreiben wir die vorherige Abfrage neu, um Mitarbeiter mithilfe der JPA-API nach emp_number zu ermitteln. Statt eines Parameters verwenden wir jedoch ein Literal, um die Situation klar darzustellen :

String empNumber = "A123"; TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.empNumber = '" + empNumber + "'", Employee.class); Employee employee = query.getSingleResult();

Dieser Ansatz hat einige Nachteile:

  • Das Einbetten von Parametern birgt ein Sicherheitsrisiko, das uns für JPQL-Injection-Angriffe anfällig macht. Anstelle des erwarteten Werts kann ein Angreifer jeden unerwarteten und möglicherweise gefährlichen JPQL-Ausdruck einfügen
  • Abhängig von der von uns verwendeten JPA-Implementierung und den Heuristiken unserer Anwendung kann der Abfrage-Cache erschöpft sein. Eine neue Abfrage kann jedes Mal erstellt, kompiliert und zwischengespeichert werden, wenn wir sie mit jedem neuen Wert / Parameter verwenden. Zumindest ist es nicht effizient und kann auch zu einem unerwarteten OutOfMemoryError führen

4. JPA-Abfrageparameter

Ähnlich wie bei den von JDBC vorbereiteten Anweisungsparametern gibt JPA zwei verschiedene Möglichkeiten zum Schreiben parametrisierter Abfragen an:

  • Positionsparameter
  • Benannte Parameter

Wir können entweder Positions- oder benannte Parameter verwenden, dürfen diese jedoch nicht in derselben Abfrage mischen.

4.1. Positionsparameter

Die Verwendung von Positionsparametern ist eine Möglichkeit, um die oben aufgeführten Probleme zu vermeiden.

Mal sehen, wie wir eine solche Abfrage mit Hilfe von Positionsparametern schreiben würden:

TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.empNumber = ?1", Employee.class); String empNumber = "A123"; Employee employee = query.setParameter(1, empNumber).getSingleResult();

Wie wir im vorherigen Beispiel gesehen haben, deklarieren wir diese Parameter innerhalb der Abfrage, indem wir ein Fragezeichen gefolgt von einer positiven Ganzzahl eingeben . Wir beginnen mit 1 und gehen weiter, wobei wir jedes Mal um eins erhöhen.

Wir können denselben Parameter innerhalb derselben Abfrage mehrmals verwenden, wodurch diese Parameter den benannten Parametern ähnlicher werden.

Die Parameternummerierung ist eine sehr nützliche Funktion, da sie die Benutzerfreundlichkeit, Lesbarkeit und Wartung verbessert.

Erwähnenswert ist, dass die Bindung von Positionsparametern auch von nativen SQL-Abfragen unterstützt wird .

4.2. Sammlungswert-Positionsparameter

Wie bereits erwähnt, können wir auch Parameter mit Sammlungswerten verwenden:

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

4.3. Benannte Parameter

Benannte Parameter sind Positionsparametern ziemlich ähnlich. Indem wir sie verwenden, machen wir die Parameter expliziter und die Abfrage wird lesbarer:

TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.empNumber = :number" , Employee.class); String empNumber = "A123"; Employee employee = query.setParameter("number", empNumber).getSingleResult();

Die vorherige Beispielabfrage ist dieselbe wie die erste, aber wir haben verwendet : number , einen benannten Parameter anstelle von ? 1 .

Wir können sehen, dass wir den Parameter mit einem Doppelpunkt gefolgt von einem String-Bezeichner (JPQL-Bezeichner) deklariert haben, der ein Platzhalter für den tatsächlichen Wert ist, der zur Laufzeit festgelegt wird. Vor dem Ausführen der Abfrage müssen der oder die Parameter durch Ausgabe der setParameter- Methode festgelegt werden.

Eine interessante Sache zu bemerken ist , dass der TypedQuery Träger Methode Verkettungs , die sehr nützlich wird , wenn mehrere Parameter eingestellt werden müssen.

Lassen Sie uns fortfahren und eine Variation der vorherigen Abfrage mit zwei benannten Parametern erstellen, um die Methodenverkettung zu veranschaulichen:

TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.name = :name AND e.age = :empAge" , Employee.class); String empName = "John Doe"; int empAge = 55; List employees = query .setParameter("name", empName) .setParameter("empAge", empAge) .getResultList();

Hier rufen wir alle Mitarbeiter mit dem angegebenen Namen und Alter ab. Wie wir deutlich sehen und erwarten können, können wir Abfragen mit mehreren Parametern und so vielen Vorkommen wie erforderlich erstellen .

Wenn wir aus irgendeinem Grund denselben Parameter innerhalb derselben Abfrage mehrmals verwenden müssen, müssen wir ihn nur einmal festlegen, indem wir die Methode " setParameter " ausgeben . Zur Laufzeit ersetzen die angegebenen Werte jedes Auftreten des Parameters.

Abschließend ist zu erwähnen, dass die Java Persistence API-Spezifikation keine benannten Parameter vorschreibt, die von nativen Abfragen unterstützt werden sollen . Selbst wenn einige Implementierungen wie Hibernate dies unterstützen, müssen wir berücksichtigen, dass die Abfrage nicht so portabel ist, wenn wir sie verwenden.

4.4. Sammlungsbewertete benannte Parameter

Lassen Sie uns der Klarheit halber auch zeigen, wie dies mit Sammlungsparametern funktioniert:

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

Wie wir sehen können, funktioniert es ähnlich wie Positionsparameter.

5. Parameter für die Abfrage von Kriterien

A JPA query may be built by using the JPA Criteria API, which Hibernate's official documentation explains in great detail.

In this type of query, we represent parameters by using objects instead of names or indices.

Let's build the same query again but this time using the Criteria API to demonstrate how to handle query parameters when dealing with 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); Employee employee = query.getResultList();

For this type of query, the parameter's mechanic is a little bit different since we use a parameter object but in essence, there's no difference.

Within the previous example, we can see the usage of the Employee_ class. We generated this class with the Hibernate metamodel generator. These components are part of the static JPA metamodel, which allows criteria queries to be built in a strongly-typed manner.

6. Fazit

In diesem Artikel haben wir uns auf die Mechanik beim Erstellen von Abfragen mithilfe von JPA-Abfrageparametern oder Eingabeparametern konzentriert.

Wir haben gelernt, dass wir zwei Arten von Abfrageparametern haben, positionell und benannt. Es liegt an uns, welches am besten zu unseren Zielen passt.

Es ist auch erwähnenswert, dass alle Abfrageparameter außer in Ausdrücken einwertig sein müssen . Für in Ausdrücken können wir Sammelwerteingabeparameter, wie Arrays oder verwenden Liste s wie gezeigt in den vorherigen Beispielen.

Der Quellcode dieses Tutorials ist wie üblich auf GitHub verfügbar.