移動數據庫 Realm 在 React-Native 的使用詳解

在開發中有些數據咱們須要在本地進行持久化存儲,在須要的地方調用。通常來講,咱們會用到 AsyncStorage 來對數據進行持久化的存儲,這是官方推薦使用的存儲方式,相似於 iOS 中的 NSUserDefault ,區別在於,AsyncStorage 只能存儲字符串鍵值對,而 NSUserDefault 能夠存儲字符串和 number。如此,當咱們須要存儲的數據規模較爲龐大時,須要考慮選擇另外一種持久化的存儲方式-- Realmjavascript

Realm:與 SQList 進行數據存儲相比,在性能上,各有優點,可是操做上,Realm 有明顯優點,也更方便使用。其中囊括了各端的使用,包括java

接下來咱們就來看看怎麼使用 Realmnode

1.安裝

npm install --save realmreact

2.連接

react-native link realmlinux

須要注意有兩點:android

1.安卓端可能使用 link 無效,這時能夠進行如下步驟:

  • android/settings.gradle 內添加:
include ':realm'
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
複製代碼
  • android/app/build.gradle 增長依賴庫:
// When using Android Gradle plugin 3.0 or higher
dependencies {
  implementation project(':realm')
}

// When using Android Gradle plugin lower than 3.0
dependencies {
  compile project(':realm')
}
複製代碼
  • MainApplication.java 中導入而且連接 package:
import io.realm.react.RealmReactPackage; // add this 

import public class MainApplication extends Application implements ReactApplication {
    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new RealmReactPackage() // add this line
        );
    }
}
複製代碼

2.由於我是在0.56版本上開大,所以須要固定選擇安裝的版本爲 2.16.0,記住把2上面的 ^ 去掉。

3.初步使用:

Realm.open({
      schema: [{name: 'Dog', properties: {name: 'string'}}]
    }).then(realm => {
      realm.write(() => {
        realm.create('Dog', {name: 'Rex'});
      });
      this.setState({ realm });
    });
複製代碼

4.介紹:

Realm JavaScript enables you to efficiently write your app’s model layer in a safe, persisted and fast way. It’s designed to work with React Native數據庫

意思是在 RN 上用很快,很安全,很棒棒的。npm

來看一個例子:swift

//在文件中引入 realm
const Realm = require('realm');

// 建立數據模型,而且在 properties 中建立屬性
const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: {type: 'int', default: 0},
  }
};
const PersonSchema = {
  name: 'Person',
  properties: {
    name:     'string',
    birthday: 'date',
    cars:     'Car[]',
    picture:  'data?' // optional property
  }
};

//realm 使用的特別之處,把建立的數據模型整合到 schema  之中,經過 open 方法打開一條 Realm
Realm.open({schema: [CarSchema, PersonSchema]})
  .then(realm => {
    // 只能在 write 方法中操做數據,查詢則不用
    realm.write(() => {
    //create 建立數據的方法
      const myCar = realm.create('Car', {
        make: 'Honda',
        model: 'Civic',
        miles: 1000,
      });
      // 更新數據
      myCar.miles += 20; 
    });

    // 查詢,返回數組
    const cars = realm.objects('Car').filtered('miles > 1000');
    
    //這個時候長度是1
	cars.length // => 1

    // 再次新增另外一條數據
    realm.write(() => {
      const myCar = realm.create('Car', {
        make: 'Ford',
        model: 'Focus',
        miles: 2000,
      });
    });

    // 查詢結果更新,變成2
    cars.length // => 2
  })
  .catch(error => {
    console.log(error);
  });
複製代碼

4.Realm 獨有的管理工具--> Realm Studio

用於查看存儲在本地的數據react-native

MacLinuxWindows 版本

5.詳解

Realm.open({schema: [Car, Person]})
  .then(realm => {
    // ...use the realm instance here
  })
  .catch(error => {
    // Handle the error here if something went wrong
  });
複製代碼

open 方法是打開數據庫的方法, open(config: Realm.Configuration): ProgressPromise;,

還有一個是異步線程上的使用:static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void

Configuration 裏帶有不少參數,咱們進去看看:

interface Configuration {
        encryptionKey?: ArrayBuffer | ArrayBufferView | Int8Array;
        migration?: (oldRealm: Realm, newRealm: Realm) => void;
        shouldCompactOnLaunch?: (totalBytes: number, usedBytes: number) => boolean;
        path?: string;
        readOnly?: boolean;
        inMemory?: boolean;
        schema?: (ObjectClass | ObjectSchema)[];
        schemaVersion?: number;
        sync?: Partial<Realm.Sync.SyncConfiguration>;
        deleteRealmIfMigrationNeeded?: boolean;
        disableFormatUpgrade?: boolean;
}
複製代碼

着重講幾個參數:

  • path:建立一個指定存儲路徑的 Realm,默認是 Realm.realm,也能夠本身命名
  • migration:移動功能,把數據遷移到另外一個地方
  • sync:一個同步對象,在數據庫服務中打開 Realm 的同步
  • inMemoryRealm 將在內存中打開,而且對象不會被持久化; 一旦最後一個 Realm 實例關閉,全部對象都會消失
  • deleteRealmIfMigrationNeeded:若是處於開發模式,可使用這項功能來自動刪除數據
  • schemaVersion
    • 若是表格中新增的字段,而後不作任何變化的打開,則程序就會報錯;系統默認打開的版本是0,爲了不程序報錯,也爲了能正確的更新表格數據,應該這樣寫:Realm.open({schema: [Car, Person], schemaVersion: 1});
    • 查看當前版本:Realm.schemaVersion(Realm.defaultPath);

6.數據模型

當初始化的時候,經過建立的 Realm,數據模型伴隨着 schema 的生成而建立,每一個 schema 包含 name(模型名稱),primaryKey(惟一標誌符,一般用於數組),properties(屬性)

const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: {type: 'int', default: 0},
  }
};
const PersonSchema = {
  name: 'Person',
  properties: {
    name:     'string',
    birthday: 'date',
    cars:     'Car[]'
    picture:  'data?', // optional property
  }
};

// Initialize a Realm with Car and Person models
Realm.open({schema: [CarSchema, PersonSchema]})
  .then(realm => {
    // ... use the realm instance to read and modify data
  })
複製代碼

7.參數類型

參數類型有7種,bool,int,float,double,string,data,date

注意的點

  • 其中 dataArrayBuffer,具體是什麼我還不太清楚,不過這個並非數組的意思。
  • 必填屬性保存的時候不支持 null 或者 undefined,選填屬性能夠保存這兩種類型的值,所以若是在賦值時有可能屬於這些值時,應該先作好判斷,不然會拋出異常
  • 選填屬性optional 或者在類型後給個 ? 來作表示,好比:
const PersonSchema = {
  name: 'Person',
  properties: {
    realName:    'string', // 必填屬性
    displayName: 'string?', // 選填屬性
    birthday:    {type: 'date', optional: true}, // 選填屬性
  }
};
複製代碼
  • 除了存儲單個值以外,還能夠將屬性聲明爲任何受支持的基本類型的列表。例如存儲相似 JavaScript array 的話,經過將[]附加到類型名稱來完成
const PersonSchema = {
  name: 'Person',
  properties: {
    name: 'string',
    testScores: 'double?[]'
  }
};

let realm = new Realm({schema: [PersonSchema, CarSchema]});

realm.write(() => {
  let charlie = realm.create('Person', {
    name: 'Charlie',
    testScores: [100.0]
  });

  
  charlie.testScores.push(null);

  
  charlie.testScores.push(70.0);
  
  console.log('charlie.testScores==',charlie.testScores)//打印結果:{ '0': 100, '1': null, '2': 70 }
});
複製代碼
  • 另外,若是須要建立一個屬性中包含多種對象的數組,那能夠往屬性種添加 schema

8.數據操做

  • 更新數據須要在 write 方法內寫,查詢數據則不用,而且最好使用 try/catch 方式以獲取拋出的異常:
try {
  realm.write(() => {
    realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
  });
} catch (e) {
  console.log("Error on creation");
}
複製代碼
  • 刪除對象:
realm.write(() => {
  // Create a book object
  let book = realm.create('Book', {id: 1, title: 'Recipes', price: 35});

  // Delete the book
  realm.delete(book);

  // Delete multiple books by passing in a `Results`, `List`,
  // or JavaScript `Array`
  let allBooks = realm.objects('Book');
  realm.delete(allBooks); // Deletes all books
});
複製代碼
  • filtered 查詢數據的時候,參數只能傳常量或者屬性名稱(目前沒找到傳變量的方法,因此這個方法不是很靈活
  • 更新數據除了使用 filtered,還可使用建立的方法,只要在後面把默認修改狀態改成 true
realm.create('dtList'{realmId :realmId,editDataType:'DELETE'},true);
複製代碼

須要注意的還有一點:

根據文檔上的解釋:

If your model class includes a primary key, you can have Realm intelligently update or add objects based off of their primary key values. This is done by passing true as the third argument to the create method:

須要設置 primary key,這個一開始我覺得是隨便填,原來用處是在這裏,這個 primary key 是跟 properties 內的一個參數相關聯,所以須要設置一個相似 id 的主鍵,以此來作修改指定數據的依據。

除了用 id 作主鍵,若是存儲的數據大,而且還涉及到與後臺的信息同步,那就能夠用生成隨機 UDID 的方法:

export function  getUUID(){
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
 		}).toUpperCase();
}
複製代碼

這樣在建立數據或者修改的時候就不會出現涉及到被覆蓋的數據。

在結束操做的時候,須要執行關閉數據庫操做的處理,這樣避免 realm 一直佔用線程資源,程序發生奔潰現象。

realm.close()

相關文章
相關標籤/搜索