1. Pengenalan
Membina pertanyaan menggunakan JPA tidak sukar; namun, kita kadang-kadang melupakan perkara-perkara sederhana yang membuat perbezaan besar.
Salah satu perkara ini adalah parameter pertanyaan JPA, dan inilah yang akan kita bincangkan.
2. Apakah Parameter Pertanyaan?
Mari mulakan dengan menerangkan parameter pertanyaan.
Parameter pertanyaan adalah cara untuk membina dan melaksanakan pertanyaan parameter. Jadi, bukannya:
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 menetapkan parameter sebelum menjalankan pertanyaan:
pStatement.setString(1, 123);
3. Mengapa Kita Perlu Menggunakan Parameter Pertanyaan?
Daripada menggunakan parameter pertanyaan, kita boleh memilih untuk menggunakan literal, namun bukan cara yang disyorkan untuk melakukannya, seperti yang akan kita lihat sekarang.
Mari tulis semula pertanyaan sebelumnya untuk mendapatkan pekerja dengan emp_number menggunakan JPA API, tetapi bukannya 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 mempunyai beberapa kelemahan:
- Parameter penyisipan memperkenalkan risiko keselamatan yang menjadikan kita rentan terhadap serangan suntikan JPQL. Daripada nilai yang diharapkan, penyerang boleh menyuntikkan ungkapan JPQL yang tidak dijangka dan mungkin berbahaya
- Bergantung pada pelaksanaan JPA yang kami gunakan dan heuristik aplikasi kami, cache pertanyaan mungkin akan habis. Pertanyaan baru mungkin dibuat, disusun dan disimpan dalam cache setiap kali kita menggunakannya dengan setiap nilai / parameter baru. Sekurang-kurangnya, ia tidak akan cekap dan mungkin juga menyebabkan OutOfMemoryError yang tidak dijangka
4. Parameter Pertanyaan JPA
Mirip dengan parameter penyataan yang disiapkan JDBC, JPA menentukan dua cara yang berbeza untuk menulis pertanyaan parameter dengan menggunakan:
- Parameter kedudukan
- Parameter dinamakan
Kami mungkin menggunakan parameter kedudukan atau nama tetapi kami tidak boleh mencampurkannya dalam pertanyaan yang sama.
4.1. Parameter Posisi
Menggunakan parameter positional adalah salah satu cara untuk mengelakkan masalah yang disebutkan sebelumnya.
Mari kita lihat bagaimana kita menulis pertanyaan sedemikian dengan bantuan parameter kedudukan:
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, kita menyatakan parameter ini dalam pertanyaan dengan menaip tanda tanya diikuti dengan bilangan bulat positif . Kita akan mulakan dengan 1 dan terus maju, bertambah satu demi satu setiap kali.
Kami mungkin menggunakan parameter yang sama lebih dari sekali dalam pertanyaan yang sama yang menjadikan parameter ini lebih serupa dengan parameter yang dinamakan.
Penomboran parameter adalah ciri yang sangat berguna kerana meningkatkan kebolehgunaan, kebolehbacaan, dan penyelenggaraan.
Perlu disebutkan bahawa pengikatan parameter kedudukan disokong oleh pertanyaan SQL asli juga .
4.2. Parameter Posisi Pengumpulan-Nilai
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 Dinamakan
Parameter yang dinamakan hampir sama dengan parameter kedudukan; namun, dengan menggunakannya, kami membuat parameter lebih jelas dan pertanyaan 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();
Pertanyaan sampel sebelumnya sama dengan yang pertama tetapi kami telah menggunakan : number , parameter bernama, bukan ? 1 .
Kita dapat melihat kita menyatakan parameter dengan titik dua diikuti dengan string pengecam (pengenal JPQL) yang merupakan placeholder untuk nilai sebenar yang akan ditetapkan pada waktu runtime. Sebelum menjalankan pertanyaan, parameter atau parameter harus ditetapkan dengan mengeluarkan metode setParameter .
Satu perkara menarik untuk kata-kata bahawa yang TypedQuery menyokong kaedah chaining yang menjadi sangat berguna apabila pelbagai parameter perlu ditetapkan.
Mari teruskan dan buat variasi pertanyaan sebelumnya menggunakan dua parameter bernama untuk menggambarkan kaedah merantai:
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 pekerja dengan nama dan umur yang diberikan. Seperti yang kita lihat dengan jelas dan yang diharapkan, kita dapat membina pertanyaan dengan pelbagai parameter dan seberapa banyak kejadian yang diperlukan.
Sekiranya untuk beberapa alasan kita perlu menggunakan parameter yang sama berkali-kali dalam pertanyaan yang sama, kita hanya perlu menetapkannya sekali dengan mengeluarkan kaedah " setParameter ". Pada masa runtime, nilai yang ditentukan akan menggantikan setiap kejadian parameter.
Terakhir, perlu disebutkan bahawa spesifikasi Java Persistence API tidak mewajibkan parameter bernama untuk disokong oleh pertanyaan asli . Walaupun beberapa pelaksanaan seperti Hibernate memang menyokongnya, kita perlu mengambil kira bahawa jika kita menggunakannya, pertanyaan tidak akan mudah dibawa.
4.4. Parameter Dinamakan Koleksi-Bernilai
Untuk kejelasan, mari kita tunjukkan bagaimana ini berfungsi 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 kita lihat, ia berfungsi dengan cara yang serupa dengan parameter kedudukan.
5. Parameter Pertanyaan 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. Kesimpulannya
Dalam artikel ini, kami telah memfokuskan pada mekanisme pembuatan pertanyaan dengan menggunakan parameter pertanyaan JPA atau parameter input.
Kami telah mengetahui bahawa kami mempunyai dua jenis parameter pertanyaan, kedudukan dan dinamakan. Terpulang kepada kita mana yang paling sesuai dengan objektif kita.
Perlu juga diperhatikan bahawa semua parameter pertanyaan mesti dinilai satu kecuali dalam ungkapan. Untuk dalam ungkapan, kami boleh menggunakan parameter input koleksi bernilai, seperti tatasusunan atau List seperti yang ditunjukkan dalam contoh sebelumnya.
Kod sumber tutorial ini, seperti biasa, terdapat di GitHub.