Dokumen ini berfokus pada penggunaan framework persistensi Java Data Object (JDO) untuk kueri App Engine Datastore. Untuk mengetahui informasi umum selengkapnya tentang kueri, lihat halaman Kueri Datastore utama.
Kueri mengambil entity dari Datastore yang memenuhi serangkaian kondisi tertentu. Kueri beroperasi di entity jenis tertentu; kueri ini dapat menentukan filter untuk nilai properti, kunci, dan ancestor entity, dan dapat menampilkan nol entity atau beberapa entity sebagai hasil. Kueri juga dapat menentukan tata urutan untuk mengurutkan hasil berdasarkan nilai propertinya. Hasilnya mencakup semua entity yang memiliki setidaknya satu nilai (mungkin null) untuk setiap properti yang disebutkan dalam filter dan tata urutan, dan yang nilai propertinya memenuhi semua kriteria filter yang ditentukan. Kueri dapat menampilkan seluruh entity, proyeksi entitas, atau hanya entity kunci.
Kueri tipikal mencakup hal berikut:
- Jenis entity tempat kueri diterapkan
- Nol atau beberapa filter berdasarkan nilai properti, kunci, dan ancestor entity
- Nol atau beberapa pilah urutan untuk mengurutkan hasil
Catatan: Untuk menghemat memori dan meningkatkan performa, kueri harus, jika memungkinkan, menentukan batas jumlah hasil yang ditampilkan.
Catatan: Mekanisme kueri berbasis indeks mendukung berbagai kueri dan cocok untuk sebagian besar aplikasi. Namun, mekanisme ini tidak mendukung beberapa jenis kueri yang umum digunakan dalam teknologi database lainnya: khususnya, join dan kueri agregat tidak didukung dalam mesin kueri Datastore. Lihat halaman Kueri Datastore untuk mengetahui batasan pada kueri Datastore.
Kueri dengan JDOQL
JDO mencakup bahasa kueri untuk mengambil objek yang memenuhi serangkaian kriteria. Bahasa ini, yang disebut JDOQL, mengacu pada kolom dan class data JDO secara langsung, serta mencakup pemeriksaan jenis untuk parameter dan hasil kueri. JDOQL mirip dengan SQL, tetapi lebih sesuai untuk database berorientasi objek seperti App Engine Datastore. (Implementasi JDO API App Engine tidak mendukung kueri SQL secara langsung.)
Antarmuka
Query
JDO mendukung beberapa gaya panggilan: Anda dapat menentukan kueri lengkap dalam
string, menggunakan sintaksis string JDOQL, atau menentukan sebagian atau semua
bagian dari dengan memanggil metode pada objek Query
. Contoh berikut menunjukkan gaya metode panggilan, dengan satu filter dan satu tata urutan, menggunakan penggantian parameter untuk nilai yang digunakan dalam filter. Nilai argumen yang diteruskan ke metode execute()
objek Query
diganti ke dalam kueri dalam urutan yang ditentukan:
import java.util.List; import javax.jdo.Query; // ... Query q = pm.newQuery(Person.class); q.setFilter("lastName == lastNameParam"); q.setOrdering("height desc"); q.declareParameters("String lastNameParam"); try { List<Person> results = (List<Person>) q.execute("Smith"); if (!results.isEmpty()) { for (Person p : results) { // Process result p } } else { // Handle "no results" case } } finally { q.closeAll(); }
Berikut ini kueri yang sama yang menggunakan sintaks string:
Query q = pm.newQuery("select from Person " + "where lastName == lastNameParam " + "parameters String lastNameParam " + "order by height desc"); List<Person> results = (List<Person>) q.execute("Smith");
Anda dapat mencampur gaya ini untuk menentukan kueri. Contoh:
Query q = pm.newQuery(Person.class, "lastName == lastNameParam order by height desc"); q.declareParameters("String lastNameParam"); List<Person> results = (List<Person>) q.execute("Smith");
Anda dapat menggunakan kembali satu instance Query
dengan nilai berbeda yang diganti untuk parameter dengan memanggil metode execute()
beberapa kali. Setiap panggilan menjalankan kueri dan menampilkan hasilnya sebagai koleksi.
Sintaksis string JDOQL mendukung spesifikasi literal string dan nilai
numerik; semua jenis nilai lain harus menggunakan substitusi parameter. Literal dalam string kueri dapat diapit dengan tanda kutip tunggal ('
) atau tanda kutip ganda ("
). Berikut ini contoh penggunaan literal
string:
Query q = pm.newQuery(Person.class, "lastName == 'Smith' order by height desc");
Filter
Filter properti menentukan
- Nama properti
- Operator perbandingan
- Nilai properti
Query q = pm.newQuery(Person.class); q.setFilter("height <= maxHeight");
Nilai properti harus diberikan oleh aplikasi; ini tidak bisa merujuk ke atau dihitung dari segi properti lainnya. Entity memenuhi filter jika memiliki properti dengan nama tertentu yang nilainya dibandingkan dengan nilai yang ditentukan dalam filter dengan cara yang dijelaskan oleh operator perbandingan.
Operator perbandingan dapat berupa salah satu dari yang berikut:
Operator | Arti |
---|---|
== |
Sama dengan |
< |
Kurang dari |
<= |
Kurang dari atau sama dengan |
> |
Lebih dari |
>= |
Lebih dari atau sama dengan |
!= |
Tidak sama dengan |
Seperti yang dijelaskan di halaman
Kueri utama, kueri satuan tidak dapat menggunakan filter ketidaksetaraan (<
, <=
, >
, >=
,
!=
) di lebih dari satu properti. (Beberapa filter ketidaksetaraan di properti yang sama, seperti pembuatan kueri untuk rentang nilai, diizinkan.) Filter contains()
, yang sesuai dengan
filter IN
dalam SQL, didukung menggunakan sintaksis berikut:
// Query for all persons with lastName equal to Smith or Jones Query q = pm.newQuery(Person.class, ":p.contains(lastName)"); q.execute(Arrays.asList("Smith", "Jones"));
Operator
not-equal (!=
)
sebenarnya menjalankan dua kueri: salah satunya ketika semua filter lain tidak
berubah dan filter
not-equal
diganti dengan filter
less-than (<
)
, dan satunya lagi yang diganti dengan filter
greater-than (>
)
. Hasilnya kemudian digabungkan, secara berurutan. Kueri tidak boleh memiliki lebih dari satu filter
not-equal, dan kueri yang memiliki filter tersebut tidak boleh memiliki filter ketidaksetaraan lainnya.
Operator contains()
juga menjalankan beberapa
kueri: satu untuk setiap item dalam daftar yang ditentukan, dengan semua filter lainnya
tidak berubah dan filter contains()
diganti dengan
filter kesetaraan (==
)
. Hasilnya digabungkan sesuai urutan item dalam daftar. Jika kueri memiliki lebih dari satu filter contains()
, kueri tersebut akan dijalankan seperti beberapa kueri, satu untuk setiap kemungkinan kombinasi nilai yang ada dalam daftar contains()
singkat ini.
Satu kueri yang berisi operator
not-equal (!=
)
atau contains()
dibatasi hingga maksimum 30 subkueri.
Untuk informasi selengkapnya tentang bagaimana kueri
!=
dan contains()
diterjemahkan menjadi beberapa kueri dalam framework JDO/JPA, lihat artikel
Kueri dengan filter != dan IN.
Dalam sintaksis string JDOQL, Anda dapat memisahkan beberapa filter dengan operator (logika "and") &&
dan (logika "or") ||
:
q.setFilter("lastName == 'Smith' && height < maxHeight");
Pernyataan negasi ("tidak" logis) tidak didukung. Perlu diingat juga, bahwa operator ||
hanya dapat digunakan jika filter yang dipisahkan semuanya memiliki nama properti yang sama (yaitu, ketika operator dapat digabungkan menjadi satu filter contains()
):
// Legal: all filters separated by || are on the same property Query q = pm.newQuery(Person.class, "(lastName == 'Smith' || lastName == 'Jones')" + " && firstName == 'Harold'"); // Not legal: filters separated by || are on different properties Query q = pm.newQuery(Person.class, "lastName == 'Smith' || firstName == 'Harold'");
Tata Urutan
Tata urutan kueri menentukan
- Nama properti
- Arah pengurutan (menaik atau menurun)
Contoh:
Contoh:
// Order alphabetically by last name: Query q = pm.newQuery(Person.class); q.setOrdering("lastName asc"); // Order by height, tallest to shortest: Query q = pm.newQuery(Person.class); q.setOrdering("height desc");
Jika kueri menyertakan beberapa tata urutan, maka akan diterapkan dalam urutan yang ditentukan. Contoh berikut mengurutkan nama belakang secara menaik, lalu mengurutkan tinggi secara menurun:
Query q = pm.newQuery(Person.class); q.setOrdering("lastName asc, height desc");
Jika tidak ada tata urutan yang ditentukan, hasilnya akan ditampilkan sesuai urutan pengambilannya dari Datastore.
Catatan: Karena cara Datastore mengeksekusi kueri, jika kueri menentukan filter ketidaksetaraan di properti dan tata urutan di properti lain, properti yang digunakan dalam filter ketidaksetaraan harus diurutkan sebelum properti lainnya.
Rentang
Kueri dapat menentukan rentang hasil yang akan ditampilkan ke aplikasi. Rentang menunjukkan hasil mana dalam kumpulan hasil lengkap yang harus
menjadi yang pertama dan terakhir ditampilkan. Hasil diidentifikasi berdasarkan indeks numeriknya,
dengan 0
yang menunjukkan hasil pertama dalam kumpulan. Misalnya, rentang
5,
10
akan menampilkan hasil ke-6 hingga ke-10:
q.setRange(5, 10);
Catatan: Penggunaan rentang dapat memengaruhi performa, karena Datastore harus mengambil lalu menghapus semua hasil sebelum offset awal. Misalnya, kueri dengan rentang 5,
10
mengambil sepuluh hasil dari Datastore, menghapus lima hasil pertama, dan menampilkan lima hasil sisanya ke aplikasi.
Kueri Berbasis Kunci
Kunci entity dapat menjadi subjek filter kueri atau tata urutan. Datastore mempertimbangkan nilai kunci lengkap untuk kueri tersebut, termasuk jalur ancestor entity, jenis, dan string nama kunci yang ditetapkan aplikasi atau ID numerik yang ditetapkan sistem. Karena kunci ini unik di semua entity dalam sistem, kueri kunci memudahkan pengambilan entity dari jenis tertentu dalam batch, seperti untuk batch dump konten Datastore. Tidak seperti rentang JDOQL, rentang ini berfungsi secara efisien untuk sejumlah entity.
Saat membandingkan ketidaksetaraan, kunci diurutkan berdasarkan kriteria berikut secara berurutan:
- Jalur ancestor
- Jenis entity
- ID (nama kunci atau ID numerik)
Elemen jalur ancestor dibandingkan dengan cara serupa: berdasarkan jenis (string), lalu menurut nama kunci atau ID numerik. Jenis dan nama kunci merupakan string dan diurutkan berdasarkan nilai byte; ID numerik adalah bilangan bulat dan diurutkan secara numerik. Jika entity dengan induk dan jenis yang sama menggunakan campuran string nama kunci dan ID numerik, entity dengan ID numerik akan mendahului nama kunci tersebut.
Di JDO, Anda merujuk ke kunci entity dalam kueri menggunakan kolom kunci utama objek. Untuk menggunakan kunci sebagai filter kueri, tentukan jenis parameter
Key
ke
metode
declareParameters()
. Kode berikut menemukan semua entity Person
dengan makanan
favorit yang ditentukan, dengan asumsi
hubungan one-to-one yang tidak dimiliki
antara Person
dan Food
:
Food chocolate = /*...*/; Query q = pm.newQuery(Person.class); q.setFilter("favoriteFood == favoriteFoodParam"); q.declareParameters(Key.class.getName() + " favoriteFoodParam"); List<Person> chocolateLovers = (List<Person>) q.execute(chocolate.getKey());
Kueri khusus kunci hanya menampilkan kunci entity hasil, bukan entity itu sendiri, dengan latensi dan biaya yang lebih rendah daripada mengambil seluruh entity:
Query q = pm.newQuery("select id from " + Person.class.getName()); List<String> ids = (List<String>) q.execute();
Sering kali lebih ekonomis untuk melakukan kueri khusus kunci terlebih dahulu, lalu mengambil subset entity dari hasil, daripada menjalankan kueri umum yang dapat mengambil lebih banyak entity daripada yang sebenarnya Anda butuhkan.
Jangkauan
Ekstensi JDO mewakili setiap objek dalam Datastore untuk class tertentu. Anda membuatnya dengan meneruskan class yang diinginkan ke metode
getExtent()
Pengelola Persistensi. Antarmuka
Extent
memperluas antarmuka
Iterable
untuk mengakses hasil, mengambilnya dalam batch sesuai kebutuhan. Setelah selesai mengakses hasilnya, panggil metode closeAll()
ekstensi.
Contoh berikut melakukan iterasi pada setiap objek Person
di Datastore:
import java.util.Iterator; import javax.jdo.Extent; // ... Extent<Person> extent = pm.getExtent(Person.class, false); for (Person p : extent) { // ... } extent.closeAll();
Menghapus Entitas berdasarkan Kueri
Jika Anda mengeluarkan kueri dengan tujuan menghapus semua entity yang cocok dengan filter kueri, Anda dapat sedikit menghemat waktu coding menggunakan fitur "hapus berdasarkan kueri" di JDO. Perintah berikut menghapus semua orang yang melebihi ketinggian tertentu:
Query q = pm.newQuery(Person.class); q.setFilter("height > maxHeightParam"); q.declareParameters("int maxHeightParam"); q.deletePersistentAll(maxHeight);
Anda akan melihat satu-satunya perbedaan di sini adalah kita memanggil
q.deletePersistentAll()
,
bukan
q.execute()
.
Semua aturan dan pembatasan filter, tata urutan, dan indeks yang dijelaskan di atas berlaku untuk kueri, baik Anda memilih atau menghapus kumpulan hasil.
Namun, perhatikan bahwa seolah-olah Anda telah menghapus entity Person
ini dengan
pm.deletePersistent()
, setiap turunan dependen entity yang dihapus oleh kueri juga akan dihapus. Untuk informasi selengkapnya tentang turunan dependen, lihat
halaman Hubungan Entity di JDO.
Query Cursor
Di JDO, Anda dapat menggunakan ekstensi dan class JDOCursorHelper
untuk menggunakan kursor dengan kueri JDO. Cursor berfungsi saat melakukan fetching hasil sebagai daftar atau menggunakan iterator. Untuk mendapatkan cursor, teruskan daftar hasil atau iterator ke metode statis JDOCursorHelper.getCursor()
:
import java.util.HashMap; import java.util.List; import java.util.Map; import javax.jdo.Query; import com.google.appengine.api.datastore.Cursor; import org.datanucleus.store.appengine.query.JDOCursorHelper; Query q = pm.newQuery(Person.class); q.setRange(0, 20); List<Person> results = (List<Person>) q.execute(); // Use the first 20 results Cursor cursor = JDOCursorHelper.getCursor(results); String cursorString = cursor.toWebSafeString(); // Store the cursorString // ... // Query q = the same query that produced the cursor // String cursorString = the string from storage Cursor cursor = Cursor.fromWebSafeString(cursorString); Map<String, Object> extensionMap = new HashMap<String, Object>(); extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor); q.setExtensions(extensionMap); q.setRange(0, 20); List<Person> results = (List<Person>) q.execute(); // Use the next 20 results
Untuk mengetahui informasi selengkapnya tentang query cursor, lihat halaman Kueri Datastore.
Kebijakan Pembacaan Datastore dan Batas Waktu Panggilan
Anda dapat menetapkan kebijakan baca (konsistensi kuat vs. konsistensi akhir) dan batas waktu panggilan Datastore untuk semua panggilan yang dilakukan oleh instance PersistenceManager
menggunakan konfigurasi. Anda juga dapat mengganti opsi ini untuk setiap objek Query
. (Namun,
perhatikan bahwa tidak ada cara untuk mengganti konfigurasi opsi ini
saat Anda mengambil entity berdasarkan kunci.)
Saat konsistensi akhir dipilih untuk kueri Datastore, indeks yang digunakan kueri untuk mengumpulkan hasil juga diakses dengan konsistensi tertunda. Kueri terkadang menampilkan entity yang tidak cocok dengan kriteria kueri—meskipun hal ini juga berlaku jika kebijakan baca sangat konsisten. (Jika kueri menggunakan filter ancestor, Anda dapat menggunakan transaksi untuk memastikan kumpulan hasil yang konsisten.)
Untuk mengganti kebijakan baca satu kueri, panggil metode
addExtension()
-nya:
Query q = pm.newQuery(Person.class); q.addExtension("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL");
Nilai yang mungkin adalah "EVENTUAL"
dan "STRONG"
; defaultnya adalah "STRONG"
, kecuali jika disetel lain dalam file konfigurasi jdoconfig.xml
.
Untuk mengganti batas waktu panggilan Datastore untuk satu kueri, panggil metode
setDatastoreReadTimeoutMillis()
-nya:
q.setDatastoreReadTimeoutMillis(3000);
Nilainya adalah interval waktu yang dinyatakan dalam milidetik.