03 realm 쓰기 & 질의

31
0 Realm 쓰기 & 질의 Made by 이종찬

Upload: lee-jong-chan

Post on 16-Apr-2017

200 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: 03   realm 쓰기 & 질의

0

Realm쓰기 & 질의

Made by 이종찬

Page 2: 03   realm 쓰기 & 질의

1

01주차 Realm introrealm을 안드로이드 스튜디오에 설치하기

02주차 모델 & 관계지원하는 필드 타입과 어노테이션 & 관계 설정하는 법

03주차 쓰기 & 질의트랜잭션을 통한 삽입 & 질의의 종류

Page 3: 03   realm 쓰기 & 질의

2

01Transaction

Page 4: 03   realm 쓰기 & 질의

3

01 TransactionRealm 객체를 사용하기 전 Transaction에 대해서 알아보자.

1 Transaction이란?

1 데이터 베이스에서 읽거나 쓰기 위해 실행하는 여러 개의쿼리(질의)를 끊기지 않고 전부 수행하기 위한 것.-> 완전성(integrity)를 보장

2 Atomicity(원자성), Consistency(일관성), Isolation(고립성), Durability(영구성)을 보장한다.

Realm은 MVCC 아키텍처를 사용하기 때문에 쓰기 트랜잭션 실행되도읽기는 막히지 않는다.

-> 읽기는 언제든지 객체를 접근 할 수 있다.-> 추가, 수정, 삭제는 트랜잭션 안에서 해야한다.

Page 5: 03   realm 쓰기 & 질의

4

MVCC

다중 버전 동시성 제어(multiversion concurrency control)

A가 읽기에 대한 처리를 트랜잭션을 통해 수행하면 동일 데이터를다른 사람 B는 A가 끝날 때까지 접근을 할 수 없었다.

하지만 MVCC는 A가 트랜잭션을 할 때 동일한 데이터를 복사해 두어B도 동시에 접근을 가능하게 만들었다.

쉽게 말해서 [동시에 여러명이 접근이 가능하게 했다] 라는 것이다.

Page 6: 03   realm 쓰기 & 질의

5

01 TransactionRealm에서 Transaction을 관리하는 법을 알아보자.

Transaction관리하기

1 수동으로 관리하기

// Realm 인스턴스를 얻습니다Realm realm = Realm.getDefaultInstance();

realm.beginTransaction();// 객체를 여기에서 추가하거나 갱신합니다

realm.commitTransaction();// commit이 호출되면 모든 작업이 정상적으로 완료한 것입니다.

realm.cancelTransaction();// 오류가 났을 때는 cancel을 호출하여 모든 작업을 취소할 수 있습니다.

Page 7: 03   realm 쓰기 & 질의

6

01 TransactionRealm에서 Transaction을 관리하는 법을 알아보자.

Transaction관리하기

2 자동으로 관리하기

// Realm 인스턴스를 얻습니다Realm realm = Realm.getDefaultInstance();

realm.executeTransaction(new Realm.Transaction() {@Overridepublic void execute(Realm realm) {

User user = realm.createObject(User.class);user.setName("John");user.setEmail("[email protected]");

}});

Transaction 블록을 사용하여 간편하게 Transaction을 관리할 수 있다.

Page 8: 03   realm 쓰기 & 질의

7

01 TransactionRealm에서 Transaction을 관리하는 법을 알아보자.

Transaction관리하기

3 비동기 트랜잭션(Async)

realm.executeTransactionAsync(new Realm.Transaction() {@Overridepublic void execute(Realm bgRealm) {

// 수행할 내용.}

}, new Realm.Transaction.OnSuccess() {@Overridepublic void onSuccess() {

// 트랜잭션이 성공하면 호출되는 콜백 함수.}

}, new Realm.Transaction.OnError() {@Overridepublic void onError(Throwable error) {// 트랜잭션이 실패하면 호출되는 콜백 함수}

});

Page 9: 03   realm 쓰기 & 질의

8

동기 & 비동기

동기 (Synchronous: 동시에 일어나는)- 요청과 그 결과가 동시에 일어난다는 약속- 장점 : 설계가 간단하고 직관적이다.- 단점 : 결과가 주어질 때까지 아무것도 못하고 대기해야 한다.

비동기 (Asynchronous: 동시에 일어나지 않는)- 요청과 그 결과가 동시에 일어나지 않을 것이라는 약속이다. -> 요청한 그 자리에서 바로 결과가 주어지는 것이 아니라,

이따가 준다고 약속하는 것이다.- 장점 : 결과가 주어지지 않아도 그 시간 동안 다른 작업을 할 수 있다.- 단점 : 설계가 복잡하다.

Page 10: 03   realm 쓰기 & 질의

9

01 TransactionRealm에서 Transaction을 관리하는 법을 알아보자.

Transaction관리하기

3 비동기 트랜잭션(Async)

APIPublic RealmAsyncTask executeTransactionAsync(Realm.Transaction transaction);Public RealmAsyncTask executeTransactionAsync(Realm.Transaction transaction,

Realm.Transaction.OnSuccess onSuccsess);Public RealmAsyncTask executeTransactionAsync(Realm.Transaction transaction,

Realm.Transaction.OnError onError);Public RealmAsyncTask executeTransactionAsync(Realm.Transaction transaction,

Realm.Transaction.OnSuccess onSuccsess,Realm.Transaction.OnError onError);

백그라운드 쓰레드에서 쓰기 작업을 하는 중인데 UI 쓰레드가 쓰기 작업을하려고 하면 ANR에러가 발생할 수 있다.-> 때문에 비동기 트랜잭션을 사용한다.

OnSuccess와 OnError 콜백은 선택 사항으로 사용하면 된다.콜백은 Looper에 의해 제어되고 Looper 스레드에서만 허용된다.

Page 11: 03   realm 쓰기 & 질의

10

01 TransactionRealm에서 Transaction을 관리하는 법을 알아보자.

Transaction관리하기

3 비동기 트랜잭션(Async)

비동기 트랜잭션은 RealmAsyncTask 객체로서 저장할 수 있다.

RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {// 수행할 내용.

});

객체로 표현하는 이유는 트랜잭션이 끝나기 전에 액티비티나 프레그먼트를 끝내야할 때 대기중인 트랜잭션을 취소하기 위해서도 사용하기 때문이다.Ex) 1. 현재 대기중인 트랜잭션이 있다고 가정하자.

2. 이 트랜잭션을 수행한 다음에는 액티비티의 TextView를 갱신한다.3. 하지만 이 트랜잭션이 수행되기 전에 액티비티를 종료하였다.4. 만약 트랜잭션이 그대로 수행된다면 존재하지 않는 메모리에 TextView에 대한

데이터 갱신 작업을 수행해야 하는 것이다. 앱이 충돌할 수 있다.

때문에 액티비티를 종료하기 전에 확인 작업을 해주어야 한다.public void onStop () {

if (transaction != null && !transaction.isCancelled()) {transaction.cancel();

}}

Page 12: 03   realm 쓰기 & 질의

11

02쓰기(write)

Page 13: 03   realm 쓰기 & 질의

12

02 쓰기Realm 객체를 생성하는 법을 알아보자.

객체 생성하기

1 Realm을 통해 객체 생성하기

RealmObject들은 Realm과 긴밀하게 연결되어 있기 때문에Realm을 통해 인스턴스가 생성되어야 한다.

realm.beginTransaction();User user = realm.createObject(User.class); // 새 객체 만들기user.setName("John");user.setEmail("[email protected]");realm.commitTransaction();

Realm에 의해 관리되는 객체의 데이터를 수정할 경우 Transaction 안에서 해줘야한다는 단점이 있을 수 있다.(내 생각)

Page 14: 03   realm 쓰기 & 질의

13

02 쓰기Realm 객체를 생성하는 법을 알아보자.

객체 생성하기

2 객체를 생성 후 Realm에 등록하기

Realm을 통해서만 객체를 생성할 때는 불편한 점이 있을 수 있다.그때는 Realm.copyToRealm() 메소드를 사용하여 등록하면 된다.

User user = new User("John");user.setEmail("[email protected]");

realm.beginTransaction();User realmUser = realm.copyToRealm(user);realm.commitTransaction();

< 중요 > copyToRealm을 사용할 때 반환되는 객체를 Realm이 관리한다. -> 원본 객체(user)의 내용을 바꿔도 Realm에 있는 데이터가 바뀌는 것이 아니다.-> Realm이 관리하는 객체(realmUser)를 바꿔야 Realm에 있는 데이터가 바뀐다.

Page 15: 03   realm 쓰기 & 질의

14

02 쓰기객체를 생성하고 쓰기를 할 때 주의할 점을 알아보자.

주의 할 점

createObject를 사용하여 객체를 처음 생성할 경우 Realm은 RealmObject안에 있는 필드의 내용을 default value(쓰레기값)으로 설정을 하고 객체를 만든다.이때 객체의 필드에 PrimaryKey가 등록되어 있으면 충돌이 일어 날 수 있다.-> default value는 고정값이기 때문이다.

이를 해결하기 위해서는 비관리 객체를 생성하고 PrimaryKey에 대한 값을 설정한다음 copyToRealm을 이용해서 Realm에 등록해 주어야 한다.

User user = realm.createObject(User.class); // 필드들에 대한 값을 default value로 설정한다.// 이때 PrimaryKey에 대한 충돌이 날 수 있다.

User user = new User("John");uesr.setId(value);

realm.beginTransaction();User realmUser = realm.copyToRealm(user);realm.commitTransaction();// 이렇게 해주면 충돌을 피할 수 있다.

1 PrimaryKey 충돌

Page 16: 03   realm 쓰기 & 질의

15

02 쓰기객체를 생성하고 쓰기를 할 때 주의할 점을 알아보자.

주의 할 점

Realm은 문자열이나 바이트 배열의 개별 요소를 갱신하는 것은 불가능하다.특정 요소를 갱신하기 위해선 아래처럼 해야한다.

realm.executeTransaction(new Realm.Transaction() {@Overridepublic void execute(Realm realm) {

bytes[] bytes = realmObject.binary;bytes[4] = 'a';realmObject.binary = bytes;

}});

2 문자열과 바이트 배열 갱신하기

Realm이 MVCC 아키텍처에 따라 기존의 데이터를 변경하는 것을 막아 다른 스레드와프로세스가 안정된 상태의 데이터를 읽기 위함이다.

Page 17: 03   realm 쓰기 & 질의

16

03질의(Query)

Page 18: 03   realm 쓰기 & 질의

17

03 질의(Query)Realm의 기본적인 사용법(1)

질의의 시작은 realm.where를 시작으로 하며 끝은 find(All)을 통해 끝난다.

Realm의 모든 질의는 바로 처리되지 않고 속성에 접근할 때에만 데이터를 읽는다.

API(Realm.class) Public RealmQuery<E> where(java.lang.Class<E> class);

(RealmQuery.class) Public RealmResults<E> findAll();(RealmQuery.class) Public RealmResults<E> findAllAsync();(RealmQuery.class) Public RealmResults<E> findAllSorted(String fieldName);

(RealmQuery.class) Public E findFirst();

Page 19: 03   realm 쓰기 & 질의

18

03 질의(Query)Realm의 기본적인 사용법(1)

// Build the query looking at all users:RealmQuery<User> query = realm.where(User.class);

// 질의 조건을 추가합니다query.equalTo("name", "John");query.or().equalTo("name", "Peter");

// 질의를 수행합니다RealmResults<User> result1 = query.findAll();

질의를 통해 나온 결과가 RealmResults에 저장되어 나온다.이때 객체는 복사되지 않고, 레퍼런스가 저장되어 있는 RealmResult를 통해서직접적으로 다룰 수 있다.

RealmResults는 AbstractList를 상속받았기 때문에각 객체는 인덱스를 통해서 접근이 가능하다.

또한 질의에 맞는 결과가 없더라도 null을 반환하지 않고size()메소드가 0을 반환한다.

각 객체의 내용을 수정하거나 삭제할 경우 Transaction을 이용해야 한다.

Page 20: 03   realm 쓰기 & 질의

19

03 질의(Query)Realm의 기본적인 사용법(2) – Fluent interface

// 같은 일들을 한번에 합니다 ("Fluent interface"):RealmResults<User> result2 = realm.where(User.class)

.equalTo("name", "John")

.or()

.equalTo("name", "Peter")

.findAll();

RealmQuery<User> query = realm.where(User.class);

query.equalTo("name", "John");query.or().equalTo("name", "Peter");

RealmResults<User> result1 = query.findAll();

Page 21: 03   realm 쓰기 & 질의

20

03 질의(Query)Realm의 Query engine에 대해서 알아보자.

Fluent interface

Realm은 Query Engine으로 Fluent interface를 사용하였다.Fluent interface란 메소드 체이닝(method chaining) 기법으로return 값으로 자기 자신 또는 다른 객체를 지정 함으로서메소드를 연속적으로 호출 할 수 있도록 하는 기법이다.

가장 쉽게 볼 수 있는 것은 자바스크립트(또는 Jquery)에서 볼 수 있다.document.getElementById("demo").innerHTML…

안드로이드에서 쉽게 볼 수 있는 곳은 Toast 부분에서이다.Toast.makeText(…).show(); (또는 스낵바)

Toast의 makeText는 return 값으로 Toast 객체를 반환한다.-> 즉 자기 자신을 반환하는 것이다. 때문에 자기 자신의 메소드를

다시 호출 할 수 있어서 메소드 체이닝이 가능하게 된다.

Realm의 Query도 이러한 구조를 띄고 있다.

Page 22: 03   realm 쓰기 & 질의

21

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

1 조건 관련 질의대부분 이름만으로 설명이 된다.• between(), greaterThan(), lessThan(), greaterThanOrEqualTo(), lessThanOrEqualTo()

-> (필드 이름, 숫자 (, 숫자))• equalTo(), notEqualTo()

-> (필드 이름, 값)• contains(), beginsWith(), endsWith()

-> (필드 이름, 문자)• isNull(), isNotNull()

-> (필드 이름)• isEmpty(), isNotEmpty()

-> (필드 이름)

equalTo(), contains(), beginsWith(), endsWith()문자에 대한 질의 중 이 4개에 대해서는(필드 이름, 문자, case)를 통해서 대소문자를 구분의 유무를 정할 수 있다.

-> CASE.SENSITIVE , CASE.INSENSITIVE

Page 23: 03   realm 쓰기 & 질의

22

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

2 논리 연산자• Realm의 Query는 암묵적으로 and 연산을 가지고 있으며 or연산이필요할 때는 or연산만 명시해주면 된다.

• 그룹 조건의 연산 순서를 명시하기 위해서는 왼쪽 괄호로 beginGroup()을사용하고 오른쪽 괄호로 endGroup()을 사용한다.

• not()으로 부정 조건을 만들 수 있다. 하위 조건을 부정하기 위해서는beginGroup과 endGroup을 함께 사용해야 한다.

RealmResults<User> r = realm.where(User.class).greaterThan("age", 10) // 암묵적인 AND.beginGroup()

.equalTo("name", "Peter")

.or()

.contains("name", "Jo").endGroup().findAll();

Page 24: 03   realm 쓰기 & 질의

23

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

3 결과 정렬하기RealmResults<User> result = realm.where(User.class).findAll();result = result.sort("age"); // 오름차순으로 정렬result = result.sort("age", Sort.DESCENDING); // 내림차순으로 정렬

API (RealmResults.class)

Public RealmResults<E> sort(String fieldname);Public RealmResults<E> sort(String fieldname, Sort sortOrder);Public RealmResults<E> sort(String[] fieldname, Sort[] sortOrder);Public RealmResults<E> sort(String fieldname1, Sort sortOrder1,

String fieldname2, Sort sortOrder2);

Sort.Enum - Sort.ASCENDING, Sort.DESCENDING

Page 25: 03   realm 쓰기 & 질의

24

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

4 집합(arregation)

RealmResults<User> results = realm.where(User.class).findAll();

long sum = results.sum("age").longValue();long min = results.min("age").longValue();long max = results.max("age").longValue();double average = results.average("age");

long matches = results.size();

API (RealmResults.class)숫자가 저장되어 있는 필드만 지원한다.

Public Number sum(String fieldname);Public Number max(String fieldname);Public Number min(String fieldname);Public double average(String fieldname);

Page 26: 03   realm 쓰기 & 질의

25

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

5 삭제(Deletion)

final RealmResults<Dog> results = realm.where(Dog.class).findAll();// 데이터에 대한 모든 변경은 트랜잭션에서 이루어져야 합니다realm.executeTransaction(new Realm.Transaction() {

@Overridepublic void execute(Realm realm) {

// 하나 맞는 데이터를 삭제합니다results.deleteFirstFromRealm();results.deleteLastFromRealm();

// 하나의 객체를 삭제합니다Dog dog = results.get(5);dog.deleteFromRealm();

// 전체 맞는 데이터를 삭제합니다results.deleteAllFromRealm();

}});

Page 27: 03   realm 쓰기 & 질의

26

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

// RealmResults에서 맨 처음 객체를 삭제하거나 맨 마지막 객체를 삭제합니다.results.deleteFirstFromRealm();results.deleteLastFromRealm();

// 하나의 객체를 삭제합니다Dog dog = results.get(5);dog.deleteFromRealm();

1) 하나의 데이터를 삭제하는 방법

이렇게 할 경우 Realm에서는 실제로는 삭제가 되었지만, RealmResults에서는 삭제가 되지 않았을 수도 있다.-> 후에 RealmResults에서 삭제한 데이터가 다시 검색될 수도 있다.

// RealmResults에서 삭제를 직접한다.(realm 문서 – 질의 – 반복자 참고)results.deleteFromRealm(5);

5 삭제(Deletion)

Page 28: 03   realm 쓰기 & 질의

27

03 질의(Query)Realm의 Query의 몇 가지 종류를 알아보자.

Query

API (RealmResults.class)

Public Boolean deleteAllFromRealm();Public Boolean deleteFristFromRealm();Public Boolean deleteLastFromRealm();Public void deleteFromRealm(int location);

API (RealmObject.class)

Public void deleteFromRealm();

5 삭제(Deletion)

2) 결과에 맞는 여러 개의 데이터를 삭제하는 방법

// RealmResults에 있는 모든 데이터를 삭제한다.results.deleteAllFromRealm();

Page 29: 03   realm 쓰기 & 질의

28

03 질의(Query)Realm의 비동기 질의에 대해서 알아보자.

비동기 질의

Realm의 대부분의 질의는 UI스레드에서 동기적으로 처리할 수 있을 정도로충분히 빠르다.

하지만 복잡한 질의나 대규모 데이터 집합을 질의하는 경우, 백그라운드에서질의 하는 것이 더 나은 경우들이 있다.

RealmResults<User> result = realm.where(User.class).equalTo("name", "John").or().equalTo("name", "Peter").findAllAsync();

// findAll 이였던 부분을 findAllAsync로만 바꿔주면 된다.

질의는 블록되지 않고 즉시 RealmResults를 반환하는 것을 유의해야 한다.-> 표준 자바의 Future와 흡사한 구조이다. (Thread와 관련 있는 것으로 생각된다)

질의는 백그라운드에서 계속되며 한번 완료되면 반환된RealmResults 인스턴스를 갱신한다.

Page 30: 03   realm 쓰기 & 질의

29

03 질의(Query)Realm의 비동기 질의에 대해서 알아보자.

비동기 질의

1) 백그라운드에서 질의가 끝났는지 확인할 수 있다.

if (result.isLoaded()) {// 결과는 지금 사용할 수 있습니다

}

2) 비동기 질의를 강제적으로 동기적으로 로딩 할 수 있다.

동기적으로 획득한 RealmResults의 isLoaded()를 호출하면 항상 true를 반환한다.

result.load() // 주의하세요. 이 코드는 현재 스레드를 반환될 때까지 멈추게 합니다

3) 루퍼 스레드에서만 비동기 질의를 사용할 수 있다.비동기 질의는 결과를 일관되게 전달하기 위해 Handler를 사용한다.Looper 없이 스레드 내에서 비동기 질의를 하게 되면ILLegalStateException을 발생시킨다.

Page 31: 03   realm 쓰기 & 질의

30

감사합니다.

정보 출처 : realm.ioppt템플릿 : 홍양홍삼님 블로그