移动数据库 Realm 在 React-Native 中的使用中文详解

数据持久化一直都是软件开发中重要的一个环节,几乎所有的应用都具备这一项功能;那什么是数据持久化呢?—— 说白了就是数据的本地化存储,将数据存储到本地,在需要的时候进行调用。

这边我们介绍两种在 React-Native 中比较常用的存储方式

AsyncStorage:这是官方使用的存储方式,类似于 iOS 中的 NSUserDefault ,区别在于,AsyncStorage 只能存储 字符串键值对,而 NSUserDefault 可以存储 字符串和 number

Realm:与 SQList 进行数据存储相比,在性能上,各有优势,但是操作上,Realm 有明显优势,更方便使用。其中囊括了各端的使用,包括

接下来我们就来看看怎么使用 Realm

1.安装

npm install --save realm

2.链接

react-native link realm

需要注意有两点:

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 上用很快,很安全,很棒棒的。

来看一个例子:

//在文件中引入 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

用于查看存储在本地的数据

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()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容