NoSQLDatabase 提供了一个支持即时同步功能的 NoSQL 数据库; 开发者可以使用 NoSQLDatabase 存储, 查询, 同步 JSON 数据对象。
NoSQLDatabase
表示一个数据库的实例, 用于对数据库进行设置和操作, 以及获取数据节点引用 (NodeReference)所有数据操作需要通过数据节点的引用进行。
Summary
Constructor | Description |
| 创建一个 namespace 下指定数据空间的数据库实例。 |
Returns | Public Methods |
void |
|
void |
|
void |
|
NodeReference |
|
void |
|
void |
|
void |
|
Constructor
NoSQLDatabase(Context context, String accessKey, String namespace, String shardId)
创建一个 namespace 下指定数据空间的数据库实例。
Parameters | type | Description |
context | String | Application context。 |
accessKey | String | 项目关联的网关应用AppKey。 |
namespace | String | 指定要访问 SyncStore 的 namespace。 |
shardId | String | 标示本地不同数据空间的本地数据库, 建议传入数据空间的 id(即账号 id 或设备 id); 如果本地同时存在多个不同数据空间的数据库副本 (如切换账号时), 必须使用不同的 shardId 加以区分, 否则本地不同数据空间的数据会混乱。 |
Throws | Description |
DatabaseException | 初始化字段不合法。 |
注意
应用中同一配置的数据库应当只被创建一次并保持单例, 严格避免重复创建相同配置的数据库实例; 使用不同的 db connection 操作同一个 db 可能出现 db 锁死, 参考 http://www.sqlite.org/cvstrac/wiki?p=DatabaseIsLocked不同配置的数据库实例可以共存。
Example
初始化数据库实例
NoSQLDatabase db = new NoSQLDatabase(
context,
accessKey, // 项目关联的网关应用AppKey
namespace, // 在控制台上申请的 namespace
shardId, // 标示本地不同数据空间的本地数据库, 建议传入数据空间的id (即账id 或设备id); 如果本地同时存在多个不同数据空间的数据库副本(如切换账号时), 必须重新创建数据库并使用不同的 shardId 加以区分, 否则本地不同数据空间的数据会混乱
endPoint // 服务地址, https:// | http:// 开头
);
Public Methods
setAccountToken(String token)
将当前账号鉴权体系的 token 传给数据库实例, 用于云端资源访问鉴权。
在数据库处于 online 状态时, 设置有效 token 后数据库会开始进行同步token 无效或失效后, 数据库实例会发出 onTokenInvalid
事件, 表示云端资源鉴权失败, 需要开发者进行相处理。
Parameters | type | Description |
token | String | 账号 token。 |
setDeviceToken(String token)
将当前设备鉴权体系的 token 传给数据库实例, 用于云端资源访问鉴权。
在数据库处于 online 状态时, 设置有效 token 后数据库会开始进行同步token 无效或失效后, 数据库实例会发出 onTokenInvalid
事件, 表示云端资源鉴权失败, 需要开发者进行相处理。
Parameters | type | Description |
token | String | 设备 token。 |
goOnline()
将数据库实例设置为在线模式, 如果网络连通且 token 有效, 任何本地修改都会被立即同步到云端, 云端的修改也会准实时地同步到本地。
goOffline()
将数据库实例设置为离线模式在离线模式下, 本地的变更不会同步到云端, 云端的变更也不会通知到本地。
数据库实例创建之后默认为离线状态, 在调用 goOnline() 之后进入在线状态。
getReference(String path)
获取一个节点的引用(NodeReference)所有对节点的读写, 监听数据变更都需要通过节点引用进行。
Parameters | type | Description |
path | Stirng | 节点的路径, 唯一标识一个 JSON 对象中的节点; 和文件路径相同, 以 “/“ 分隔, 如 a/b 标识 |
Returns | Description |
NodeReference | path 对应的节点的引用。 |
Throws | Description |
DatabaseException | path 为空, path 包含非法字符, path 长度过长, 获取到的 ref 层级超过最大深度。 |
Example
NodeReference ref1 = db.getReference("/a/b");
NodeReference ref2 = db.getReference("a/b");
NodeReference ref3 = db.getReference("a/b/");
NodeReference ref4 = db.getReference("a//b");
// ref1, ref2, ref3, ref4 指向同一位置
NodeReference ref = db.getReference("a/b/c/d/e/f/g/i/h");
// Error: path 深度超过上限
close(boolean dropDatabase)
关闭当前数据库实例关闭数据库后对 db 实例进行的操作均无效, 对 db 上创建的 ref 进行读写操作均返回错误。
Parameters | type | Description |
dropDatabase | boolean | 是否删除当前数据库。 |
setOnDatabaseEventListener(OnDatabaseEventListener listener)
监听数据库发出的事件。
Parameters | type | Description |
listener | OnDatabaseEventListener | 事件回调。 |
event 包括:
onSyncStart()
同步开始。onSyncFinish(int code)
同步结束, code 标示同步是否成功, 0 为成功。onSyncError(DatabaseException e)
返回同步过程中遇到的错误, 用于开发调试, e.code 为错误码。onTokenInvalid(int code)
表示云端资源鉴权失败, 需要开发者进行相处理, err.code 为错误码; 此时数据库同步停止 (offline); 开发者需要重新获取相应体系的 token 并传给 db。
错误码参考
编号 | 含义 | 备注 |
0 | 成功 | 处理成功。 |
10000 | 参数为空 | 必选参数为空。 |
10001 | 错误的JSON层级 | 例如上传的JSON树超过8层。 |
10002 | 错误的参数格式 | 例如上传的JSON格式不正确。 |
10003 | 参数错误 | 例如path错误。 |
10004 | 版本错误 | 同步协议相关。 |
10005 | 生成密钥失败 | 管理平台相关-添加namespace。 |
10006 | token错误 | accesstoken或者uuidtoken错误。 |
10007 | 认证类型错误 | 认证类型和namespace不一致。 |
10008 | 获取锁失败 | 并发冲突。 |
10009 | triggerpath存在 | 管理平台相关-添加trigger。 |
11001 | 账号token错误 | 账号token错误。 |
11002 | 设备token错误 | 设备token错误。 |
11003 | YunOS账号token错误 | YunOS账号token错误。 |
11004 | YunOS设备token错误 | YunOS设备token错误。 |
20001 | 存储数据失败 | 服务存储数据失败。 |
20002 | 获取数据失败 | 服务获取数据失败。 |
1 | 失败 | 流程出现异常。 |
Example
NoSQLDatabase db = new NoSQLDatabase(...);
db.setOnDatabaseEventListener(new OnDatabaseEventListener() {
@Override
public void onSyncStart() {
Log.d(TAG, "sync start");
}
@Override
public void onSyncFinish(int code) {
Log.d(TAG, "sync finish");
// code 为 0 代表成功
}
@Override
public void onSyncError(DatabaseException e) {
Log.d(TAG, "sync error: " + e.toString());
// 返回同步过程中遇到的错误, 用于开发调试
}
@Override
public void onTokenInvalid(int code) {
// token 无效后同步自动停止, code 反映具体哪个 token 无效
// 详见上文“错误码参考”中 11XXX 系列错误码
Log.d(TAG, "token invalid: " + Srting.valueOf(code));
// 需要重新获取并传入 auth 信息
db.setXXXToken(XXXToken);
// 重新继续同步
db.goOnline();
}
});
NodeReference
数据节点的引用, 用于对数据节点进行操作, 以及监听本节点数据的变更。
注意
ref 自身不包含任何数据, 只代表数据库中 JSON 结构某一位置的一个引用。
Summary
Returns | Public Methods |
NodeReference |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
String |
|
void |
|
void |
|
void |
|
void |
|
void |
|
void |
|
void |
|
void |
|
void |
|
void |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
NodeReference |
|
Public Methods
copy()
复制一个当前节点的引用对象,节点上的数据视图不会被复制。
Returns | Description |
NodeReference | 复制的引用对象。 |
child(String path)
根据相对路径获取一个子节点的引用对象。
Parameters | type | Description |
path | String | 子节点与当前节点的相对路径。 |
Throws | Description |
DatabaseException | path 为空,path 包含非法字符,path 长度过长,获取到的 ref 层级超过最大深度。 |
Returns | Description |
NodeReference | 子节点的引用对象。 |
parent()
获取当前节点父节点的引用对象。
Returns | Description |
NodeReference | 父节点的引用对象。 |
root()
获取根节点的引用对象根节点之不能进行写操作 (set,update,push)。
Returns | Description |
NodeReference | 根节点的引用对象。 |
path()
获取当前节点引用的全路径。
Returns | Description |
String | 当前节点引用的全路径。 |
setOnDataChangeListener(OnDataChangeListener l)
监听当前 ref 上的云端数据变更事件只有在注册状态下才能收到云端变更回调,默认为启用注册状态。
Parameters | type | Description |
listener | OnDataChangeListener | 事件回调。 |
eventName 包括:
onChange(JSONObject newObject, JSONObject conflictObject)
表示接收到云端 ref 中的数据变化或 ref 自身发生变化。newObject
数据变化后 ref 下的数据内容。conflictObject
本次变更的冲突内容,如果本次数据更新没有冲突则无此字段。
注意
事件监听根据监听的范围不同会产生一定的消耗,监听的范围越大,产生的消耗也越大监听事件触发时会返回 ref 下所有数据,请避免在会返回大量数据的 ref 上进行监听,如只关心变化事件,可使用 ref.orderByXXX().limitToFirst() 等数据视图进行过滤以减少数据返回量请保证只对必要的范围进行监听,并且在不需要时及时取消监听。
Example: 数据冲突
t 时刻之前云和端保持同步,数据为:
{
"a": {
"b": 1
"c": 100
}
}
t+1 时客户端断网,客户端将数据变为:
{
"a": {
"b": 2,
"c": 100
}
}
云端将数据变为数据为:
{
"a": {
"b": 3,
"c": 100
}
}
t+2 时刻客户端联网,同步时产生冲突:
ref.setOnDataChangeListener(new OnDataChangeListener() {
@Override
public void onChange(JSONObject newObject,JSONObject conflictObject) {
if (conflictObject != null) {
// 更新后有冲突
// newObject 本地更新后的数据(包括成功合并的没有冲突部分的云端数据)
// conflictObject 冲突部分的云端数据
JSONObject local = newObject; // {"a": {"b": 2,"c": 100}}
JSONObject conflicts = conflictObject; // {"a": {"b": 3}}
// 开发者可选择应用冲突数据的云端版本
ref.update(conflictObject,null);
} else {
// 更新后没有冲突
}
});
removeOnDataChangeListener()
取消节点引用上的数据变更事件监听取消后数据库接收到云端数据变更后不会向 ref 发出变更事件; 如有数据变更 ref 的事件监听收不到回调。
get(GetCallback callback)
获取本节点下的数据对象如有数据视图,则获取数据视图过滤之后部分的数据,否则获取本节点下全部数据。
注意
get() 会读取并创建数据对象,会占用内存get() 不限制读出的数据量大小,如果读出大量数据,有耗尽内存的风险!
Parameters | type | Description |
callback | GetCallback | 操作回调。 |
keys(KeysCallback callback)
获取本节点下所有 key 的列表。
Parameters | type | Description |
callback | KeysCallback | 操作回调。 |
set(JSONObject object,PutCallback callback)
向本节点写入数据,已经存在的数据会被替换。
注意
不能在根节点的引用上进行写操作。
Parameters | type | Description |
obj | JSONObject | 要写入的数据。 |
callback | PutCallback | 操作回调。 |
update(JSONObject obj,PutCallback callback)
向本节点写入新数据,已有数据与新写入的数据合并,重复的数据被新值覆盖 (所有写入的数据,无论写入的值是否和之前不同,都会被同步)。
注意
不能在根节点的引用上进行写操作。
Parameters | type | Description |
obj | JSONObject | 要写入的数据。 |
callback | PutCallback | 操作回调。 |
updateWithDiff(JSONObject obj,PutCallback callback)
向本节点写入数据,功能与 update 相同,只有新值中与旧值不同(或旧值不存在)的部分会被写入(并且只有值发生变化的部分数据会被同步),性能低于 update。
注意
不能在根节点的引用上进行写操作。
Parameters | type | Description |
obj | JSONObject | 要写入的数据。 |
callback | PutCallback | 操作回调。 |
push(JSONObject,PutCallback callback)
向节点中写入个 k-v 对,k 的值为自动生成的自增 id,v 的值为传入的 value。
注意
不能在根节点的引用上进行写操作自动生成的 id 以本地时间为基础进行自增, 本地时间的调整可能会破坏 id 的有序性。
Parameters | type | Description |
object | JSONObject | 要写入的数据。 |
callback | PutCallback | 操作回调。 |
Example
数据库中有以下内容:
{
"notes": {
...
}
}
NodeReference ref = db.getReference("notes");
JSONObject note;
// note 内容为
// {
// "title": "note1",
// "content": "my note"
// }
ref.push(note, new PutCallback() {
@Override
public void onSuccess() {
// 数据库内容变为
// {
// "notes": {
// ...
// "-Kr4cMSWIFQzmEG588Xe": { // 自动生成的 id
// "title": "note1",
// "content": "my note"
// }
// }
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
delete(String path, PutCallback callback)
从当前节点中删除某个 path 代表的子节点。
Parameters | type | Description |
path | String | 要删除的子节点的相对路径。 |
callback | PutCallback | 操作回调。 |
remove(PutCallback callback)
删除节点自身。
Parameters | type | Description |
callback | PutCallback | 操作回调。 |
orderByChild(String field)
数据视图-排序条件:数据视图匹配当前节点对象下所有对象节点的 filed 字段,没有 field 字段的节点或非对象节点不会参与匹配也不会在数据视图中返回如果同时有指定 limit 条件, 则返回由 filed 字段进行排序后 limit 范围内部分的数据集合。
此方法可以与 limitToFirst / limitToLast 和 startAt / endAt / equalTo 方法联合使用如不指定 limitToFirst / limitToLast 则默认为 limitToFirst(1024)。
注意
排序条件只影响查询时获取的结果范围,不影响结果的有序性不返回值节点。
Parameters | type | Description |
field | String | 查询的对象中要排序的属性名。 |
Returns | Description |
NodeReference | 当前节点引用自身。 |
Example:
数据库中有以下内容:
{
"notes": {
"Fumcjda-cja8dand": {
"title": "note1",
"content": "some content",
"time": 1497506811474
},
"adfIead-I9ewniwe": {
"title": "note2",
"content": "some content",
"time": 1497506851057
},
...
"Moviene-8dna0dJe": {
"title": "note3",
"content": "some content",
"time": 1497506854890
},
"Ondafsd-UQiefasd": "this is a value"
}
}
获取最近的 3 条 notes 的集合数据视图匹配当前节点下所有对象节点中的 time 字段, 没有 time 字段的节点或非对象节点不会参与匹配, 如 "Ondafsd-UQiefasd": "this is a value"
这个值节点不会进入数据视图。
NodeReference ref = db.getReference("notes");
ref.orderByChild("time").limitToLast(3);
注意
排序条件只指定结果包含最近的 3 条数据,但不保证结果中 3 个节点的排列顺序 (因为 Object key 的无序性),如在结果对象上进行 key 遍历,无法保证第一条是 time 最小的一条。
ref.get(new GetCallback() {
@Override
public void onSuccess(JSONObject object) {
// 返回最近三条结果
// {
// "Fumcjda-cja8dand": {
// "title": "note1",
// "content": "some content",
// "time": 1497506811474
// },
// "adfIead-I9ewniwe": {
// "title": "note2",
// "content": "some content",
// "time": 1497506851057
// },
// "Moviene-8dna0dJe": {
// "title": "note3",
// "content": "some content",
// "time": 1497506854890
// }
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
若使用关系型数据库存储以上数据,每个 note 为一行,以上查询可类比为:
select * from notes order by time limit 3;
orderByKey()
数据视图-排序条件:数据视图指定返回当前节点下所有数据(包括值节点和对象节点)如果指定了排序条件, 则返回由 key 进行排序后 limit 范围内部分的数据集合。
此方法可以与 limitToFirst / limitToLast 和 startAt / endAt / equalTo 方法联合使用如不指定 limitToFirst / limitToLast 则默认为 limitToFirst(1024)。
注意
排序条件只影响查询时获取的结果范围,不影响结果的有序性不指定排序条件时本条件无意义(仍然返回本节点下所有数据)。
Returns | Description |
NodeReference | 当前节点引用自身。 |
Example:
数据库中有以下内容:
{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": {"k4": "v4"},
"k5": {"k5": "v5"},
"k6": {"k6": "v6"},
}
获取本层下按 key 排序的前 4 条数据的集合数据视图匹配当前节点下所有节点中的 key, 返回最大的 4 条的集合。
NodeReference ref = db.getReference("/");
ref.orderByKey().limitToLast(4);
ref.get(new GetCallback() {
@Override
public void onSuccess(JSONObject object) {
// 返回值节点和对象节点
// {
// "k1": "v1",
// "k2": "v2",
// "k3": "v3",
// "k4": {"k4": "v4"}
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
orderByValue()
数据视图-排序条件:数据视图匹配当前节点对象下所有值节点,对象节点不会参与匹配也不会在数据视图中返回如果指定了排序条件,则返回由值节点的值进行排序后 limit 范围内部分的数据集合。
此方法可以与 limitToFirst / limitToLast 和 startAt / endAt / equalTo 方法联合使用如不指定 limitToFirst / limitToLast 则默认为 limitToFirst(1024)。
注意
排序条件只影响查询时获取的结果范围,不影响结果的有序性不返回对象节点。
Returns | Description |
NodeReference | 当前节点引用自身。 |
Example:
数据库中有以下内容:
{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": {"k4": "v4"},
"k5": {"k5": "v5"},
"k6": {"k6": "v6"},
}
获取本层下按 value 排序的前 4 条数据的集合。
NodeReference ref = db.getReference("/");
ref.orderByValue().limitToLast(4);
ref.get(new GetCallback() {
@Override
public void onSuccess(JSONObject object) {
// 不返回对象节点
// {
// "k1": "v1",
// "k2": "v2",
// "k3": "v3",
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
limitToFirst(int limit)
数据视图-数量:正序排列,一共返回 limit 个节点。
此方法需要与 orderByChild,orderByKey,orderByValue 方法联合使用,可以与 startAt / endAt / equalTo 方法联合使用。
Parameters | type | Description |
limit | int | 返回的结果数量。 |
Returns | Description |
NodeReference | 当前节点引用自身。 |
limitToLast(int limit)
数据视图-数量:倒序排列,一共返回 limit 个节点。
此方法需要与 orderByChild,orderByKey,orderByValue 方法联合使用,可以与 startAt / endAt / equalTo 方法联合使用。
Parameters | type | Description |
limit | int | 返回的结果数量。 |
Returns | Description |
NodeReference | 当前节点引用自身。 |
startAt(String value)
数据视图-过滤条件:过滤 orderBy 中指定的字段,返回大于等于指定的 value 的节点。
此方法需要与 orderByChild,orderByKey,orderByValue 方法联合使用,可以与 limitToFirst / limitToLast 方法联合使用。
Parameters | type | Description |
value | String | 过滤条件的值。 |
Returns | Description |
NodeReference | 当前节点引用自身。 |
Example:
以下实例展示 orderBy 和过滤条件组合产生的过虑行为。
数据库中有以下内容:
{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": {"k": "v4"},
"k5": {"k": "v5"},
"k6": {"k": "v6"}
}
查询子对象节点中 “k” 字段的值大于等于 “v5” 的
NodeReference ref = db.getReference("/");
ref.orderByChild("k").startAt("v5");
ref.get(new GetCallback() {
@Override
public void onSuccess(JSONObject object) {
// {
// "k5": {"k": "v5"},
// "k6": {"k": "v6"}
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
查询子节点中 key 的值大于等于 “k3” 的
NodeReference ref = db.getReference("/");
ref.orderByKey().startAt("k3");
ref.get(new GetCallback() {
@Override
public void onSuccess(JSONObject object) {
// {
// "k3": "v3",
// "k4": {"k": "v4"},
// "k5": {"k": "v5"},
// "k6": {"k": "v6"}
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
查询子值节点中 value 的值大于等于 “v2” 的
NodeReference ref = db.getReference("/");
ref.orderByValue().startAt("v2");
ref.get(new GetCallback() {
@Override
public void onSuccess(JSONObject object) {
// {
// "k2": "v2",
// "k3": "v3"
// }
}
@Override
public void onError(DatabaseException e) {
// error
}
});
endAt(String value)
数据视图-过滤条件:过滤 orderBy 中指定的字段,返回小于等于指定的 value 的节点。
此方法需要与 orderByChild,orderByKey,orderByValue 方法联合使用,可以与 limitToFirst / limitToLast 方法联合使用。
Parameters | type | Description |
value | String | 过滤条件的值。 |
Returns | Description |
NodeReference | 当前节点引用自身。 |
equalTo(String value)
数据视图-过滤条件:过滤 orderBy 中指定的字段,返回等于指定的 value 的节点。
Parameters | type | Description |
value | string/number/boolean | 过滤条件的值。 |
Returns | Description |
NodeReference | 当前节点引用自身。 |