Penggunaan Parameter Kueri JPA

1. Perkenalan

Membangun kueri menggunakan JPA tidaklah sulit; Namun, terkadang kita melupakan hal-hal sederhana yang membuat perbedaan besar.

Salah satunya adalah parameter kueri JPA, dan inilah yang akan kita bicarakan.

2. Apakah Parameter Kueri itu?

Mari kita mulai dengan menjelaskan apa itu parameter kueri.

Parameter kueri adalah cara untuk membangun dan menjalankan kueri berparameter. Jadi, alih-alih:

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

Kami akan melakukan:

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

Dengan menggunakan pernyataan yang disiapkan JDBC, kita perlu mengatur parameter sebelum menjalankan kueri:

pStatement.setString(1, 123);

3. Mengapa Kita Harus Menggunakan Parameter Kueri?

Alih-alih menggunakan parameter kueri, kami dapat memilih untuk menggunakan literal, itu bukan cara yang disarankan untuk melakukannya, seperti yang akan kita lihat sekarang.

Mari tulis ulang kueri sebelumnya untuk mendapatkan karyawan dengan emp_number menggunakan JPA API, tetapi alih-alih menggunakan parameter, kami akan menggunakan literal sehingga kami dapat menggambarkan situasi dengan jelas:

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

Pendekatan ini memiliki beberapa kekurangan:

  • Parameter penyematan menimbulkan risiko keamanan yang membuat kita rentan terhadap serangan injeksi JPQL. Alih-alih nilai yang diharapkan, penyerang dapat memasukkan ekspresi JPQL yang tidak terduga dan mungkin berbahaya
  • Bergantung pada implementasi JPA yang kita gunakan dan heuristik aplikasi kita, cache kueri mungkin habis. Kueri baru dapat dibuat, dikompilasi, dan di-cache setiap kali kita menggunakannya dengan setiap nilai / parameter baru. Setidaknya, ini tidak akan efisien dan juga dapat menyebabkan OutOfMemoryError yang tidak terduga

4. Parameter Kueri JPA

Mirip dengan parameter pernyataan yang disiapkan JDBC, JPA menentukan dua cara berbeda untuk menulis kueri berparameter dengan menggunakan:

  • Parameter posisi
  • Parameter bernama

Kita dapat menggunakan parameter posisi atau bernama tetapi kita tidak boleh mencampurnya dalam kueri yang sama.

4.1. Parameter Posisi

Menggunakan parameter posisi adalah salah satu cara untuk menghindari masalah yang disebutkan sebelumnya.

Mari kita lihat bagaimana kita akan menulis kueri seperti itu dengan bantuan parameter posisi:

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();

Seperti yang telah kita lihat dalam contoh sebelumnya, kami mendeklarasikan parameter ini dalam kueri dengan mengetikkan tanda tanya diikuti dengan bilangan bulat positif . Kami akan mulai dengan 1 dan maju, menambahnya satu setiap kali.

Kami dapat menggunakan parameter yang sama lebih dari sekali dalam kueri yang sama yang membuat parameter ini lebih mirip dengan parameter bernama.

Penomoran parameter adalah fitur yang sangat berguna karena meningkatkan kegunaan, keterbacaan, dan pemeliharaan.

Perlu disebutkan bahwa pengikatan parameter posisi juga didukung oleh kueri SQL asli .

4.2. Parameter Posisi Berharga Koleksi

Seperti yang dinyatakan sebelumnya, kami juga dapat menggunakan parameter bernilai koleksi:

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. Parameter Bernama

Parameter yang diberi nama sangat mirip dengan parameter posisi; namun, dengan menggunakannya, kami membuat parameter lebih eksplisit dan kueri menjadi lebih mudah dibaca:

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();

Kueri sampel sebelumnya sama dengan yang pertama tetapi kami telah menggunakan : angka , parameter bernama, bukan ? 1 .

Kita dapat melihat kita mendeklarasikan parameter dengan titik dua diikuti dengan pengenal string (pengidentifikasi JPQL) yang merupakan placeholder untuk nilai aktual yang akan ditetapkan saat runtime. Sebelum menjalankan kueri, parameter atau parameter harus disetel dengan mengeluarkan metode setParameter .

Satu hal yang menarik untuk komentar adalah bahwa para TypedQuery mendukung metode chaining yang menjadi sangat berguna ketika beberapa parameter yang harus diatur.

Mari kita lanjutkan dan buat variasi dari kueri sebelumnya menggunakan dua parameter bernama untuk menggambarkan rangkaian metode:

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();

Di sini, kami mengambil semua karyawan dengan nama dan usia tertentu. Seperti yang kita lihat dengan jelas dan mungkin diharapkan, kita bisa membangun kueri dengan beberapa parameter dan sebanyak kemunculannya sesuai kebutuhan.

Jika karena alasan tertentu kita perlu menggunakan parameter yang sama berkali-kali dalam kueri yang sama, kita hanya perlu menyetelnya sekali dengan mengeluarkan metode " setParameter ". Saat runtime, nilai yang ditentukan akan menggantikan setiap kemunculan parameter.

Terakhir, perlu disebutkan bahwa spesifikasi Java Persistence API tidak mengharuskan parameter bernama didukung oleh kueri asli . Bahkan ketika beberapa implementasi seperti Hibernate mendukungnya, kita perlu memperhitungkan bahwa jika kita menggunakannya, kueri tidak akan portabel.

4.4. Parameter Bernama Collection-Valued

Untuk kejelasan, mari kita juga mendemonstrasikan bagaimana ini bekerja dengan parameter bernilai koleksi:

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();

Seperti yang bisa kita lihat, ini bekerja dengan cara yang mirip dengan parameter posisi.

5. Parameter Query Kriteria

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. Kesimpulan

Di artikel ini, kami telah berfokus pada mekanisme pembuatan kueri dengan menggunakan parameter kueri atau parameter input JPA.

Kami telah mempelajari bahwa kami memiliki dua jenis parameter kueri, posisi dan nama. Terserah kita mana yang paling sesuai dengan tujuan kita.

Perlu juga diperhatikan bahwa semua parameter kueri harus bernilai tunggal kecuali dalam ekspresi. Untuk di ekspresi, kita dapat menggunakan parameter masukan koleksi bernilai, seperti array atau Daftar s seperti yang ditunjukkan dalam contoh sebelumnya.

Kode sumber dari tutorial ini, seperti biasa, tersedia di GitHub.