1# 关系型数据库跨设备数据同步
2
3
4## 场景介绍
5
6当应用程序本地存储的关系型数据存在跨设备同步的需求时,可以将需要同步的表数据迁移到新的支持跨设备的表中,当然也可以在刚完成表创建时设置其支持跨设备。
7
8
9## 基本概念
10
11关系型数据库跨设备数据同步,支持应用在多设备间同步存储的关系型数据。
12
13- 应用在数据库中新创建表后,可以设置其为分布式表。在查询远程设备数据库时,根据本地表名可以获取指定远程设备的分布式表名。
14
15- 设备之间同步数据,数据同步有两种方式,将数据从本地设备推送到远程设备或将数据从远程设备拉至本地设备。
16
17
18## 运作机制
19
20底层通信组件完成设备发现和认证,会通知上层应用程序设备上线。收到设备上线的消息后数据管理服务可以在两个设备之间建立加密的数据传输通道,利用该通道在两个设备之间进行数据同步。
21
22
23### 数据跨设备同步机制
24
25![relationalStore_sync](figures/relationalStore_sync.jpg)
26
27业务将数据写入关系型数据库后,向数据管理服务发起同步请求。
28
29数据管理服务从应用沙箱内读取待同步数据,根据对端设备的deviceId将数据发送到其他设备的数据管理服务。再由数据管理服务将数据写入同应用的数据库内。
30
31
32### 数据变化通知机制
33
34增、删、改数据库时,会给订阅者发送数据变化的通知。主要分为本地数据变化通知和分布式数据变化通知。
35
36- **本地数据变化通知**:本地设备的应用内订阅数据变化通知,数据库增删改数据时,会收到通知。
37
38- **分布式数据变化通知**:同一应用订阅组网内其他设备数据变化的通知,其他设备增删改数据时,本设备会收到通知。
39
40
41## 约束限制
42
43- 每个应用程序最多支持同时打开16个关系型分布式数据库。
44
45- 单个数据库最多支持注册8个订阅数据变化的回调。
46
47
48## 接口说明
49
50以下是关系型设备协同分布式数据库跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见[关系型数据库](../reference/apis-arkdata/js-apis-data-relationalStore.md)。
51
52| 接口名称 | 描述 |
53| -------- | -------- |
54| setDistributedTables(tables: Array<string>, callback: AsyncCallback<void>): void | 设置分布式同步表。 |
55| sync(mode: SyncMode, predicates: RdbPredicates, callback: AsyncCallback<Array<[string, number]>>): void | 分布式数据同步。 |
56| on(event: 'dataChange', type: SubscribeType, observer: Callback<Array<string>>): void | 订阅分布式数据变化。 |
57| off(event:'dataChange', type: SubscribeType, observer: Callback<Array<string>>): void | 取消订阅分布式数据变化。 |
58| obtainDistributedTableName(device: string, table: string, callback: AsyncCallback<string>): void | 根据本地数据库表名获取指定设备上的表名。 |
59| remoteQuery(device: string, table: string, predicates: RdbPredicates, columns: Array<string> , callback: AsyncCallback<ResultSet>): void | 根据指定条件查询远程设备数据库中的数据。 |
60
61
62## 开发步骤
63
64> **说明:**
65>
66> 数据只允许向数据安全标签不高于对端设备安全等级的设备同步数据,具体规则可见[跨设备同步访问控制机制](access-control-by-device-and-data-level.md#跨设备同步访问控制机制)。
67
681. 导入模块。
69
70   ```ts
71   import { relationalStore } from '@kit.ArkData';
72   ```
73
742. 请求权限。
75
76   1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见[声明权限](../security/AccessToken/declare-permissions.md)。
77   2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/AccessToken/request-user-authorization.md)。
78
793. 创建关系型数据库,设置将需要进行分布式同步的表。
80
81   ```ts
82   import { UIAbility } from '@kit.AbilityKit';
83   import { BusinessError } from '@kit.BasicServicesKit';
84   import { window } from '@kit.ArkUI';
85
86   class EntryAbility extends UIAbility {
87     onWindowStageCreate(windowStage: window.WindowStage) {
88       const STORE_CONFIG: relationalStore.StoreConfig = {
89         name: "RdbTest.db",
90         securityLevel: relationalStore.SecurityLevel.S3
91       };
92
93       relationalStore.getRdbStore(this.context, STORE_CONFIG, (err: BusinessError, store: relationalStore.RdbStore) => {
94         store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', (err) => {
95           // 设置分布式同步表。
96           store.setDistributedTables(['EMPLOYEE']);
97           // 进行数据的相关操作
98         })
99       })
100     }
101   }
102   ```
103
1044. 分布式数据同步。使用SYNC_MODE_PUSH触发同步后,数据将从本设备向组网内的其它所有设备同步。
105
106   ```ts
107   // 构造用于同步分布式表的谓词对象
108   let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
109   // 调用同步数据的接口
110   if(store != undefined)
111   {
112     (store as relationalStore.RdbStore).sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicates, (err, result) => {
113       // 判断数据同步是否成功
114       if (err) {
115         console.error(`Failed to sync data. Code:${err.code},message:${err.message}`);
116         return;
117       }
118       console.info('Succeeded in syncing data.');
119       for (let i = 0; i < result.length; i++) {
120         console.info(`device:${result[i][0]},status:${result[i][1]}`);
121       }
122     })
123   }
124   ```
125
1265. 分布式数据订阅。数据同步变化将触发订阅回调方法执行,回调方法的入参为发生变化的设备ID。
127
128   ```ts
129   let devices: string | undefined = undefined;
130   try {
131     // 调用分布式数据订阅接口,注册数据库的观察者
132     // 当分布式数据库中的数据发生更改时,将调用回调
133     if(store != undefined) {
134       (store as relationalStore.RdbStore).on('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (storeObserver)=>{
135         if(devices != undefined){
136           for (let i = 0; i < devices.length; i++) {
137             console.info(`The data of device:${devices[i]} has been changed.`);
138           }
139         }
140       });
141     }
142   } catch (err) {
143     console.error('Failed to register observer. Code:${err.code},message:${err.message}');
144   }
145   // 当前不需要订阅数据变化时,可以将其取消。
146   try {
147     if(store != undefined) {
148       (store as relationalStore.RdbStore).off('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (storeObserver)=>{
149       });
150     }
151   } catch (err) {
152     console.error('Failed to register observer. Code:${err.code},message:${err.message}');
153   }
154   ```
155
1566. 跨设备查询。如果数据未完成同步,或未触发数据同步,应用可以使用此接口从指定的设备上查询数据。
157
158   > **说明:**
159   >
160   > deviceIds通过调用[deviceManager.getAvailableDeviceListSync](../reference/apis-distributedservice-kit/js-apis-distributedDeviceManager.md#getavailabledevicelistsync)方法得到。
161
162
163   ```ts
164   // 获取deviceIds
165   import { distributedDeviceManager } from '@kit.DistributedServiceKit';
166   import { BusinessError } from '@kit.BasicServicesKit';
167
168   let dmInstance: distributedDeviceManager.DeviceManager;
169   let deviceId: string | undefined = undefined ;
170
171   try {
172     dmInstance = distributedDeviceManager.createDeviceManager("com.example.appdatamgrverify");
173     let devices = dmInstance.getAvailableDeviceListSync();
174
175     deviceId = devices[0].networkId;
176
177     // 构造用于查询分布式表的谓词对象
178     let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
179     // 调用跨设备查询接口,并返回查询结果
180     if(store != undefined && deviceId != undefined) {
181       (store as relationalStore.RdbStore).remoteQuery(deviceId, 'EMPLOYEE', predicates, ['ID', 'NAME', 'AGE', 'SALARY', 'CODES'],
182         (err: BusinessError, resultSet: relationalStore.ResultSet) => {
183           if (err) {
184             console.error(`Failed to remoteQuery data. Code:${err.code},message:${err.message}`);
185             return;
186           }
187           console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
188         }
189       )
190     }
191   } catch (err) {
192     let code = (err as BusinessError).code;
193     let message = (err as BusinessError).message;
194     console.error("createDeviceManager errCode:" + code + ",errMessage:" + message);
195   }
196   ```
197
198## 相关实例
199
200针对关系型数据库开发,有以下相关实例可供参考:
201
202- [分布式组网认证(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/DistributedAppDev/DistributedAuthentication)
203
204- [分布式关系型数据库(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedRdb)
205
206- [分布式账号(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/DistributedAppDev/DistributedAccount)