1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import data_rdb from '@ohos.data.relationalStore' 17import common from '@ohos.app.ability.common' 18import Contact from '../model/Contact' 19import Logger from '../model/Logger' 20import { STORE_CONFIG, TABLE_NAME, TYPE_INSERT, TYPE_UPDATE, TYPE_DELETE } from '../model/RdbConst' 21import { ValuesBucket } from '@ohos.data.ValuesBucket'; 22 23const TAG = 'RdbModel' 24 25export default class RdbModel { 26 private rdbStore: data_rdb.RdbStore | undefined = undefined 27 private tableName: string = '' 28 private sqlCreateTable: string = '' 29 private columns: Array<string> = [] 30 private distributedTable: string = '' 31 private dataChangeCallback: Function | null = null 32 private dataChangeDetailCallback: Function | null = null 33 private isCreateDbDone: boolean = false 34 private context: common.UIAbilityContext 35 36 constructor(tableName: string, sqlCreateTable: string, columns: Array<string>, context: common.UIAbilityContext) { 37 this.tableName = tableName 38 this.sqlCreateTable = sqlCreateTable 39 this.columns = columns 40 this.context = context 41 42 this.getRdbStore() 43 } 44 45 // 初始化数据库 46 async getRdbStore() { 47 Logger.info(TAG, 'getRdbStore begin') 48 if (this.isCreateDbDone) { 49 Logger.info(TAG, 'getRdbStore isCreateDbDone') 50 return 51 } 52 try { 53 // 获取数据库存储对象 54 this.rdbStore = await data_rdb.getRdbStore(this.context, STORE_CONFIG); 55 } catch (err) { 56 console.info(`getRdbStore err ${JSON.stringify(err)}`); 57 } 58 Logger.info(TAG, 'getRdbStore end') 59 try { 60 // 执行sql语句,联系人个各个属性设定 61 if (this.rdbStore != undefined) { 62 await this.rdbStore.executeSql(this.sqlCreateTable) 63 console.info(`create tabe start ` + this.sqlCreateTable); 64 // 设置分布式表,表明为contact 65 await this.rdbStore.setDistributedTables([this.tableName]) 66 } 67 } catch (e) { 68 Logger.error(TAG, 'getRdbStore:' + JSON.stringify(e)) 69 } 70 // 分布式数据库创建为完成 71 this.isCreateDbDone = true 72 Logger.info(TAG, 'create table done') 73 } 74 75 async insertData(contact: Contact) { 76 let value1 = contact.name; 77 let value2 = contact.gender; 78 let value3 = contact.phone; 79 let value4 = contact.remark; 80 let value5 = contact.age; 81 82 const valueBucket: ValuesBucket = { 83 'name': value1, 84 'gender': value2, 85 'phone': value3, 86 'remark': value4, 87 'age': value5, 88 } 89 if (this.rdbStore != undefined) { 90 let ret = await this.rdbStore.insert(this.tableName, valueBucket, data_rdb.ConflictResolution.ON_CONFLICT_REPLACE) 91 Logger.info(TAG, `insert done:${ret}`) 92 } 93 } 94 95 async updateData(contact: Contact) { 96 let value1 = contact.name; 97 let value2 = contact.gender; 98 let value3 = contact.phone; 99 let value4 = contact.remark; 100 let value5 = contact.age; 101 102 const valueBucket: ValuesBucket = { 103 'name': value1, 104 'gender': value2, 105 'phone': value3, 106 'remark': value4, 107 'age': value5, 108 } 109 let predicates = new data_rdb.RdbPredicates(this.tableName) 110 Logger.info(TAG, `updateData id=${contact.id}`) 111 predicates.equalTo('id', contact.id) 112 if (this.rdbStore != undefined) { 113 let ret = await this.rdbStore.update(valueBucket, predicates) 114 Logger.info(TAG, `updated row count: ${ret}`) 115 } 116 } 117 118 async deleteContacts(contacts: Array<Contact>) { 119 let predicates = new data_rdb.RdbPredicates(this.tableName) 120 contacts.forEach((contact) => { 121 predicates.or() 122 .equalTo('id', contact.id) 123 }) 124 if (this.rdbStore != undefined) { 125 let rows = await this.rdbStore.delete(predicates) 126 Logger.info(TAG, `delete rows: ${rows}`) 127 } 128 } 129 130 async query(predicates: data_rdb.RdbPredicates): Promise<Array<Contact>> { 131 Logger.info(TAG, 'query start') 132 Logger.info(TAG, 'predicates is ' + JSON.stringify(predicates)) 133 Logger.info(TAG, 'columns ' + JSON.stringify(this.columns)) 134 135 if (this.rdbStore != undefined) { 136 // 默认查询所有列 137 let resultSet: data_rdb.ResultSet = await this.rdbStore.query(predicates, this.columns); 138 Logger.info(TAG, 'result is ' + JSON.stringify(resultSet.rowCount)) 139 // 处理查询到的结果数组 140 return this.getListFromResultSet(resultSet) 141 } 142 return [] 143 } 144 145 async syncData(predicates: data_rdb.RdbPredicates) { 146 Logger.info(TAG, 'syncData') 147 if (this.rdbStore != undefined) { 148 let result = await this.rdbStore.sync(data_rdb.SyncMode.SYNC_MODE_PUSH, predicates) 149 for (let i = 0; i < result.length; i++) { 150 Logger.info(TAG, `device=${result[i][0]}, status = ${result[i][1]}`) 151 } 152 } 153 } 154 155 async onDataChange(device: string, callback: Function) { 156 Logger.info(TAG, `onDataChange enter,device=` + device + ` ,tableName = ` + this.tableName) 157 try { 158 if (this.rdbStore != undefined) { 159 this.distributedTable = await this.rdbStore.obtainDistributedTableName(device, this.tableName) 160 Logger.info(TAG, `obtainDistributedTableName,distributedTable=` + this.distributedTable) 161 } 162 } catch (err) { 163 Logger.error(TAG, `ObtainDistributedTableName failed, code is ${err.code},message is ${err.message}`) 164 } 165 this.dataChangeCallback = callback 166 await this.pullData() 167 if (this.rdbStore != undefined) { 168 this.rdbStore.on('dataChange', data_rdb.SubscribeType.SUBSCRIBE_TYPE_REMOTE, async (devices) => { 169 Logger.info(TAG, `on dataChange, callback`) 170 await this.pullData() 171 }) 172 } 173 } 174 175 async onDataChangeDetail(device: string, callback: Function) { 176 Logger.info(TAG, `onDataChangeDetail enter,device=` + device + ` ,tableName = ` + this.tableName); 177 try { 178 if (this.rdbStore != undefined) { 179 this.distributedTable = await this.rdbStore.obtainDistributedTableName(device, this.tableName); 180 Logger.info(TAG, `obtainDistributedTableName,distributedTable=` + this.distributedTable); 181 } 182 } catch (err) { 183 Logger.error(TAG, `ObtainDistributedTableName failed, code is ${err.code},message is ${err.message}`); 184 } 185 this.dataChangeDetailCallback = callback; 186 if (this.rdbStore != undefined) { 187 this.rdbStore.on('dataChange', data_rdb.SubscribeType.SUBSCRIBE_TYPE_LOCAL_DETAILS, async (changeInfo: Array<data_rdb.ChangeInfo>) => { 188 Logger.info(TAG, `on dataChange SUBSCRIBE_TYPE_REMOTE, callback`); 189 Logger.info(TAG, `on dataChange SUBSCRIBE_TYPE_REMOTE =` + JSON.stringify(changeInfo)); 190 await this.pullDataDetail(changeInfo); 191 }) 192 } 193 } 194 195 async pullData() { 196 Logger.info(TAG, `start pullData`) 197 if (this.rdbStore != undefined) { 198 await this.rdbStore.executeSql('delete from ' + this.tableName) 199 let predicates = new data_rdb.RdbPredicates(this.distributedTable) 200 let resultSet = await this.rdbStore.query(predicates, this.columns) 201 let result = this.getListFromResultSet(resultSet) 202 Logger.info(TAG, `on dataChange,result.length=${result.length}`) 203 for (let i = 0; i < result.length; i++) { 204 Logger.info(TAG, `on dataChange,insert${result[i].name}`) 205 let predicate = new data_rdb.RdbPredicates(this.tableName) 206 predicate.equalTo('name', result[i].name) 207 let exit = await this.rdbStore.query(predicate, this.columns) 208 exit.goToFirstRow() 209 if (exit.rowCount === 0) { 210 await this.insertData(result[i]) 211 } else { 212 result[i].id = exit.getDouble(resultSet.getColumnIndex('id')) 213 await this.updateData(result[i]) 214 } 215 } 216 if (this.dataChangeCallback != null) { 217 this.dataChangeCallback(result) 218 } 219 } 220 } 221 222 async pullDataDetail(changeInfo: Array<data_rdb.ChangeInfo>) { 223 Logger.info(TAG, `start pullDataDetail`); 224 if (this.rdbStore != undefined) { 225 if (changeInfo.length > 0) { 226 let result: Contact[] = []; 227 for (let i = 0; i < changeInfo.length; i++) { 228 let table: string = changeInfo[i].table; 229 if (table !== TABLE_NAME) { 230 return; 231 } 232 let inserted: Array<string> | Array<number> = changeInfo[i].inserted; 233 let updated: Array<string> | Array<number> = changeInfo[i].updated; 234 let deleted: Array<string> | Array<number> = changeInfo[i].deleted; 235 if (inserted.length > 0) { 236 let insertData: Contact[] = await this.getDetailByType(inserted, TYPE_INSERT); 237 insertData.forEach(element => { 238 result.push(element); 239 }); 240 } 241 if (updated.length > 0) { 242 let updateData: Contact[] = await this.getDetailByType(updated, TYPE_UPDATE); 243 updateData.forEach(element => { 244 result.push(element); 245 }); 246 } 247 if (deleted.length > 0) { 248 for (let index = 0; index < deleted.length; index++) { 249 result.push(new Contact(deleted[index] as number, '', 0, '', 0, '', TYPE_DELETE)); 250 } 251 } 252 } 253 Logger.info(TAG, `result:` + JSON.stringify(result)); 254 if (this.dataChangeDetailCallback !== null) { 255 this.dataChangeDetailCallback(result); 256 } 257 } 258 } 259 } 260 261 async getDetailByType(id: Array<string> | Array<number>, type: string): Promise<Array<Contact>> { 262 let predicates = new data_rdb.RdbPredicates(TABLE_NAME); 263 predicates.in('id', id); 264 let aData = await this.query(predicates); 265 Logger.info(TAG, `type:${type}`); 266 Logger.info(TAG, `aData:` + JSON.stringify(aData)); 267 if (aData.length > 0) { 268 for (let i = 0; i < aData.length; i++) { 269 aData[i].type = type; 270 } 271 } 272 return aData; 273 } 274 275 offDataChange() { 276 if (this.rdbStore != undefined) { 277 this.rdbStore.off('dataChange', data_rdb.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (devices) => { 278 for (let i = 0; i < devices.length; i++) { 279 Logger.info(TAG, `device=${devices[i]} off data changed`) 280 } 281 }) 282 } 283 } 284 285 offDataChangeDetail() { 286 if (this.rdbStore != undefined) { 287 this.rdbStore.off('dataChange', data_rdb.SubscribeType.SUBSCRIBE_TYPE_LOCAL_DETAILS, (devices) => { 288 for (let i = 0; i < devices.length; i++) { 289 Logger.info(TAG, `device=${devices[i]} off data changed`) 290 } 291 }) 292 } 293 } 294 295 // 处理数据格式 296 getListFromResultSet(resultSet: data_rdb.ResultSet): Contact[] { 297 // 声明结果变量 298 let contacts: Contact[] = [] 299 // 进入结果集的第一行 300 resultSet.goToFirstRow() 301 // 如果没有结束就继续遍历 302 while (!resultSet.isEnded) { 303 // 读取各个属性,初始化临时变量contact 304 let contact: Contact = new Contact(resultSet.getDouble(resultSet.getColumnIndex('id')) 305 , resultSet.getString(resultSet.getColumnIndex('name')) 306 , resultSet.getDouble(resultSet.getColumnIndex('gender')) 307 , resultSet.getString(resultSet.getColumnIndex('phone')) 308 , resultSet.getLong(resultSet.getColumnIndex('age')) 309 , resultSet.getString(resultSet.getColumnIndex('remark'))) 310 if (!contacts.includes(contact)) { 311 // 如果数据集合中没有这条数据就添加进去 312 contacts.push(contact) 313 } 314 // 进入下一行 315 resultSet.goToNextRow() 316 } 317 // 数据整合完毕就释放资源 318 resultSet.close() 319 Logger.info(TAG, 'contacts number is ' + contacts.length) 320 // 返回整合的联系人数据 321 return contacts 322 } 323}