OpenHarmony之 eTS DataAbility 的使用及数据管理

Posted 开源基础软件社区官方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenHarmony之 eTS DataAbility 的使用及数据管理相关的知识,希望对你有一定的参考价值。

OpenHarmony之 eTS DataAbility 的使用及数据管理

@toc

1.介绍

本文是eTS DataAbility样例实践,主要展示了eTS FA使用 eTS DataAbility实现图书信息的管理功能,包括 rdb数据库的创建,数据的增删改查,批量操作等。是继 ServiceAbility 的第二篇。
Ability是HarmonyOS应用程序的重要组成部分,分为FA(Feature Ability)和PA(Particle Ability)两种类型。PA有 Service Ability和Data Ability,Service Ability模板用于提供后台运行任务的能力;Data Ability模板用于对外部提供统一的数据访问抽象。

Gitee 样例地址
https://gitee.com/openharmony/app_samples/tree/master/ability/DataAbility
大家也可以自行下载运行,但需要OpenHarmony的设备才能运行。
<br>
先来展示一下效果

2.代码讲解

项目结构图

└─main
    │  config.json
    │
    ├─ets
    │  ├─DataAbility
    │  │      data.ts
    │  │
    │  └─MainAbility
    │      │  app.ets
    │      │
    │      ├─component
    │      │      bookView.ets
    │      │      searchBar.ets
    │      │      titleBar.ets
    │      │
    │      ├─model
    │      │      BookDataModel.ts
    │      │      DaHelperConst.ts
    │      │
    │      └─pages
    │              index.ets
    │              query.ets
    │              update.ets
    │
    └─resources

2.1 PA端 DataAbility

1). data.ts 数据访问能力类

创建一个DataAbility很简单,就是File---New ----Ability---DataAbility,起个名字,
你可以按照数据类型命名,例如:GoodsDataAbility一个用来存储商品的DataAbility,需要注意的是,你命名的其实是个目录或者叫做路径,这个路径指向一个叫data.ts的文件,相应的代码编写也是在这个data.ts的文件。
一起来看一下实现一个DataAbility都需要做哪些事,
引入依赖

//引入数据访问类
import dataAbility from @ohos.data.dataAbility
//引入rdb库
import dataRdb from @ohos.data.rdb

定义 rdb库对象、库配置、表名、创表SQL 等变量

//rdb库对象
let rdbStore: dataRdb.RdbStore = undefined
//库的配置信息:库名称
const STORE_CONFIG =  name: book.db 
//表名
const TABLE_NAME = book
//表结构
const SQL_CREATE_TABLE = CREATE TABLE IF NOT EXISTS book(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, introduction TEXT NOT NULL)

接下来是功能函数的实现,这部分完全可以直接复用。

初始化库和表

//初始化库和表
onInitialized(abilityInfo) 
    console.info(`$TAG DataAbility onInitialized`)
    console.log(`$TAG create table begin`)
   dataRdb.getRdbStore(abilityInfo, STORE_CONFIG, 1)
            .then((rdb) => 
                console.log(`$TAG create table done`)
                rdb.executeSql(SQL_CREATE_TABLE)
                rdbStore = rdb
                console.log(`$TAG create table end`)
            )
            .catch((error) => 
                console.log(`$TAG create table err = $JSON.stringify(err)`)

            )
,

增删改查 数据操作

//增删改查 数据操作
insert(uri, valueBucket, callback) 
    console.info(TAG +  insert start)
    rdbStore.insert(TABLE_NAME, valueBucket, callback)
,
batchInsert(uri, valueBuckets, callback) 
    console.info(TAG +  batch insert start)
    for (let i = 0;i < valueBuckets.length; i++) 
        console.info(TAG +  batch insert i= + i)
        if (i < valueBuckets.length - 1) 
            rdbStore.insert(TABLE_NAME, valueBuckets[i], (err: any, num: number) => 
                console.info(TAG +  batch insert ret= + num)
            )
         else 
            rdbStore.insert(TABLE_NAME, valueBuckets[i], callback)
        
    
,
query(uri, columns, predicates, callback) 
    console.info(TAG +  query start)
    let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
    rdbStore.query(rdbPredicates, columns, callback)
,
update(uri, valueBucket, predicates, callback) 
    console.info(TAG + update start)
    let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
    rdbStore.update(valueBucket, rdbPredicates, callback)
,
delete(uri, predicates, callback) 
    console.info(TAG + delete start)
    let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
    rdbStore.delete(rdbPredicates, callback)

2.2 FA端 MainAbility

1).model下的BookDataModel.ts 模型

创建一个BookModel 数据模型,定义一些属性字段 ,一个编号、一个名称、一个介绍。

export class BookModel 
  id: number
  name: string
  introduction: string

  constructor(id: number, name: string, introduction: string) 
    this.id = id
    this.name = name
    this.introduction = introduction
  

还是在这个ts文件中,再编写一些方便展示的初始化测试数据的函数,export出去就可以直接调用了。这个地方需要注意的是自增ID不要写 。

//初始化测试数据
export function initValuesBuckets() 
  let valuesBuckets = [
     name: Book name1, introduction: Book introduction1 ,
     name: Book name2, introduction: Book introduction2 ,
     name: Book name3, introduction: Book introduction3 ]
  return valuesBuckets

再编写一个将resultSet数据转换位BookModel 数组的函数,用于处理DA_HELPER返回的数据。

//获取一个BookModel 的数据列表
export function getListFromResultSet(resultSet) 
  console.info(TAG +  getListFromResultSet)
  let bookList = []
  console.log(TAG +  resultSet column names: + resultSet.columnNames)
  console.log(TAG +  resultSet row count: + resultSet.rowCount)
  console.log(TAG +  resultSet goToFirstRow: + resultSet.goToFirstRow())
  for (let i = 0;i < resultSet.rowCount; i++) 

    //按照构造函数进行封装
    let book = new BookModel(
        resultSet.getDouble(resultSet.getColumnIndex(id))
      , resultSet.getString(resultSet.getColumnIndex(name))
      , resultSet.getString(resultSet.getColumnIndex(introduction)))

    //添加到列表中
    bookList.push(book)

    console.log(TAG +  resultSet book: + JSON.stringify(book))
    console.log(TAG +  resultSet goToNextRow: + resultSet.goToNextRow())
  
  return bookList

2).model下的DaHelperConst.ts 常量类

定义一个DaHelperConst.ts 常量类,用于存储数据库常用属性。

首先引入FA访问类

//引入FA访问类
import featureAbility from @ohos.ability.featureAbility

定义数据访问URI、表列名、DataAbility帮助类 等常量

//定义 数据访问URI 常量
export const BASE_URI = dataability:///ohos.samples.etsdataability.DataAbility
//定义 表的列名
export const COLUMNS = [id, name, introduction]

//获取一个DataAbility的帮助类
export const DA_HELPER = featureAbility.acquireDataAbilityHelper(BASE_URI)

3).component下的bookView.ets

视图组件,实现了一个图书展示的列表效果。

//引入 BookDataModel 数据模块
import  BookModel  from ../model/BookDataModel

定义 book数据模型对象、删除数据的回调函数

@Component
export struct BookView 

  //定义 book数据模型对象
  private book: BookModel = null
  //定义 删除数据的回调函数
  private deleteCallback = null

构建视图显示

build() 
    Row() 
      //一张图片
      Image($r(app.media.book))
        .height(110).width(80)
        .objectFit(ImageFit.Cover)
        .margin( top: 5, bottom: 5 )

      //垂直方向的两行文本
      Flex( direction: FlexDirection.Column, alignItems: ItemAlign.Start ) 
        Text(this.book.name)
          .fontColor(Color.Black)
          .fontSize(22)
          .fontWeight(FontWeight.Bold)
        Text(this.book.introduction)
          .fontColor(Color.Grey)
          .fontSize(20)
          .margin( top: 10 )
      
      .layoutWeight(1)
      .margin( left: 5 )

      //删除数据的按钮
      if (this.deleteCallback !== null) 
        Button() 
          Image($r(app.media.delete_red))
            .height(35).width(35)
            .objectFit(ImageFit.Contain)
        
        .type(ButtonType.Circle)
        .height(50).width(50)
        .backgroundColor(#F5F5F5)
        .onClick(() => 
          this.deleteCallback(this.book)
        )
      
    
    .width(100%)
    .padding( left: 15, right: 15 )
  

效果图

4).component下的searchBar.ets

视图组件,实现了一个搜索栏的效果,使用了Stack 布局,让放大镜能叠加在文本输入框上。

用Text 模拟TextInput 效果,点击进行跳转到搜索界面。

//引用路由,用于跳转界面
import router from @ohos.router

@Preview
@Component
export struct SearchBar 
  build() 

    Stack( alignContent: Alignment.End ) 

      //模拟TextInput的文本
      Text($r(app.string.search_tip))
        .height(80%).width(94%)
        .fontSize(16)
        .padding( left: 15 )
        .margin(3%)
        .backgroundColor(#A5A5A5)
        .border( radius: 20 )

      //放大镜icon
      Image($r(app.media.search_gray))
        .height(40).width(40)
        .margin( right: 5% )
        .objectFit(ImageFit.Contain)

    
    .height(50).width(100%)
    .backgroundColor(#F5F5F5)
    .onClick(() => 
      router.push( url: pages/Query )
    )

  

效果图

5).component下的titleBar.ets

视图组件,实现了一个顶部标题栏的效果,提供了一个批量插入数据的操作按钮。

组件内定义回调函数的声明,在具体使用组件的界面在对回调函数进行实现,可以学习这种设计组件的方法。

@Component
export struct TitleBar 
  //定义 批量插入的回调函数
  private batchInsertCallback = null

  build() 
    Row() 
      //标题名称
      Text($r(app.string.title))
        .fontColor(Color.White)
        .fontSize(20)
        .layoutWeight(1)

      //批量插入 操作按钮
      Button() 
        Text($r(app.string.batch_insert))
          .fontColor(Color.White)
          .fontSize(22)
          .textAlign(TextAlign.End)
      
      .type(ButtonType.Normal)
      .height(100%)
      .backgroundColor(#0D9FFB)
      .onClick(() => 
        this.batchInsertCallback()
      )
    
    .height(8%).width(100%)
    .backgroundColor(#0D9FFB)
    .padding( left: 15, right: 15 )
  

6).page下的index.ets

index.ets是和用户完成交互的主要页面。

需要引入数据访问能力类、路由,前面定义的常量类、视图组件、数据模块类等。

//引入数据访问能力类
import dataAbility from @ohos.data.dataAbility
//引入路由,实现跳转界面
import router from @ohos.router

//引入定义的常量、视图组件
import  BASE_URI, COLUMNS, DA_HELPER  from ../model/DaHelperConst
import  BookView  from ../component/BookView
import  TitleBar  from ../component/TitleBar
import  SearchBar  from ../component/SearchBar

//引入数据模块类
import  BookModel, initValuesBuckets, getListFromResultSet  from ../model/BookDataModel

通过DA_HELPER查询数据实现页面显示图书列表效果,实现删除和批量插入数据的效果。

@Entry
@Component
struct Index 
  @State bookList: Array<BookModel> = []

  //生命周期回调,自动执行,页面显示前
  //页面显示时触发一次,包括路由过程、应用进入前后台等场景,仅@Entry修饰的自定义组件生效。
  onPageShow() 
    this.queryAll()
  

  queryAll = () => 
    console.info(TAG +  queryAll)
    //获取谓词
    let predicates = new dataAbility.DataAbilityPredicates()
    //使用DA_HELPER 查询数据
    DA_HELPER.query(BASE_URI, COLUMNS, predicates, (err, resultSet) => 
      //重新封装查询结果
      this.bookList = getListFromResultSet(resultSet)
    )

  
  deleteCallback = (book) => 
    let predicates = new dataAbility.DataAbilityPredicates()
    predicates.equalTo(id, book.id)
    //使用DA_HELPER 删除数据
    DA_HELPER.delete(BASE_URI, predicates, (err, num) => 
      console.info(TAG +  delete num= + num)
      this.queryAll()
    )
  

  batchInsertCallback = () => 
    let valuesBuckets = initValuesBuckets()
    //使用DA_HELPER 批量插入数据
    DA_HELPER.batchInsert(BASE_URI, valuesBuckets, (err, num) => 
      console.info(TAG +  batch insert num= + num)
      this.queryAll()
    )
  

build代码块,实现了列表显示的效果,使用了Stack布局,List等组件
点击行,跳转到更新数据界面,传递book对象过去,注意跳转的路由要在config.json配置好。

build() 
  Column() 
    //标题栏
    TitleBar( batchInsertCallback: this.batchInsertCallback )
    //搜索栏
    SearchBar()
    //列表
    Stack( alignContent: Alignment.BottomEnd ) 
      //列表显示
      List() 
        ForEach(this.bookList, item => 
          ListItem() 
            BookView( book: item, deleteCallback: this.deleteCallback )
          
          .onClick(() => 
            //点击行,跳转到更新数据界面,传递book对象过去
            router.push( url: pages/Update, params:  book: item  )
          )
        , item => JSON.stringify(item))
      
      .width(100%)
      .margin( bottom: 100 )
      .constraintSize( minHeight: 100% )
      .divider( strokeWidth: 1, color: Color.Gray, startMargin: 10, endMargin: 10 )

      //添加单条数据按钮
      Button() 
        Image($r(app.media.add))
      
      .type(ButtonType.Circle)
      .width(60)
      .height(60)
      .backgroundColor(#0D9FFB)
      .margin( bottom: 150, right: 50 )
      .onClick(() => 
        console.info(TAG +  insert onClick)
        let valuesBuckets =  name: Book name, introduction: Book introduction 
        DA_HELPER.insert(BASE_URI, valuesBuckets, (err, num) => 
          console.info(TAG +  insert num= + num)
          this.bookList.push( id: num, name: Book name, introduction: Book introduction )
        )
      )

    
    .width(100%).height(100%)
  
  .width(100%).height(100%)

效果图

7).page下的query.ets

引入依赖和index.ets类似

import router from @ohos.router;
import dataAbility from @ohos.data.dataAbility
import  BASE_URI, COLUMNS, DA_HELPER  from ../model/DaHelperConst
import  BookModel, getListFromResultSet  from ../model/BookDataModel
import  BookView  from ../component/BookView

查询数据

//查询数据
query(queryStr) 
  let predicates = new dataAbility.DataAbilityPredicates()

  //根据名字或介绍模糊匹配
  predicates.contains(name, queryStr)
    .or()
    .contains(introduction, queryStr)
  DA_HELPER.query(BASE_URI, COLUMNS, predicates, (err, resultSet) => 
    this.bookList = getListFromResultSet(resultSet)
  )

列表显示,和index.ets 类似,没有了删除按钮。

使用了ForEach、Stack、 TextInput 等组件, TextInput组件的onChange事件触发查询。

build() 
  Column() 
    Row() 
      Image($r(app.media.ic_back))
        .width(40).height(40)
        .objectFit(ImageFit.Contain)
        .onClick(() => 
          router.back()
        )
      Text($r(app.string.title))
        .fontColor(Color.White)
        .fontSize(20)
        .layoutWeight(1)
    
    .height(50).width(100%)
    .backgroundColor(#0D9FFB)
    .padding( left: 10, right: 10 )

    Stack( alignContent: Alignment.End ) 
      TextInput( placeholder: $r(app.string.search_placeholder))
        .height(85%).width(94%)
        .padding( left: 15 )
        .margin(3%)
        .backgroundColor(#F0F0F0)
        .border( radius: 20 )
        //触发查询
        .onChange((value: string) => 
          console.info(TAG +  query str= + value)
          if (value !== ) 
            this.query(value)
          else
            this.bookList = []
          
        )
      Image($r(app.media.search_gray))
        .height(40).width(40)
        .margin( right: 5% )
        .objectFit(ImageFit.Contain)
    
    .width(100%).height(50)
    .backgroundColor(#F5F5F5)

    List() 
      ForEach(this.bookList, item => 
        ListItem() 
          BookView( book: item, deleteCallback: null )
        
        .onClick(() => 
          router.push( url: pages/update, params:  book: item  )
        )
      , item => JSON.stringify(item))
    
    .width(100%)
    .layoutWeight(1)
    .divider( strokeWidth: 1, color: Color.Gray, startMargin: 10, endMargin: 10 )

  

8).page下的update.ets

引入依赖

//引入依赖
import router from @ohos.router;
import prompt from @system.prompt;
import dataAbility from @ohos.data.dataAbility

import  BASE_URI, DA_HELPER  from ../model/DaHelperConst
import  BookModel  from ../model/BookDataModel

声明BookModel的变量,从router中接收携带的参数book对象,用于原信息的显示

@Entry
@Component
struct Update 

  //从router中接收携带的参数book对象,用于原信息的显示
  private book: BookModel = <BookModel> router.getParams()["book"]

使用DA_HELPER 更新数据,更新完返回列表界面。

updateBook = () => 
  let predicates = new dataAbility.DataAbilityPredicates()
  predicates.equalTo(id, this.book.id)
  let valuesBucket = 
    name: this.book.name,
    introduction: this.book.introduction
  
  DA_HELPER.update(BASE_URI, valuesBucket, predicates, (err, num) => 
    console.info(TAG +  update book num= + num)
    router.back()
  )

显示要修改的图书信息,支持修改,支持返回、提交修改按钮。

使用了Scroll、Image、Text、TextInput等组件。

build() 
  Column() 
    Row() 
      //返回按钮
      Image($r(app.media.ic_back))
        .width(40).height(40)
        .objectFit(ImageFit.Contain)
        .onClick(() => 
          router.back()
        )

      //标题
      Text($r(app.string.title))
        .fontColor(Color.White)
        .fontSize(20)
        .layoutWeight(1)
      //提交修改按钮
      Image($r(app.media.submit))
        .width(50).height(50)
        .objectFit(ImageFit.Contain)
        .onClick(() => 
          console.info(TAG +  update onClick,title= + this.book.name)
          if (this.book.name ===  || this.book.introduction === ) 
            prompt.showToast( message: Please input name or introduction )
            return
          
          this.updateBook()
        )
    
    .height(50).width(100%)
    .backgroundColor(#0D9FFB)
    .padding( left: 10, right: 10 )

    //显示要修改的信息,支持滚动条
    Scroll() 
      Column() 
        Image($r(app.media.book))
          .width(120).height(150)
          .objectFit(ImageFit.Fill)
          .margin(5)
        Text($r(app.string.book_name))
          .fontWeight(FontWeight.Bold)
          .fontSize(22)
          .width(100%)
          .margin( top: 20 )
          .fontColor(Color.Black)
        TextInput( placeholder: input name, text: this.book.name )
          .type(InputType.Normal)
          .placeholderColor(Color.Gray)
          .fontSize(19)
          .margin( top: 10 )
          //修改信息后更新book对象
          .onChange((value: string) => 
            this.book.name = value
          )

        Text($r(app.string.book_intro))
          .fontWeight(FontWeight.Bold)
          .fontSize(22)
          .width(100%)
          .fontColor(Color.Black)
          .margin( top: 20 )
        TextInput( placeholder: input introduction, text: this.book.introduction )
          .type(InputType.Normal)
          .placeholderColor(Color.Gray)
          .fontSize(19)
          .margin( right: 10, top: 10 )
          //修改信息后更新book对象
          .onChange((value: string) => 
            this.book.introduction = value
          )
      
      .width(100%)
      .constraintSize( minHeight: 100% )
      .padding(15)
    .height(100%)
  

3.补充说明

1.我们发现并没有调用 DataAbility 的代码,那DataAbility 怎么被跑起来的呢?

其实是 DataAbility 是会被自动运行的,只要在config.json 中做了配置,当应用启动后,DataAbility就会被自动运行,在DataAbility完成初始化后,会调用声明周期回调函数 onInitialized,完成数据库的初始化。

看一下 DataAbility 在config.json 中的配置

"abilities": [

  "visible": true,
  "srcPath": "DataAbility",
  "name": ".DataAbility",
  "icon": "$media:icon",
  "srcLanguage": "ets",
  "description": "$string:description_dataability",
  "type": "data",
  "uri": "dataability://ohos.samples.etsdataability.DataAbility"

启动应用,从日志输出中我们可以看到 DataAbility 被运行。

05-17 23:00:11.016 526-1367/foundation I 01110/AppMgrService: [module_running_record.cpp(LaunchAbility:178)]ScheduleLaunchAbility ability:ohos.samples.etsdataability.DataAbility

那DataAbility 数据的增删改查,也没有看到调用的代码呢?

代码里只是用了DA_HELPER对象来操作数据,并没有调用DataAbility/data.ts 的 insert/batchInsert/query/update/delete 这些函数,

其实是 DA_HELPER 在初始化时 绑定的 BASE_URI(如下) 和 config.json文件中 DataAbility 的uri 是一致的,可以看上面config.json 的配置中uri的值。
(注意 config.json中的 uri 只能是 "dataablity://xxxxxxxxx" ,两道杠 )。

//定义 数据访问URI 常量
export const BASE_URI = dataability:///ohos.samples.etsdataability.DataAbility
//获取一个DataAbility的帮助类
export const DA_HELPER = featureAbility.acquireDataAbilityHelper(BASE_URI)

有关BASE_URI格式说明参见下图,更详尽的参考

2.DataAbility/data.ts 中创表语句中的表名,注意要和TABLE_NAME常量保持一致,否则数据无法插入成功。

<br>
最后,
我们可以在这个基础上去扩展库、扩展表,去实现我们自己应用的数据管理。

附件链接:https://ost.51cto.com/resource/1975

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz

以上是关于OpenHarmony之 eTS DataAbility 的使用及数据管理的主要内容,如果未能解决你的问题,请参考以下文章

#物联网征文# OpenHarmony -ArkUI(ETS)之WiFi简单的连接操作

OpenHarmony ETS UI 组件封装之环形进度条

OpenHarmony - ArkUI(TS)开发之下拉选择菜单

OpenHarmony——浅析ETS开发状态管理

#星光计划2.0# OpenHarmony3.0上采用ets开发HAP控制LED灯

#星光计划2.0# OpenHarmony 源码解析之JavaScriptAPI-NAPI实践