realm使用小结

一. 准备工作

  • Android Studio 版本大于 1.5.1
  • JDK 版本大于 7.0
  • 最新的 Android SDK 版本
  • Android API 大于等于 9

在项目的build.gradle中配置相关信息。

1
2
3
dependencies {
classpath "io.realm:realm-gradle-plugin:6.0.1"
}

在app的build.gradle中添加realm插件,并且导入realm的扩展库,方便我们使用。

1
apply plugin: 'realm-android'
1
implementation "com.github.vicpinm:krealmextensions:2.5.0"

二. 初始化及配置

  1. 初始化数据库
1
Realm.init(context);
  1. 配置数据库

    可以通过修改realm的RealmConfiguration来对reaml进行配置。几个比较常用的配置:

    • name : 指定数据库的名称。如不指定默认名为default

    • schemaVersion : 指定数据库的版本号。

    • encryptionKey : 指定数据库的密钥。

    • migration: 指定迁移操作的迁移类。

    • deleteRealmIfMigrationNeeded : 版本冲突时自动删除原数据库。

  1. 建表

    创建model类,继承自RealmObject,realm支持boolean, byte, short, int, long, float, double, String, Date and byte[] 以及realmObject子类等类型。 几种常用的注解:

    • @PrimaryKey——表示该字段是主键
    • @Required——表示该字段非空
    • @Ignore——表示忽略该字段
    • @Index——添加搜索索引

以下是一个订单表的简单示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
open class RTOrderEntity : RealmObject() {
@PrimaryKey
var orderId: String? = null // 订单号
@Required
var dutyCode: String? = "" // 班次号
var orderPaymentStatus: Int? = 0 //0未支付,1已支付,2部分支付
var payChannels: RealmList<RTPayChanelEntity>? = null //支付方式列表
var payTime: Long? = null //成交时间
...
}
  1. 将表配置到数据库中。

    1
    2
    // config参数为之前配置的realmConfiguration
    RTRealmConfig.init(RTOrderEntity::class.java, myConfig)

    realmConfig中为了配合realmExtension,对所有表的名称以及realmConfiguration做了键值对的存储,方便我们后面增删改查使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var TAG = RTRealmConfig::class.java.simpleName
    private var configMap: MutableMap<Class<out RealmModel>, RealmConfiguration> = HashMap()
    fun <T : RealmModel> init(modelClass: Class<T>, realmCfg: RealmConfiguration) {
    Logger.d(TAG, "Adding class $modelClass to realm ${realmCfg.realmFileName}")
    if (!configMap.containsKey(modelClass)) {
    configMap.put(modelClass, realmCfg)
    }
    }

三. 基本功能

  1. 添加

    如果我们没有使用RealmExtension需要这么写.

    1
    2
    3
    4
    5
    6
    7
    var orderEntity = RTOrderEntity()
    var realmInstance = Realm.getDefaultInstance()
    order.orderId = "123"
    realmInstance.transaction { realm ->
    realm.copyToRealmOrUpdate(orderEntity)
    }
    realmInstance.close()

    每次都要手动处理数据库的关闭,处理相关的事务,非常繁琐。RealmExtension把这些操作封装了一层,所以我们现在只需要这样写:

    1
    2
    3
    4
    var orderEntity = RTOrderEntity()
    //新增了一个订单id为123的订单表
    orderEntity.orderId = "123"
    orderEntity.save()
  2. 查询

    查询相比插入来说不需要在事务中操作,直接查询即可。查询是一种链式查询的方式,将查询条件拼接到后面即可,支持异步查询。平时我们使用比较多的查询有:

    • findAll ——查询所有
    • findAllAsync——异步查询
    • findFirst ——查询第一条数据
    • equalTo ——根据条件查询

    下面是一个简单的查询例子,具体的一些查询条件也就是

    RealmQuery 在官网有详细的介绍。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    orderEntity().queryAsync({
    beginGroup()
    .isNotNull("orderId")
    .isEmpty("stallCategoryCode")
    .notEqualToValue("status", 9)
    .equalToValue("isSale", 1)
    .not()
    .beginsWith("cateringTypeString", "1361")
    .isNotEmpty("stallCode")
    endGroup()
    }) { results ->
    //查询结果
    }
  3. 删除

    删除也非常简单,可以删除表中所有内容或者按条件删除。

    1
    2
    3
    4
    //删除了所有订单表
    RTOrderEntity().deleteAll()
    //删除了订单id为123的表
    RTOrderEntity().delete { equalTo("orderId", "123") }
  4. 修改

    修改和查询类似,根据相应的RealmQuery条件将某条数据查询出来之后进行修改即可。

四. 进阶功能

1.数据库备份和恢复。

数据库备份的话首先需要文件的读写权限,然后备份的话首先指定一个备份的路径和备份数据库的名称,然后将当前数据库的文件通过 writeCopyTo 写入。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var exportRealmFile: File? = null
val realm: Realm = Realm.getInstance(RealmUtils.myConfig)
Logger.d(TAG, "Realm DB Path = " + realm.path)
try {
EXPORT_REALM_PATH.mkdirs()
//参数1:文件路径 参数2: 文件名
exportRealmFile = File(EXPORT_REALM_PATH, EXPORT_REALM_FILE_NAME)
exportRealmFile.delete()
realm.writeCopyTo(exportRealmFile)
} catch (e: Exception) {
e.printStackTrace()
}
val msg = "File exported to Path: $EXPORT_REALM_PATH/$EXPORT_REALM_FILE_NAME"

需要注意的事情,备份的时候要确保当前数据库没有在执行任何操作。我们可以通过一个方法 Realm.getGlobalInstanceCount(RealmUtils.myConfig) 来打印当前实例出来的数据库个数,只有当个数为0的时候才能进行备份操作。接下来是恢复操作,恢复同样需要先指定恢复的路径以及恢复出来的名称,然后通过 outputStreaminputStream 来写入文件。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val file = File(context.filesDir, outFileName)
Log.d(TAG, "context.getFilesDir() = " + context.filesDir.toString())
val outputStream = FileOutputStream(file)
val inputStream = FileInputStream(File(oldFilePath))
val buf = ByteArray(1024)
var bytesRead: Int
while (inputStream.read(buf).let {
bytesRead = it; it > 0 }) {
outputStream.write(buf, 0, bytesRead)
}
outputStream.close()

2.数据库迁移

当已经发了一个版本,下个版本中某个表中要添加或者删除字段,或者要新增表的这些操作就需要升级数据库,也就是进行数据库迁移,数据库迁移需要新建 RealmMigration 迁移类。并且在build RealmConfiguration的时候指定这个迁移类。我们还是拿上边的订单表举例,新版本中需要在订单表中新增一个订单数量的字段,在迁移类中重写migrate方法,方法中有新旧版本号两个参数,可以帮助我们进行一些版本的逻辑判断。然后取到数据库的schema,它有很多方法,比如

  • 新增 addField
  • 重命名 renameField
  • 删除 removeField

可以通过这些方法对表中的字段进行修改。

1
2
3
4
5
6
7
8
9
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
val schedulechema = realm.schema.get(RTOrderEntity::class.java.simpleName)
schedulechema?.let {
if (!it.hasField("orderNum")){
it.addField("orderNum", Long.javaClass)
}
}
}

3.查看数据库的工具

比较常用的realm可视化工具有 RealmStudioRealm Browser 两者大体差不多,可以直接打开数据库文件查看,并且在里面执行相应的查询筛选操作。