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