HarmonyOS Next鸿蒙开发:本地数据存储-sqlite

1,254 阅读6分钟

使用sqlite 存储非敏感数据用sqlite进行缓存,提高用户和网络的体验,系统性能。

了解下sqlite

鸿蒙开发主要使用SQLite数据库。SQLite是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,其特点就是小型、快速、自给自足、高度可靠、全功能。 SQLite具有以下优势:

  • 1、轻量级,便于移植;
  • 2、无需服务器;
  • 3、零配置,无需安装和管理;
  • 4、支持数据库事务。 同时,SQLite在鸿蒙系统中也会有一些特殊的应用方式和优化策略。

一、为什么选择SQLite

SQLite是一种嵌入式SQL数据库引擎,不像常见的SQL数据库服务器,如MySQL、Oracle、PostgreSQL等需要单独的服务器进程,SQLite不需要配置和启动服务,一个轻量级的磁盘文件就可以轻松处理。在鸿蒙系统中, SQLite可以为每个应用提供独立的数据库,有效隔离了数据,保证了数据的安全性和隐私性。 SQLite的轻量级和高效性使其在嵌入式设备中的应用非常广泛

二、SQLite在鸿蒙中的应用

在鸿蒙系统中,SQLite主要用于应用的数据存储和管理。开发者可以通过SQLite的API进行数据库的创建、查询、更新和删除等操作。同时,鸿蒙系统还提供了一些特有的SQLite接口,如分布式数据管理接口,以支持分布式设备间的数据同步。 在实际的开发过程中,SQLite的使用非常简单。首先,开发者需要通过SQLite的API创建一个数据库,然后就可以通过SQL语句对数据库进行操作。所有的操作都可以通过一个简单的API完成,无需复杂的配置和管理。

三、鸿蒙对SQLite的优化 鸿蒙系统针对SQLite也进行了一些优化。首先,鸿蒙系统优化了SQLite的磁盘IO性能,通过改进磁盘IO调度算法,提高了SQLite的读写性能。其次,鸿蒙系统还优化了SQLite的内存使用,通过改进内存管理策略,降低了SQLite的内存占用,使其更适合在资源受限的设备上运行。 鸿蒙系统还为SQLite提供了一套完整的测试框架,包括单元测试、集成测试和系统测试,以保证SQLite的稳定性和可靠性。 总的来说,SQLite是鸿蒙系统的理想选择,其轻量级和高效性完全符合鸿蒙的定位。同时,鸿蒙系统还针对SQLite进行了一些优化,使其在鸿蒙系统上的性能更优,为鸿蒙开发者提供了一个便捷、高效的数据管理工具。

四、本案例我们的需求

  • 1、我们做一个用户注册用户-存储在数据库,不使用网络请求
  • 2、登录能正常登录,通过本地数据库查询用户是否存在
  • 3、需要使用到taskpool线程池,在非主线程进行数据处理提高性能和效率
  • 4、不直接在page页面进行引用sqlitedb操作

通过本案例,你可以改造做你相关业务处理,很多场景都需要用到数据库,那么除了本地数据库以外,华为鸿蒙HarmonyOS Next还提供了云数据库。

在EntryAbility的onCreate进行初始化数据库

 //初始化数据库
 MyRdb.getInstance().initRdbStore(this.context)

注册页面demo完整代码

import Prompt from '@system.prompt'
import { UserModel } from '../../../model/UserModel';
import { taskPoolExecuteInsertUser,
  taskPoolExecuteQueryUser,
  taskPoolExecuteQueryUserCount } from '../../../task/RdbTaskPool';


@Entry
@Component
struct InstertDbPage {
  @State message: string = 'Hello World';
  @State mobile: string = '13800138000'
  @State passWord: string = '1'
  @State userName: string = 'alan'
  @State surePassWord: string = '1'
  @State isShowProgress: boolean = false;

  build() {
    Column() {
      // 标题栏
      Row() {
        Row() {
          Text('用户注册')
            .fontSize(20)
            .fontWeight(600)
        }.layoutWeight(1)
        .justifyContent(FlexAlign.Center)
        Blank()
          .width(20)

      }
      .width('100%')
      .height(40)
      .justifyContent(FlexAlign.SpaceBetween)


      // 登录框
      Column() {
        Row() {
          Text('手机号')
            .fontSize(14)
          TextInput({ placeholder: '请输入手机号', text: this.mobile })
            .borderRadius(5)
            .type(InputType.PhoneNumber)
            .backgroundColor('#f5f5f5')
            .layoutWeight(1)
            .onChange(value => {
              this.mobile = value
            })
        }.width('90%')
        .padding(3)
        .borderRadius(6)
        .backgroundColor('#f5f5f5')

        Row() {
          Text('姓 名')
            .fontSize(14)
          TextInput({ placeholder: '请输入姓名', text: this.userName })
            .borderRadius(5)
            .backgroundColor('#f5f5f5')
            .layoutWeight(1)
            .onChange(value => {
              this.userName = value
            })
        }.width('90%')
        .padding(3)
        .borderRadius(6)
        .backgroundColor('#f5f5f5')
        .margin({ top: 10 })

        Row() {
          Text('密 码')
            .fontSize(14)
          TextInput({ placeholder: '请输入密码', text: this.passWord })
            .type(InputType.Password)
            .borderRadius(5)
            .backgroundColor('#f5f5f5')
            .layoutWeight(1)
            .onChange(value => {
              this.passWord = value
            })
        }
        .width('90%')
        .padding(3)
        .borderRadius(6)
        .backgroundColor('#f5f5f5')
        .margin({ top: 10 })

        Row() {
          Text('确认密码')
            .fontSize(14)
          TextInput({ placeholder: '请输入确认密码', text: this.surePassWord })
            .type(InputType.Password)
            .borderRadius(5)
            .backgroundColor('#f5f5f5')
            .layoutWeight(1)
            .onChange(value => {
              this.surePassWord = value
            })
        }
        .width('90%')
        .padding(3)
        .borderRadius(6)
        .backgroundColor('#f5f5f5')
        .margin({ top: 10 })

        Row() {
          Button('注册')
            .commonButton()
            .fontColor(Color.White)
            .backgroundColor('#0ab906')
            .onClick( async () =>
            {
              this.register()
            })

        }
        .width('90%')
        .margin({ top: 30 })
      }.width('100%')
      .margin({ top: 20 })
    }.width('100%')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.SpaceBetween)


  }

  private async register(){
      if (this.mobile.trim() == '') {
        Prompt.showToast({
          message: '请输入手机号'
        })
        return
      }

      if (this.userName.trim() == '') {
        Prompt.showToast({
          message: '请输入用户名'
        })
        return
      }

      if (this.passWord.trim() == '') {
        Prompt.showToast({
          message: '请输入密码'
        })
        return
      }
      if (this.surePassWord.trim() == '') {
        Prompt.showToast({
          message: '请输入确认密码'
        })
        return
      }
      //loadingUtil.showLoading()
    taskPoolExecuteQueryUserCount(getContext(this),this.mobile).then(async (result:number)=>{
        if(result > 0){
          this.showToast('该手机号注册了')
          //loadingUtil.hideLoading()
          return
        }
        await taskPoolExecuteInsertUser(getContext(this),this.userName,this.mobile,this.passWord)
        this.showToast('注册成功')
        await taskPoolExecuteQueryUser(getContext(this),this.mobile).then(async (result:Array<UserModel>) =>{
          console.log(`result:${JSON.stringify(result)}`)
        })
    })
  }

  showToast(content:string){
    Prompt.showToast({
      message: content
    })
  }


}


@Extend(Button) function commonButton() {
  .width('100%')
  .height(35)
  .fontSize(14)
  .type(ButtonType.Normal)
  .borderRadius(3)
}

MyRdb工具类代码


import { relationalStore, ValuesBucket } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
import { UserModel } from '../model/UserModel'

export class MyRdb {
  private static instance: MyRdb | undefined = undefined
  private rdbStore: relationalStore.RdbStore | undefined = undefined
  private TABLE_USER: string = 't_user'

  /**
   *静态方法,用于获取数据库连接实例
   * @returns数据库实例
   * */
  public static getInstance(): MyRdb {
    if (!MyRdb.instance) {
      MyRdb.instance = new MyRdb()
    }
    return MyRdb.instance
  }

  /**
   * 初始化数据
   * @param context
   * @returns
   */
  public async initRdbStore(context: common.Context): Promise<void> {
    console.log('initdb ', 'init redbStore begin')
    if (!context) {
      console.error('inidb', 'init redbstore context is invalid')
    }
    if (this.rdbStore) {
      console.info('initdb ', 'redbStore is exist')
      return
    }

    const config: relationalStore.StoreConfig = {
      name: 'mydb.db', //数据库名
      securityLevel: relationalStore.SecurityLevel.S1 //安全级别 s1最低
    }

    try {
      this.rdbStore = await relationalStore.getRdbStore(context, config)
      console.info('initRdbStore', 'getRdbStore succeed');
      await this.createTable()
    } catch (err) {
      console.error('inidb', `getRedbstore failed,error:${err}`)
    }

  }

  private async createTable(): Promise<void> {
    console.info('createTable', 'create table begin')
    try {
      //初始化sql语句
      const userSql = `CREATE TABLE IF NOT EXISTS ${this.TABLE_USER}(id INTEGER PRIMARY KEY AUTOINCREMENT,nickname TEXT NOT NULL, mobile TEXT NOT NULL,password TEXT NULL)`
      console.info("createTable",userSql)
      //执行sql语句
      if (this.rdbStore != undefined) {
        await this.rdbStore.executeSql(userSql)
        console.info('createTable', 'create table succeed')
      }
    } catch (e) {
      console.error('createTable', `create table failed, err: ${e}`)
    }

  }

  public async queryUserCount(context: common.Context,mobile: string):Promise<number>{
    console.info('queryUser', 'queryUser begin');
    if (!context) {
      console.info('queryUser','context is null or undefined')
      return 0
    }
    if (!this.rdbStore) {
      console.info('queryUser','query rdbstore is null')
      await this.initRdbStore(context)
    }
    //构建查询条件
    let predicates = new relationalStore.RdbPredicates(this.TABLE_USER)
    predicates.equalTo('mobile', mobile)
    //查询(如果是真正的企业项目,数据量大的话不能直接同步查,会很耗时,要用query方法)
    let resultSet:relationalStore.ResultSet = await this.rdbStore!!.query(predicates)
    //获取数据条数
    const rowCount = resultSet.rowCount
    //释放数据集的内存
    resultSet.close()
    return rowCount
  }
  /**
   * 查询
   */
  public async queryUserInfo(context:common.Context,mobile: string):Promise<Array<UserModel>>{
    console.info('queryUser', 'queryUser begin');
    if (!context) {
      console.info('queryUser','context is null or undefined')
      return []
    }
    if (!this.rdbStore) {
      console.info('queryUser','query rdbstore is null')
      await this.initRdbStore(context)
    }
    //构建查询条件
    let predicates = new relationalStore.RdbPredicates(this.TABLE_USER)
    if (predicates === null || predicates === undefined) {
      console.info('queryUser','predicates is null or undefined');
      return []
    }
    predicates.equalTo('mobile', mobile)
    //查询(如果是真正的企业项目,数据量大的话不能直接同步查,会很耗时,要用query方法)
    let resultSet = await this.rdbStore!!.query(predicates)
    //this.rdbStore?.querySync() //同步查询
    let list:Array<UserModel> = []
    while (!resultSet.isAtLastRow) {
      // 指针移动到下一行
      resultSet.goToNextRow()
      // 获取数据(一个字段一个字段获取)
      let id = resultSet.getLong(resultSet.getColumnIndex('id'))
      let nickname = resultSet.getString(resultSet.getColumnIndex('nickname'))
      let mobile = resultSet.getString(resultSet.getColumnIndex('mobile'))
      let password = resultSet.getString(resultSet.getColumnIndex('password'))
      list.push({
        id,nickname,mobile,password
      })
    }
    //释放数据集的内存
    resultSet.close()
    console.info('mydb',`查询数据长度:${list.length}`)
    return list
  }


  /**
   *插入数据库
   */
  public async insertUser(context: common.Context,nickname: string, mobile: string, password: string):Promise<void> {
    console.info('insertUser', 'insertUser begin');
    if (!context) {
      console.info('insertUser', 'context is null or undefined')
      return
    }
    if (!this.rdbStore) {
      console.info('insertUser', 'insert rdbStore is null')
      await this.initRdbStore(context)
    }
    const valueBucket: ValuesBucket = {
        'nickname': nickname,
        'mobile': mobile,
        'password': password
    }
    //this.rdbStore?.insertSync() //建议异步操作数据
    await this.rdbStore!!.insert(this.TABLE_USER,valueBucket, relationalStore.ConflictResolution.ON_CONFLICT_REPLACE)
    }
  }

  export default MyRdb
  

RdbTaskPool 线程池代码,主要考虑在非主线程使用数据操作

import { common } from '@kit.AbilityKit'
import { MyRdb } from '../db/MyRdb'
import { UserModel } from '../model/UserModel'
import taskpool from '@ohos.taskpool'

export async function taskPoolExecuteQueryUser(context: common.Context, mobile: string):Promise<Array<UserModel>>{
    try{
      //queryUserInfo函数调用:需使用装饰器@Concurrent
      let task: taskpool.Task = new taskpool.Task(queryUserInfo, context, mobile)
      let result = await taskpool.execute(task) as UserModel[]
      return result
    } catch (err) {
      console.error('taskPoolExecuteQueryUserInfo', 'error:' + JSON.stringify(err))
      return []
    }
}

export async function taskPoolExecuteInsertUser(context: common.Context, nickname: string, mobile: string, password: string):Promise<void> {

  try{
    //insertUser函数调用:需使用装饰器@Concurrent
    let task: taskpool.Task = new taskpool.Task(insertUser, context, nickname, mobile, password)
    await taskpool.execute(task)
  }catch (err){
    console.error('taskPoolExecuteInsertUser', 'error:'+ JSON.stringify(err))
  }

}
@Concurrent
async function queryUserInfo(context: common.Context, mobile: string): Promise<Array<UserModel>> {
  return await MyRdb.getInstance().queryUserInfo(context, mobile)
}

@Concurrent
async function insertUser(context: common.Context, nickname: string, mobile: string, password: string):Promise<void> {
  return await MyRdb.getInstance().insertUser(context, nickname, mobile, password)
}

/**
 * Concurrent实现任务的函数 统计
 * @param context
 * @param mobile
 * @returns
 */
@Concurrent
async function queryUserCount(context: common.Context, mobile: string): Promise<number> {
  return await MyRdb.getInstance().queryUserCount(context, mobile)
}

export async function taskPoolExecuteQueryUserCount(context: common.Context, mobile: string):Promise<number>{
  try {
    //queryUserCount函数调用:需使用装饰器@Concurrent
    let task: taskpool.Task = new taskpool.Task(queryUserCount,context, mobile)
    let result = await taskpool.execute(task) as number
    return result
  } catch (err) {
    console.error('taskPoolExecuteQueryUserCount', 'error:' + JSON.stringify(err))
    return 0
  }
}

usermodel实体类

export class UserModel{

  id?:number

  nickname?: string

  mobile?: string

  password?: string


}

效果图如下:

image.png

image.png

日志中可以看到查询和插入的日志 1739157320294.jpg