1# 批量数据写数据库场景 2<!--Kit: ArkTS--> 3<!--Subsystem: CommonLibrary--> 4<!--Owner: @lijiamin2025--> 5<!--Designer: @weng-changcheng--> 6<!--Tester: @kirl75; @zsw_zhushiwei--> 7<!--Adviser: @ge-yafang--> 8 9## 使用TaskPool进行频繁数据库操作 10 11对于需要频繁数据库操作的场景,由于读写数据库存在耗时,因此推荐在子线程中操作,避免阻塞UI线程。 12 13通过ArkTS提供的TaskPool能力,可以将数据库操作任务移到子线程中,实现如下。 14 151. 创建多个子任务,支持数据库的创建、插入、查询和清除等操作。 16 172. UI主线程发起数据库操作请求,在子线程中完成数据库的增删改查等操作。 18 19```ts 20// Index.ets 21import { relationalStore, ValuesBucket } from '@kit.ArkData'; 22import { taskpool } from '@kit.ArkTS'; 23 24@Concurrent 25async function create(context: Context): Promise<boolean> { 26 const CONFIG: relationalStore.StoreConfig = { 27 name: "Store.db", 28 securityLevel: relationalStore.SecurityLevel.S1, 29 }; 30 31 try { 32 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 33 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 34 console.info('Create Store.db successfully!'); 35 36 // 创建表 37 const CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS test (" + 38 "id INTEGER PRIMARY KEY AUTOINCREMENT, " + 39 "name TEXT NOT NULL, " + 40 "age INTEGER, " + 41 "salary REAL, " + 42 "blobType BLOB)"; 43 await store.executeSql(CREATE_TABLE_SQL); 44 console.info('Create table test successfully!'); 45 return true; 46 } catch (err) { 47 console.error(`Create db failed, code: ${err.code}, message: ${err.message}`); 48 return false; 49 } 50} 51 52@Concurrent 53async function insert(context: Context, valueBucketArray: Array<relationalStore.ValuesBucket>) { 54 const CONFIG: relationalStore.StoreConfig = { 55 name: "Store.db", 56 securityLevel: relationalStore.SecurityLevel.S1, 57 }; 58 59 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 60 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 61 console.info('Create Store.db successfully!'); 62 63 // 数据插入 64 await store.batchInsert("test", valueBucketArray as Object as Array<relationalStore.ValuesBucket>); 65} 66 67@Concurrent 68async function query(context: Context): Promise<Array<relationalStore.ValuesBucket>> { 69 const CONFIG: relationalStore.StoreConfig = { 70 name: "Store.db", 71 securityLevel: relationalStore.SecurityLevel.S1, 72 }; 73 74 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 75 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 76 console.info('Create Store.db successfully!'); 77 78 // 获取用于查询的谓词 79 let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates("test"); 80 // 查询所有数据 81 let resultSet = await store.query(predicates); 82 console.info(`Query data successfully! row count:${resultSet.rowCount}`); 83 let index = 0; 84 let result = new Array<relationalStore.ValuesBucket>(resultSet.rowCount); 85 resultSet.goToFirstRow(); 86 do { 87 result[index++] = resultSet.getRow(); 88 } while (resultSet.goToNextRow()); 89 resultSet.close(); 90 return result; 91} 92 93@Concurrent 94async function deleteStore(context: Context) { 95 const CONFIG: relationalStore.StoreConfig = { 96 name: "Store.db", 97 securityLevel: relationalStore.SecurityLevel.S1, 98 }; 99 100 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 101 await relationalStore.deleteRdbStore(context, CONFIG); 102 console.info('Delete Store.db successfully!'); 103} 104 105@Entry 106@Component 107struct Index { 108 @State message: string = 'Hello World'; 109 110 build() { 111 RelativeContainer() { 112 Text(this.message) 113 .id('HelloWorld') 114 .fontSize(50) 115 .fontWeight(FontWeight.Bold) 116 .alignRules({ 117 center: { anchor: '__container__', align: VerticalAlign.Center }, 118 middle: { anchor: '__container__', align: HorizontalAlign.Center } 119 }) 120 .onClick(async () => { 121 let context : Context = this.getUIContext().getHostContext() as Context; 122 123 // 数据准备 124 const count = 5; 125 let valueBucketArray = new Array<relationalStore.ValuesBucket>(count); 126 for (let i = 0; i < count; i++) { 127 let value: relationalStore.ValuesBucket = { 128 id: i, 129 name: "zhangsan" + i, 130 age: 20, 131 salary: 5000 + 50 * i 132 }; 133 valueBucketArray[i] = value; 134 } 135 let ret = await taskpool.execute(create, context); 136 if (!ret) { 137 console.error("Create db failed."); 138 return; 139 } 140 await taskpool.execute(insert, context, valueBucketArray); 141 let index = 0; 142 let resultSet = await taskpool.execute(query, context) as Array<relationalStore.ValuesBucket>; 143 for (let value of resultSet) { 144 console.info(`Row[${index}].id = ${value.id}`); 145 console.info(`Row[${index}].name = ${value.name}`); 146 console.info(`Row[${index}].age = ${value.age}`); 147 console.info(`Row[${index}].salary = ${value.salary}`); 148 index++; 149 } 150 await taskpool.execute(deleteStore, context); 151 }) 152 } 153 .height('100%') 154 .width('100%') 155 } 156} 157``` 158<!-- @[taskpool_frequently_operate_database](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/ApplicationMultithreadingDevelopment/PracticalCases/entry/src/main/ets/managers/UsingSendable.ets) --> 159 160## 使用Sendable进行大容量数据库操作 161 162由于数据库数据跨线程传递存在耗时,数据量较大时会占用UI主线程。推荐使用Sendable封装数据库数据,以降低跨线程开销。 163 1641. 定义数据库中的数据格式,可以使用Sendable,以减少跨线程操作的耗时。 165 166 ```ts 167 // SharedValuesBucket.ets 168 export interface IValueBucket { 169 id: number; 170 name: string; 171 age: number; 172 salary: number; 173 } 174 175 @Sendable 176 export class SharedValuesBucket implements IValueBucket { 177 id: number = 0 178 name: string = "" 179 age: number = 0 180 salary: number = 0 181 182 constructor(value: IValueBucket) { 183 this.id = value.id; 184 this.name = value.name; 185 this.age = value.age; 186 this.salary = value.salary 187 } 188 } 189 ``` 190 <!-- @[define_data_format](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/ApplicationMultithreadingDevelopment/PracticalCases/entry/src/main/ets/managers/SharedValuesBucket.ets) --> 191 1922. UI主线程发起数据库操作请求,在子线程完成数据的增删改查等操作。 193 194 ```ts 195 // Index.ets 196 import { relationalStore, ValuesBucket } from '@kit.ArkData'; 197 import { collections, taskpool } from '@kit.ArkTS'; 198 import { IValueBucket, SharedValuesBucket } from './SharedValuesBucket'; 199 200 @Concurrent 201 async function create(context: Context): Promise<boolean> { 202 const CONFIG: relationalStore.StoreConfig = { 203 name: "Store.db", 204 securityLevel: relationalStore.SecurityLevel.S1, 205 }; 206 207 try { 208 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 209 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 210 console.info('Create Store.db successfully!'); 211 212 // 创建表 213 const CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS test (" + 214 "id INTEGER PRIMARY KEY AUTOINCREMENT, " + 215 "name TEXT NOT NULL, " + 216 "age INTEGER, " + 217 "salary REAL, " + 218 "blobType BLOB)"; 219 await store.executeSql(CREATE_TABLE_SQL); 220 console.info('Create table test successfully!'); 221 return true; 222 } catch (err) { 223 console.error(`Create db failed, code: ${err.code}, message: ${err.message}`); 224 return false; 225 } 226 } 227 228 @Concurrent 229 async function insert(context: Context, valueBucketArray: collections.Array<SharedValuesBucket | undefined>) { 230 const CONFIG: relationalStore.StoreConfig = { 231 name: "Store.db", 232 securityLevel: relationalStore.SecurityLevel.S1, 233 }; 234 235 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 236 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 237 console.info('Create Store.db successfully!'); 238 239 // 数据插入 240 await store.batchInsert("test", valueBucketArray as Object as Array<ValuesBucket>); 241 } 242 243 @Concurrent 244 async function query(context: Context): Promise<collections.Array<SharedValuesBucket | undefined>> { 245 const CONFIG: relationalStore.StoreConfig = { 246 name: "Store.db", 247 securityLevel: relationalStore.SecurityLevel.S1, 248 }; 249 250 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 251 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 252 console.info('Create Store.db successfully!'); 253 254 // 获取用于查询的谓词 255 let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates("test"); 256 // 查询所有数据 257 let resultSet = await store.query(predicates); 258 console.info(`Query data successfully! row count:${resultSet.rowCount}`); 259 let index = 0; 260 let result = collections.Array.create<SharedValuesBucket | undefined>(resultSet.rowCount, undefined); 261 resultSet.goToFirstRow(); 262 do { 263 let value: IValueBucket = { 264 id: resultSet.getLong(resultSet.getColumnIndex("id")), 265 name: resultSet.getString(resultSet.getColumnIndex("name")), 266 age: resultSet.getLong(resultSet.getColumnIndex("age")), 267 salary: resultSet.getLong(resultSet.getColumnIndex("salary")) 268 }; 269 result[index++] = new SharedValuesBucket(value); 270 } while (resultSet.goToNextRow()); 271 resultSet.close(); 272 return result; 273 } 274 275 @Concurrent 276 async function deleteStore(context: Context) { 277 const CONFIG: relationalStore.StoreConfig = { 278 name: "Store.db", 279 securityLevel: relationalStore.SecurityLevel.S1, 280 }; 281 282 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 283 await relationalStore.deleteRdbStore(context, CONFIG); 284 console.info('Delete Store.db successfully!'); 285 } 286 287 @Entry 288 @Component 289 struct Index { 290 @State message: string = 'Hello World'; 291 292 build() { 293 RelativeContainer() { 294 Text(this.message) 295 .id('HelloWorld') 296 .fontSize(50) 297 .fontWeight(FontWeight.Bold) 298 .alignRules({ 299 center: { anchor: '__container__', align: VerticalAlign.Center }, 300 middle: { anchor: '__container__', align: HorizontalAlign.Center } 301 }) 302 .onClick(async () => { 303 let context : Context = this.getUIContext().getHostContext() as Context; 304 305 // 数据准备 306 const count = 5; 307 let valueBucketArray = collections.Array.create<SharedValuesBucket | undefined>(count, undefined); 308 for (let i = 0; i < count; i++) { 309 let value: IValueBucket = { 310 id: i, 311 name: "zhangsan" + i, 312 age: 20, 313 salary: 5000 + 50 * i 314 }; 315 valueBucketArray[i] = new SharedValuesBucket(value); 316 } 317 let ret = await taskpool.execute(create, context); 318 if (!ret) { 319 console.error("Create db failed."); 320 return; 321 } 322 await taskpool.execute(insert, context, valueBucketArray); 323 let index = 0; 324 let resultSet: collections.Array<SharedValuesBucket> = 325 await taskpool.execute(query, context) as collections.Array<SharedValuesBucket>; 326 for (let value of resultSet.values()) { 327 console.info(`Row[${index}].id = ${value.id}`); 328 console.info(`Row[${index}].name = ${value.name}`); 329 console.info(`Row[${index}].age = ${value.age}`); 330 console.info(`Row[${index}].salary = ${value.salary}`); 331 index++; 332 } 333 await taskpool.execute(deleteStore, context); 334 }) 335 } 336 .height('100%') 337 .width('100%') 338 } 339 } 340 ``` 341 <!-- @[operate_child_thread_data](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/ApplicationMultithreadingDevelopment/PracticalCases/entry/src/main/ets/managers/UsingTaskPool.ets) --> 342 343## 复杂类实例对象使用Sendable进行大容量数据库操作 344 345普通类实例对象的属性可持有Sendable类实例对象。 346 347对于复杂的普通类实例对象,可以先将相应数据库数据字段封装为Sendable类实例对象,再由普通类实例对象持有,从而降低跨线程开销。 348 3491. 定义数据库中的数据格式,采用Sendable,减少跨线程耗时。 350 351 ```ts 352 // SharedValuesBucket.ets 353 export interface IValueBucket { 354 id: number; 355 name: string; 356 age: number; 357 salary: number; 358 } 359 360 @Sendable 361 export class SharedValuesBucket implements IValueBucket { 362 id: number = 0; 363 name: string = ""; 364 age: number = 0; 365 salary: number = 0; 366 367 constructor(value: IValueBucket) { 368 this.id = value.id; 369 this.name = value.name; 370 this.age = value.age; 371 this.salary = value.salary; 372 } 373 } 374 ``` 375 3762. 定义普通类实例对象,持有Sendable类实例对象。 377 378 ```ts 379 // Material.ets 380 import { SharedValuesBucket } from './SharedValuesBucket'; 381 import { collections } from '@kit.ArkTS'; 382 383 export class Material { 384 seq: number = 0; 385 materialName: string = ""; 386 // ... 省略其他属性 387 buckets: collections.Array<SharedValuesBucket | undefined>; 388 389 constructor(seq: number, materialName: string, buckets: collections.Array<SharedValuesBucket | undefined>) { 390 this.seq = seq; 391 this.materialName = materialName; 392 this.buckets = buckets; 393 } 394 395 getBuckets() : collections.Array<SharedValuesBucket | undefined>{ 396 return this.buckets; 397 } 398 399 setBuckets(buckets: collections.Array<SharedValuesBucket | undefined>) { 400 this.buckets = buckets; 401 } 402 } 403 ``` 404 4053. UI主线程发起数据库操作请求,在子线程进行数据的增删改查等操作。 406 407 ```ts 408 // Index.ets 409 import { relationalStore, ValuesBucket } from '@kit.ArkData'; 410 import { collections, taskpool } from '@kit.ArkTS'; 411 import { IValueBucket, SharedValuesBucket } from './SharedValuesBucket'; 412 import { Material } from './Material'; 413 414 @Concurrent 415 async function create(context: Context): Promise<boolean> { 416 const CONFIG: relationalStore.StoreConfig = { 417 name: "Store.db", 418 securityLevel: relationalStore.SecurityLevel.S1, 419 }; 420 421 try { 422 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 423 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 424 console.info('Create Store.db successfully!'); 425 426 // 创建表 427 const CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS test (" + 428 "id INTEGER PRIMARY KEY AUTOINCREMENT, " + 429 "name TEXT NOT NULL, " + 430 "age INTEGER, " + 431 "salary REAL, " + 432 "blobType BLOB)"; 433 await store.executeSql(CREATE_TABLE_SQL); 434 console.info('Create table test successfully!'); 435 return true; 436 } catch (err) { 437 console.error(`Create db failed, code: ${err.code}, message: ${err.message}`); 438 return false; 439 } 440 } 441 442 @Concurrent 443 async function insert(context: Context, valueBucketArray: collections.Array<SharedValuesBucket | undefined>) { 444 const CONFIG: relationalStore.StoreConfig = { 445 name: "Store.db", 446 securityLevel: relationalStore.SecurityLevel.S1, 447 }; 448 449 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 450 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 451 console.info('Create Store.db successfully!'); 452 453 // 数据插入 454 await store.batchInsert("test", valueBucketArray as Object as Array<ValuesBucket>); 455 } 456 457 @Concurrent 458 async function query(context: Context): Promise<collections.Array<SharedValuesBucket | undefined>> { 459 const CONFIG: relationalStore.StoreConfig = { 460 name: "Store.db", 461 securityLevel: relationalStore.SecurityLevel.S1, 462 }; 463 464 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 465 let store: relationalStore.RdbStore = await relationalStore.getRdbStore(context, CONFIG); 466 console.info('Create Store.db successfully!'); 467 468 // 获取用于查询的谓词 469 let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates("test"); 470 // 查询所有数据 471 let resultSet = await store.query(predicates); 472 console.info(`Query data successfully! row count:${resultSet.rowCount}`); 473 let index = 0; 474 let result = collections.Array.create<SharedValuesBucket | undefined>(resultSet.rowCount, undefined); 475 resultSet.goToFirstRow(); 476 do { 477 let value: IValueBucket = { 478 id: resultSet.getLong(resultSet.getColumnIndex("id")), 479 name: resultSet.getString(resultSet.getColumnIndex("name")), 480 age: resultSet.getLong(resultSet.getColumnIndex("age")), 481 salary: resultSet.getLong(resultSet.getColumnIndex("salary")) 482 }; 483 result[index++] = new SharedValuesBucket(value); 484 } while (resultSet.goToNextRow()); 485 resultSet.close(); 486 return result; 487 } 488 489 @Concurrent 490 async function deleteStore(context: Context) { 491 const CONFIG: relationalStore.StoreConfig = { 492 name: "Store.db", 493 securityLevel: relationalStore.SecurityLevel.S1, 494 }; 495 496 // 默认数据库文件路径为 context.databaseDir + "/rdb/" + StoreConfig.name 497 await relationalStore.deleteRdbStore(context, CONFIG); 498 console.info('Delete Store.db successfully!'); 499 } 500 501 function initMaterial() : Material { 502 // 数据准备 503 const count = 5; 504 let valueBucketArray = collections.Array.create<SharedValuesBucket | undefined>(count, undefined); 505 for (let i = 0; i < count; i++) { 506 let value: IValueBucket = { 507 id: i, 508 name: "zhangsan" + i, 509 age: 20, 510 salary: 5000 + 50 * i 511 }; 512 valueBucketArray[i] = new SharedValuesBucket(value); 513 } 514 let material = new Material(1, "test", valueBucketArray); 515 return material; 516 } 517 518 @Entry 519 @Component 520 struct Index { 521 @State message: string = 'Hello World'; 522 523 build() { 524 RelativeContainer() { 525 Text(this.message) 526 .id('HelloWorld') 527 .fontSize(50) 528 .fontWeight(FontWeight.Bold) 529 .alignRules({ 530 center: { anchor: '__container__', align: VerticalAlign.Center }, 531 middle: { anchor: '__container__', align: HorizontalAlign.Center } 532 }) 533 .onClick(async () => { 534 let context : Context = this.getUIContext().getHostContext() as Context; 535 let material = initMaterial(); 536 let ret = await taskpool.execute(create, context); 537 if (!ret) { 538 console.error("Create db failed."); 539 return; 540 } 541 await taskpool.execute(insert, context, material.getBuckets()); 542 let index = 0; 543 let resultSet: collections.Array<SharedValuesBucket> = 544 await taskpool.execute(query, context) as collections.Array<SharedValuesBucket>; 545 material.setBuckets(resultSet); 546 for (let value of resultSet.values()) { 547 console.info(`Row[${index}].id = ${value.id}`); 548 console.info(`Row[${index}].name = ${value.name}`); 549 console.info(`Row[${index}].age = ${value.age}`); 550 console.info(`Row[${index}].salary = ${value.salary}`); 551 index++; 552 } 553 await taskpool.execute(deleteStore, context); 554 }) 555 } 556 .height('100%') 557 .width('100%') 558 } 559 } 560 ```