CRUD - 읽기 - Java SDK
이 페이지의 내용
읽기 작업
Realm 에 저장 한 데이터를 다시 읽을 수 있습니다. Realm SDK의 표준 데이터 액세스 패턴 은 객체를 순서대로 찾고, 필터하다 하고, 정렬하는 것입니다. 앱 이 성장하고 쿼리가 복잡해짐에 따라 Realm 에서 최상의 성능을 얻으려면 Realm 읽기 특성에 대한 확실한 이해를 바탕으로 앱의 데이터 액세스 패턴을 설계하세요.
특성 읽기
Realm 읽기의 다음 세 가지 주요 특성을 중심으로 앱의 데이터 액세스 패턴을 설계하면 최대한 효율적으로 데이터를 읽을 수 있다고 확신할 수 있습니다.
결과가 사본이 아님
쿼리 결과는 데이터의 복사본이 아닙니다. 쿼리 결과를 수정하면 디스크에 있는 데이터가 직접 수정됩니다. 또한 이 메모리 매핑은 결과가 라이브라는 의미이기도 합니다. 즉, 항상 디스크의 현재 상태를 반영합니다.
결과 지연
Realm 은 사용자가 결과에 액세스 때까지 쿼리 실행을 연기합니다. 중간 상태 를 프로세스 하기 위해 예비 작업 없이 여러 필터하다 및 정렬 작업을 연결할 수 있습니다.
참조 유지됨
Realm 객체 모델의 장점 중 하나는 Realm이 객체의 모든 관계를 자동으로 직접 참조로 유지하므로 쿼리 결과를 통해 관계 그래프를 직접 탐색할 수 있다는 것입니다.
직접 참조(포인터)를 사용하면 참조를 통해 관련 객체의 속성에 직접 액세스할 수 있습니다.
다른 데이터베이스는 직접 작업해야 할 때 일반적으로 데이터베이스 저장소의 객체를 애플리케이션 메모리로 복사합니다. 애플리케이션 객체에는 직접 참조가 포함되어 있으므로 각 직접 참조가 참조하는 객체가 필요할 경우 데이터베이스에서 객체를 복사하거나 각 객체에 대한 외래 키를 복사하고 해당 키에 액세스할 경우 해당 객체를 쿼리할 수 있습니다. 참조된 객체를 애플리케이션 메모리에 복사하면 액세스하지 않는 객체에 많은 리소스가 사용될 수 있는 반면 외래 키만 복사하도록 선택하면 참조된 객체 조회로 인해 애플리케이션 속도가 느려질 수 있습니다.
Realm은 제로 카피 라이브 객체를 사용하여 이 모든 과정을 우회합니다. Realm 객체 접근자는 메모리 매핑을 사용하여 데이터베이스 스토리지를 직접 지시하므로 영역의 객체와 애플리케이션 메모리의 쿼리 결과를 구분하지 않습니다. 이로 인해 쿼리 결과에서 전체 영역에 걸쳐 직접 참조를 순회할 수 있습니다.
해당 페이지의 예시에 대한 정보
이 페이지의 예제에서는 Project
및 Task
라는 두 가지 Realm 객체 유형이 있는 프로젝트 관리 앱의 Realm 데이터 모델을 사용합니다. 에는 이(가) 0개 이상 Project
Tasks
있습니다.
아래에서 이 두 클래스의 스키마 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에서 읽기
영역에서의 읽기는 일반적으로 다음 단계로 구성됩니다.
모든 쿼리, 필터하다 및 정렬 작업은 결과 컬렉션 을 반환합니다. 결과 컬렉션은 실시간이므로 항상 관련 쿼리 의 최신 결과가 포함되어 있습니다.
중요
UI 스레드의 동기식 읽기 및 쓰기
기본적으로 비동기 트랜잭션을 사용하여 애플리케이션의 UI 스레드에서 영역에 읽기 또는 쓰기만 가능합니다. 즉, 동기 메서드의 사용을 명시적으로 허용하지 않는 한 Android 애플리케이션의 기본 스레드에서 이름이 Async
(이)라는 단어로 끝나는 Realm
메서드만 사용할 수 있습니다.
이러한 제한은 애플리케이션 사용자를 위해 존재합니다. 즉, UI 스레드에서 읽기 및 쓰기 작업을 수행하면 UI 상호 작용이 응답하지 않거나 느려질 수 있으므로 일반적으로 이러한 작업을 비동기적으로 처리하거나 배경 스레드에서 처리하는 것이 가장 좋습니다. 하지만 애플리케이션이 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.") } })
기본 키로 특정 객체 찾기
특정 기본 키 값을 가진 객체 를 찾으려면 영역 을 열고 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")
주어진 유형의 모든 객체 쿼리
모든 읽기의 첫 번째 단계는 영역 에서 특정 유형의 모든 객체를 가져오는 것 입니다. 이 결과 컬렉션 을 사용하면 유형 또는 필터하다 의 모든 인스턴스에 대해 작업을 수행하고 정렬하여 결과를 구체화할 수 있습니다.
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)
객체 속성을 기준으로 쿼리 필터링
필터 는 하나 이상의 객체 속성 값을 기반으로 결과의 하위 집합을 선택합니다. Realm은 필터를 정의하는 데 사용할 수 있는 모든 기능을 갖춘 쿼리 엔진 을 제공합니다. 가장 일반적인 사용 사례는 특정 속성이 특정 값과 일치하는 객체를 찾는 것입니다. 또한 문자열을 비교하고, 숫자 컬렉션을 집계하고, 논리 연산자를 사용하여 복잡한 쿼리를 작성할 수 있습니다.
다음 예시 에서는 쿼리 엔진의 비교 연산자를 사용하여 다음을 수행합니다.
priority
속성 값 중 우선 순위가 높은 것으로 간주되는 값을 임계값과 비교하여 우선순위가 작업을 찾습니다.progressMinutes
속성이 특정 범위 내에 속하는지 확인하여 방금 시작한 작업이나 단기 실행 작업을 찾습니다.assignee
속성 이 null인 작업을 찾아 할당되지 않은 작업을 찾습니다.이름 목록에
assignee
속성이 있는지 확인하여 Ali 또는 Jamie라는 특정한 팀원에게 할당된 작업을 찾을 수 있습니다.
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이 쿼리된 객체를 반환하는 순서를 구성할 수 있습니다. 결과 컬렉션에 있는 객체의 속성을 하나 이상의 기준으로 정렬할 수 있습니다.
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
클래스 간의 다음 관계 를 고려하세요. 이 배열을 사용하면 각 인간이 한 마리의 고양이를 키울 수 있습니다.
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
클래스 간의 다음과 같은 관계를 가정해 보겠습니다. 이 배열을 통해 각 사람이 개 한 마리를 키울 수 있습니다:
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"))