Kueri Datastore di JDO

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 entity , atau hanya kunci entity ini.

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
Saat dieksekusi, kueri akan mengambil semua entity dari jenis tertentu yang memenuhi semua filter yang diberikan, yang diurutkan dalam urutan yang ditentukan. Kueri dijalankan sebagai hanya baca.

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 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
Contoh:

Filter propertyFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);
Query q = new Query("Person").setFilter(propertyFilter);
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"));

tidak sama (!=) operator benar-benar melakukan dua kueri: satu ketika semua filter lain tidak berubah dan kueri tidak sama filter diganti dengan kurang-dari (<) , dan satu filter yang diganti dengan lebih besar dari (>) . 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 tidak sama (!=) atau contains() adalah dibatasi hingga maksimum 30 sub kueri.

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:

// Order alphabetically by last name:
Query q1 = new Query("Person").addSort("lastName", SortDirection.ASCENDING);

// Order by height, tallest to shortest:
Query q2 = new Query("Person").addSort("height", SortDirection.DESCENDING);

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 =
    new Query("Person")
        .addSort("lastName", SortDirection.ASCENDING)
        .addSort("height", SortDirection.DESCENDING);
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:

  1. Jalur ancestor
  2. Jenis entity
  3. 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 = new Query("Person").setKeysOnly();
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

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