CRUD - 読み取り - Java SDK
項目一覧
読み取り操作
Realm に保存したデータを読み取ることができます。 Realm SDK 全体の標準データ アクセス パターンは、オブジェクトをこの順序で検索、フィルタリング、ソートすることです。 アプリが大きくなり、クエリが複雑になるにつれて Realm から最高のパフォーマンスを得るには、Realm の読み取り特性を確実に理解するためにアプリのデータ アクセス パターンを設計します。
読み取り特性
Realm での読み取りに関する 3 つの主要な特性に基づいてアプリのデータ アクセス パターンを設計すると、データをできるだけ効率的に読み取ることができます。
結果はコピーではない
クエリへの結果はデータのコピーではありません。クエリの結果を変更すると、ディスク上のデータが直接変更されます。 このメモリ マッピングは、結果がライブであることも意味します。つまり、ディスクの現在の状態を常に反映します。
結果は遅延
Realm は、結果にアクセスするまでクエリの実行を延期します。 中間状態を処理するために余計な作業を必要とせずに、複数のフィルターやソート操作を連鎖させることができます。
参照は保持されます
Realm のオブジェクトモデルの利点の 1 つは、Realm がオブジェクトのすべての関係を直接参照として自動的に保持することであるため、クエリの結果から関係のグラフを直接走査できることです。
直接参照(ポインター)を使用すると、参照を介して関連オブジェクトのプロパティに直接アクセスできます。
他のデータベースでは通常、オブジェクトを直接操作する必要がある場合、データベース ストレージからアプリケーション メモリにオブジェクトがコピーされます。 Because application objects contain direct references, you are left with a choice: copy the object referred to by each direct reference out of the database in case it's needed, or just copy the foreign key for each object and query for the object with that key if it's accessed. 参照されたオブジェクトをアプリケーション メモリにコピーすることを選択すると、アクセスされていないオブジェクトの多くのリソースを使用することができますが、外部キーのみをコピーすることを選択した場合は、参照されたオブジェクトの検索によってアプリケーションが遅くなる可能性があります。
Realm は、ゼロコピーのライブ オブジェクトを使用して、これをすべてバイパスします。 Realm オブジェクト アアクセスはメモリ マッピングを使用してデータベース ストレージを直接指すため、Realm 内のオブジェクトとアプリケーション メモリ内のクエリの結果を区別しません。 このため、どのクエリ結果からでも、Realm 全体にわたる直接参照を走査できます。
このページの例について
このページの例では、 Project
とTask
の 2 つの Realm オブジェクトタイプを持つプロジェクト管理アプリのデータモデルを使用します。 Project
には 0 個以上のTasks
があります。
これらの 2 つのクラス、 Project
とTask
のスキーマを以下で参照してください。
import org.bson.types.ObjectId; import io.realm.RealmObject; import io.realm.annotations.PrimaryKey; import io.realm.annotations.RealmClass; import io.realm.annotations.Required; public class ProjectTask extends RealmObject { public ObjectId _id; public String name; public String assignee; public int progressMinutes; public boolean isComplete; public int priority; public String _partition; }
import org.bson.types.ObjectId; import io.realm.RealmList; import io.realm.RealmObject; import io.realm.annotations.PrimaryKey; import io.realm.annotations.RealmClass; import io.realm.annotations.Required; public class Project extends RealmObject { public ObjectId _id; public String name; public RealmList<ProjectTask> tasks = new RealmList<>(); }
import io.realm.RealmObject import io.realm.annotations.PrimaryKey import io.realm.annotations.Required import org.bson.types.ObjectId open class ProjectTask( var _id: ObjectId = ObjectId(), var name: String = "", var assignee: String? = null, var progressMinutes: Int = 0, var isComplete: Boolean = false, var priority: Int = 0, var _partition: String = "" ): RealmObject()
import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey import io.realm.annotations.Required import org.bson.types.ObjectId open class Project( var _id: ObjectId = ObjectId(), var name: String = "", var tasks: RealmList<ProjectTask> = RealmList(), ): RealmObject()
Realm からの読み取り
Realm からの読み取りは通常、次の手順で構成されます。
Realm から特定のタイプのすべてのオブジェクトを取得します。
必要に応じて、 クエリ エンジン を使用して結果を フィルタリング します。
必要に応じて、結果を並べ替えます。
すべてのクエリ、フィルタ、ソート操作は結果のコレクションを返します。 結果コレクションはライブです。つまり、関連付けられたクエリの最新の結果が常に含まれます。
重要
UI スレッドでの同期読み取りと書込み
デフォルトでは、非同期トランザクションを使用して、アプリケーションの UI スレッド内の Realm の読み取りまたは書き込みのみが可能です。 つまり、同期メソッドの使用を明示的に許可しない限り、Android アプリケーションのメイン スレッドでは、名前がAsync
という単語で終わるRealm
メソッドのみを使用できます。
この制限はアプリケーション ユーザーのメリットのために存在します。UI スレッドで読み取りおよび書込み操作を実行すると、UI のインタラクションが応答しなくなったり低速になったりする可能性があるため、通常、これらの操作は非同期またはバックグラウンド スレッドで取り扱うことをお勧めします。 ただし、アプリケーションで同期 Realm の読み取りまたは UI スレッドでの書込みを使用する必要がある場合は、次のSyncConfiguration
オプションを使用して同期メソッドの使用を明示的に許可できます。
SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build(); Realm.getInstanceAsync(config, new Realm.Callback() { public void onSuccess(Realm realm) { Log.v( "EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread." ); } });
val config = SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build() Realm.getInstanceAsync(config, object : Realm.Callback() { override fun onSuccess(realm: Realm) { Log.v("EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread.") } })
プライマリキーによる特定のオブジェクトの検索
特定のプライマリキー値を持つオブジェクトを検索するには、Realm を開き、 RealmQuery.equalTo() メソッドを使用して、目的のプライマリキー値をプライマリキー フィールドでクエリします。 メソッド:
ProjectTask task = realm.where(ProjectTask.class).equalTo("_id", PRIMARY_KEY_VALUE.get()).findFirst(); Log.v("EXAMPLE", "Fetched object by primary key: " + task);
val task = realm.where(ProjectTask::class.java) .equalTo("_id", ObjectId.get()).findFirst() Log.v("EXAMPLE", "Fetched object by primary key: $task")
特定の型のすべてのオブジェクトのクエリ
読み取りの最初のステップは、Realm 内の特定のタイプのすべてのオブジェクトを取得することです。 この結果コレクションでは、タイプまたはフィルターのすべての インスタンスに対して を操作し、並べ替えて結果を絞り込むことができます。
ProjectTask
とProject
のすべてのインスタンスにアクセスするには、 where()メソッドを使用してクラスを指定します。
RealmQuery<ProjectTask> tasksQuery = realm.where(ProjectTask.class); RealmQuery<Project> projectsQuery = realm.where(Project.class);
val tasksQuery = realm.where(ProjectTask::class.java) val projectsQuery = realm.where(Project::class.java)
オブジェクト プロパティに基づいてクエリをフィルタリングする
フィルターは、1 つ以上のオブジェクト プロパティの値に基づいて結果のサブセットを選択します。 Realm は、フィルターの定義に使用できるフル機能のクエリ エンジンを提供します。 最も一般的なユースケースは、特定のプロパティが特定の値と一致するオブジェクトを検索することです。 さらに、文字列を比較したり、数値のコレクションを集計したり、論理演算子を使用して複雑なクエリを構築したりすることもできます。
次の例では、クエリ エンジンの比較演算子を使用して次のようにします。
priority
プロパティ値の値をしきい値と比較して、優先順位の高いタスクを見つけます。どの優先順位を超えると、優先順位が高いと見なされます。progressMinutes
プロパティが特定の範囲内にあるかどうかを確認して、開始されたばかりのタスクまたは実行時間が短いタスクを見つけます。assignee
プロパティが null に等しいタスクを見つけて、割り当てられていないタスクを見つけます。assignee
プロパティが名前のリストにあるかどうかを確認して、特定のチームメイト エイリアスまたはレイテンシに割り当てられたタスクを見つけます。
RealmQuery<ProjectTask> tasksQuery = realm.where(ProjectTask.class); Log.i("EXAMPLE", "High priority tasks: " + tasksQuery.greaterThan("priority", 5).count()); Log.i("EXAMPLE", "Just-started or short tasks: " + tasksQuery.between("progressMinutes", 1, 10).count()); Log.i("EXAMPLE", "Unassigned tasks: " + tasksQuery.isNull("assignee").count()); Log.i("EXAMPLE", "Ali or Jamie's tasks: " + tasksQuery.in("assignee", new String[]{"Ali", "Jamie"}).count());
val tasksQuery = realm.where(ProjectTask::class.java) Log.i( "EXAMPLE", "High priority tasks: " + tasksQuery.greaterThan( "priority", 5 ).count() ) Log.i( "EXAMPLE", "Just-started or short tasks: " + tasksQuery.between( "progressMinutes", 1, 10 ).count() ) Log.i( "EXAMPLE", "Unassigned tasks: " + tasksQuery.isNull("assignee").count() ) Log.i( "EXAMPLE", "Ali or Jamie's tasks: " + tasksQuery.`in`( "assignee", arrayOf( "Ali", "Jamie" ) ).count() )
クエリ結果のソート
ソート操作を使用すると、Realm がクエリされたオブジェクトを返す順序を構成できます。 結果コレクション内のオブジェクトの 1 つ以上のプロパティに基づいて並べ替えることができます。
Realm は、結果がソートされる場合にのみ、一貫した結果の順序を保証します。
次のコードは、プロジェクトを名前で逆アルファベット順にソートします(つまり 「降順」の順序)を超えるドキュメントを選択します。
RealmQuery<Project> projectsQuery = realm.where(Project.class); RealmResults<Project> results = projectsQuery.sort("name", Sort.DESCENDING).findAll();
val projectsQuery = realm.where(Project::class.java) val results = projectsQuery.sort("name", Sort.DESCENDING).findAll()
関係のクエリ
クラスHuman
とCat
の次の関係を考慮します。 この配置により、各人間は 1 月のコレクションを所有することができます。
import org.bson.types.ObjectId; import io.realm.RealmObject; import io.realm.annotations.PrimaryKey; public class Human extends RealmObject { private ObjectId _id = new ObjectId(); private String name; private Cat cat; public Human(String name) { this.name = name; } public Human() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public ObjectId get_id() { return _id; } }
import org.bson.types.ObjectId; import io.realm.RealmObject; import io.realm.RealmResults; import io.realm.annotations.LinkingObjects; import io.realm.annotations.PrimaryKey; public class Cat extends RealmObject { private ObjectId _id = new ObjectId(); private String name = null; private final RealmResults<Human> owner = null; public Cat(String name) { this.name = name; } public Cat() { } public ObjectId get_id() { return _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public RealmResults<Human> getOwner() { return owner; } }
この関係をクエリするには、クエリでドット表記を使用して、リンクされたオブジェクトの任意のプロパティにアクセスします。
SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build(); Realm.getInstanceAsync(config, new Realm.Callback() { public void onSuccess(Realm realm) { Log.v( "EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread." ); realm.executeTransaction(transactionRealm -> { Human owner = transactionRealm.where(Human.class).equalTo("cat.name", "bucky").findFirst(); Cat cat = owner.getCat(); Log.v("EXAMPLE", "Queried for humans with cats named 'bucky'. Found " + owner.getName() + ", who owns " + cat.getName()); }); realm.close(); } });
クラスPerson
とDog
の次の関係を考慮します。 この配置により、各人は 1 台の犬を所有することができます。
import io.realm.RealmObject import io.realm.annotations.PrimaryKey import org.bson.types.ObjectId open class Person(var name : String? = null) : RealmObject() { var _id : ObjectId = ObjectId() var dog: Dog? = null }
import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.LinkingObjects import io.realm.annotations.PrimaryKey import org.bson.types.ObjectId open class Dog(var name : String? = null): RealmObject() { var _id : ObjectId = ObjectId() val owner: RealmResults<Person>? = null }
この関係をクエリするには、クエリでドット表記を使用して、リンクされたオブジェクトの任意のプロパティにアクセスします。
val config = SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build() Realm.getInstanceAsync(config, object : Realm.Callback() { override fun onSuccess(realm: Realm) { Log.v( "EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread." ) realm.executeTransaction { transactionRealm -> val owner = transactionRealm.where<Person>().equalTo("dog.name", "henry").findFirst() val dog = owner?.dog Log.v("EXAMPLE", "Queried for people with dogs named 'henry'. Found $owner, owner of $dog") } realm.close() } })
逆関係のクエリ
クラスCat
とHuman
の次の関係を考慮します。 この例では、すべての環境は人間(または複数の人間オブジェクトが同じ月を参照する場合は複数の人間)にリンクします。 Realm は、 @LinkingObjects
アノテーションに指定されたフィールド名に基づいて、各コストの所有者を計算します。
import org.bson.types.ObjectId; import io.realm.RealmObject; import io.realm.RealmResults; import io.realm.annotations.LinkingObjects; import io.realm.annotations.PrimaryKey; public class Cat extends RealmObject { private ObjectId _id = new ObjectId(); private String name = null; private final RealmResults<Human> owner = null; public Cat(String name) { this.name = name; } public Cat() { } public ObjectId get_id() { return _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public RealmResults<Human> getOwner() { return owner; } }
import org.bson.types.ObjectId; import io.realm.RealmObject; import io.realm.annotations.PrimaryKey; public class Human extends RealmObject { private ObjectId _id = new ObjectId(); private String name; private Cat cat; public Human(String name) { this.name = name; } public Human() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public ObjectId get_id() { return _id; } }
この関係をクエリするには、クエリでドット表記を使用して、リンクされたオブジェクトの任意のプロパティにアクセスします。
SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build(); Realm.getInstanceAsync(config, new Realm.Callback() { public void onSuccess(Realm realm) { Log.v("EXAMPLE", "Successfully opened a realm."); realm.executeTransaction(transactionRealm -> { Cat cat = transactionRealm.where(Cat.class) .equalTo("owner.name", "steven").findFirst(); Human owner = cat.getOwner().first(); Log.v("EXAMPLE", "Queried for cats with owners named 'steven'. Found " + cat.getName() + ", owned by " + owner.getName()); }); realm.close(); } });
クラスDog
とPerson
の次の関係を考慮します。 この例では、すべての犬が所有者(または複数の所有者が同じ犬を参照している場合は複数の所有者)にリンクします。 Realm は、 @LinkingObjects
アノテーションに指定されたフィールド名に基づいて、各犬の所有者を計算します。
import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.LinkingObjects import io.realm.annotations.PrimaryKey import org.bson.types.ObjectId open class Dog(var name : String? = null): RealmObject() { var _id : ObjectId = ObjectId() val owner: RealmResults<Person>? = null }
import io.realm.RealmObject import io.realm.annotations.PrimaryKey import org.bson.types.ObjectId open class Person(var name : String? = null) : RealmObject() { var _id : ObjectId = ObjectId() var dog: Dog? = null }
この関係をクエリするには、クエリでドット表記を使用して、リンクされたオブジェクトの任意のプロパティにアクセスします。
val config = SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build() Realm.getInstanceAsync(config, object : Realm.Callback() { override fun onSuccess(realm: Realm) { Log.v( "EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread." ) realm.executeTransaction { transactionRealm -> val dog = transactionRealm.where<Dog>() .equalTo("owner.name", "dwayne").findFirst() val owner = dog?.owner?.first() Log.v("EXAMPLE", "Queried for dogs with owners named 'dwayne'. Found $dog, owned by $owner") } realm.close() } })
データを集計する
RealmQuery<ProjectTask> tasksQuery = realm.where(ProjectTask.class); /* Aggregate operators do not support dot-notation, so you cannot directly operate on a property of all of the objects in a collection property. You can operate on a numeric property of the top-level object, however: */ Log.i("EXAMPLE", "Tasks average priority: " + tasksQuery.average("priority"));
val tasksQuery = realm.where(ProjectTask::class.java) /* Aggregate operators do not support dot-notation, so you cannot directly operate on a property of all of the objects in a collection property. You can operate on a numeric property of the top-level object, however: */Log.i("EXAMPLE", "Tasks average priority: " + tasksQuery.average("priority"))