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 51以下是关系型设备协同分布式数据库跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见[关系型数据库](../reference/apis-arkdata/js-apis-data-relationalStore.md)。 52 53| 接口名称 | 描述 | 54| -------- | -------- | 55| setDistributedTables(tables: Array<string>, callback: AsyncCallback<void>): void | 设置分布式同步表。 | 56| sync(mode: SyncMode, predicates: RdbPredicates, callback: AsyncCallback<Array<[string, number]>>): void | 分布式数据同步。 | 57| on(event: 'dataChange', type: SubscribeType, observer: Callback<Array<string>>): void | 订阅分布式数据变化。 | 58| off(event:'dataChange', type: SubscribeType, observer: Callback<Array<string>>): void | 取消订阅分布式数据变化。 | 59| obtainDistributedTableName(device: string, table: string, callback: AsyncCallback<string>): void | 根据本地数据库表名获取指定设备上的表名。 | 60| remoteQuery(device: string, table: string, predicates: RdbPredicates, columns: Array<string> , callback: AsyncCallback<ResultSet>): void | 根据指定条件查询远程设备数据库中的数据。 | 61 62 63## 开发步骤 64 65> **说明:** 66> 67> 数据只允许向数据安全标签不高于对端设备安全等级的设备同步数据,具体规则可见[跨设备同步访问控制机制](access-control-by-device-and-data-level.md#跨设备同步访问控制机制)。 68 691. 导入模块。 70 71 ```ts 72 import { relationalStore } from '@kit.ArkData'; 73 ``` 74 752. 请求权限。 76 77 1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见[声明权限](../security/AccessToken/declare-permissions.md)。 78 2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/AccessToken/request-user-authorization.md)。 79 803. 创建关系型数据库,设置将需要进行分布式同步的表。 81 82 ```ts 83 import { UIAbility } from '@kit.AbilityKit'; 84 import { BusinessError } from '@kit.BasicServicesKit'; 85 import { window } from '@kit.ArkUI'; 86 87 class EntryAbility extends UIAbility { 88 onWindowStageCreate(windowStage: window.WindowStage) { 89 const STORE_CONFIG: relationalStore.StoreConfig = { 90 name: "RdbTest.db", 91 securityLevel: relationalStore.SecurityLevel.S3 92 }; 93 94 relationalStore.getRdbStore(this.context, STORE_CONFIG, (err: BusinessError, store: relationalStore.RdbStore) => { 95 store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', (err) => { 96 // 设置分布式同步表。 97 store.setDistributedTables(['EMPLOYEE']); 98 // 进行数据的相关操作 99 }) 100 }) 101 } 102 } 103 ``` 104 1054. 分布式数据同步。使用SYNC_MODE_PUSH触发同步后,数据将从本设备向组网内的其它所有设备同步。 106 107 ```ts 108 // 构造用于同步分布式表的谓词对象 109 let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); 110 // 调用同步数据的接口 111 if(store != undefined) 112 { 113 (store as relationalStore.RdbStore).sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicates, (err, result) => { 114 // 判断数据同步是否成功 115 if (err) { 116 console.error(`Failed to sync data. Code:${err.code},message:${err.message}`); 117 return; 118 } 119 console.info('Succeeded in syncing data.'); 120 for (let i = 0; i < result.length; i++) { 121 console.info(`device:${result[i][0]},status:${result[i][1]}`); 122 } 123 }) 124 } 125 ``` 126 1275. 分布式数据订阅。数据同步变化将触发订阅回调方法执行,回调方法的入参为发生变化的设备ID。 128 129 ```ts 130 let devices: string | undefined = undefined; 131 try { 132 // 调用分布式数据订阅接口,注册数据库的观察者 133 // 当分布式数据库中的数据发生更改时,将调用回调 134 if(store != undefined) { 135 (store as relationalStore.RdbStore).on('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (storeObserver)=>{ 136 if(devices != undefined){ 137 for (let i = 0; i < devices.length; i++) { 138 console.info(`The data of device:${devices[i]} has been changed.`); 139 } 140 } 141 }); 142 } 143 } catch (err) { 144 console.error('Failed to register observer. Code:${err.code},message:${err.message}'); 145 } 146 // 当前不需要订阅数据变化时,可以将其取消。 147 try { 148 if(store != undefined) { 149 (store as relationalStore.RdbStore).off('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (storeObserver)=>{ 150 }); 151 } 152 } catch (err) { 153 console.error('Failed to register observer. Code:${err.code},message:${err.message}'); 154 } 155 ``` 156 1576. 跨设备查询。如果数据未完成同步,或未触发数据同步,应用可以使用此接口从指定的设备上查询数据。 158 159 > **说明:** 160 > 161 > deviceIds通过调用[deviceManager.getAvailableDeviceListSync](../reference/apis-distributedservice-kit/js-apis-distributedDeviceManager.md#getavailabledevicelistsync)方法得到。 162 163 164 ```ts 165 // 获取deviceIds 166 import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 167 import { BusinessError } from '@kit.BasicServicesKit'; 168 169 let dmInstance: distributedDeviceManager.DeviceManager; 170 let deviceId: string | undefined = undefined ; 171 172 try { 173 dmInstance = distributedDeviceManager.createDeviceManager("com.example.appdatamgrverify"); 174 let devices = dmInstance.getAvailableDeviceListSync(); 175 176 deviceId = devices[0].networkId; 177 178 // 构造用于查询分布式表的谓词对象 179 let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); 180 // 调用跨设备查询接口,并返回查询结果 181 if(store != undefined && deviceId != undefined) { 182 (store as relationalStore.RdbStore).remoteQuery(deviceId, 'EMPLOYEE', predicates, ['ID', 'NAME', 'AGE', 'SALARY', 'CODES'], 183 (err: BusinessError, resultSet: relationalStore.ResultSet) => { 184 if (err) { 185 console.error(`Failed to remoteQuery data. Code:${err.code},message:${err.message}`); 186 return; 187 } 188 console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`); 189 } 190 ) 191 } 192 } catch (err) { 193 let code = (err as BusinessError).code; 194 let message = (err as BusinessError).message; 195 console.error("createDeviceManager errCode:" + code + ",errMessage:" + message); 196 } 197 ``` 198 199## 相关实例 200 201针对关系型数据库开发,有以下相关实例可供参考: 202 203- [分布式组网认证(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/DistributedAppDev/DistributedAuthentication) 204 205- [分布式关系型数据库(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedRdb) 206 207- [分布式账号(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/DistributedAppDev/DistributedAccount)