Harmony应用开发——项目实战——图书管理系统

前言:

在这里,我们将着手开发一个图书管理系统,旨在帮助读者熟练运用关系型数据库(RelationalStore)。图书管理系统是一个常见的应用场景,涉及查看图书、修改图书、删除图书和添加图书等功能。通过这个项目,我们将学习如何利用常见的 UI 组件,并重点掌握关系型数据库(RelationalStore)的使用。

案例介绍:

图书管理系统是一个常见的应用,通过这个案例,我们将学习如何设计和开发一个完整的应用程序。系统的主要功能包括:

  1. 查看图书: 用户可以浏览系统中存储的图书信息,包括书名、作者、出版年份等内容。
  2. 修改图书: 用户可以对图书信息进行修改,例如修改书名、作者、出版年份等。
  3. 删除图书: 用户可以从系统中删除特定的图书记录。
  4. 添加图书: 用户可以向系统中添加新的图书信息。

通过实现这些功能,我们将学习如何设计用户界面、处理用户输入、管理数据以及与关系型数据库(RelationalStore)交互。这将为我们提供一个全面的实践经验,加深对于关系型数据库的理解和运用。

项目目录介绍:

common:存放一些公共能力,包括数据库操作工具类、常量等。

    • constants: 存放公共常量。
      • CommonConstants.ets: 存放通用的常量。
    • database: 数据库相关的代码文件。
      • tables: 存放数据库中表的操作代码。
        • BooksTable.ets: 对表结构 "books" 进行操作的工具类。
    • Rdb.ets: 关系型数据库代码封装接口。

model:存放模型层的代码文件。

    • Book.ets: Book 类的接口,定义了 Book 对象的属性。

pages:存放页面的 UI 布局文件。

    • AddBookPage.ets: 添加图书的页面。
    • BooksListPage.ets: 展示图书的页面。
    • DeleteBookPage.ets: 删除图书的页面。
    • Index.ets: 图书管理系统的首页,包含四个按钮。
    • ModifyBookPage.ets: 修改图书的页面。

entryability:存放代码入口。

    • EntryAbility.ets: 程序的入口类。

resources:存放资源文件的目录。

效果展示:

前置准备:

数据库设计:

对于该项目,我们使用了数据库进行图书的管理。在着手编写项目代码文件时,我们先设计一下该项目的表结构:

列名

类型

属性

说明

id

INTEGER

PRIMARY KEY

主键,自增

title

TEXT

书籍标题

author

TEXT

书籍作者

publicationYear

TEXT

书籍出版年份

type

TEXT

书籍类型或类别

以及对应的SQL创建表语句:

CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, publicationYear TEXT, type TEXT)

CommonConstants.ets文件:

该代码文件用来存储通用常量:


import relationalStore from '@ohos.data.relationalStore';
import Book from '../../model/Book';

export default class CommonConstants {

  static readonly STORE_CONFIG: relationalStore.StoreConfig = {
    name: 'database.db',
    securityLevel: relationalStore.SecurityLevel.S1
  };

  static readonly BOOKS_TABLE = {
    tableName: 'books',
    sqlCreate: 'CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, publicationYear TEXT, type TEXT)',
  };

}

relationalStore.StoreConfig接口说明:

功能:管理关系数据库配置。

名称

类型

必填

说明

name

string

数据库文件名,也是数据库唯一标识符。

securityLevel

SecurityLevel

设置数据库安全级别

encrypt

boolean

指定数据库是否加密,默认不加密。

true:加密。

false:非加密。

Book.ets文件:
 

在ets目录中新建model目录:

新建Book.ets:

// 定义一个书籍接口
export default interface Book {
  id: number; // 书籍的唯一标识符
  title: string; // 书籍的标题
  author: string; // 书籍的作者
  publicationYear: string; // 书籍的出版年份
  type: string; // 书籍的类型
}

页面布局

Index.ets

图书管理系统的首页,包含四个按钮。

// 引入路由模块,用于页面间的导航
import router from '@ohos.router'
// 引入BooksTable类,用于数据库操作
// import BooksTable from '../common/database/tables/BooksTable'

// 使用@Entry装饰器标记该组件为程序入口
@Entry
  // 使用@Component装饰器定义一个组件
@Component
  // 定义一个名为Index的结构体,用作组件的主体
struct Index {
  // aboutToAppear生命周期钩子,在组件即将出现时触发
  aboutToAppear() {
    // 创建BooksTable实例,并在创建成功或失败时输出信息
    // BooksTable.booksTable = new BooksTable((flag) => {
    //   // 如果flag为false,表示创建失败,输出失败信息
    //   if (!flag) {
    //     console.info('创建失败')
    //     return
    //   }
    //   // 如果创建成功,输出成功信息
    //   console.info('创建成功')
    // })
  }

  // build方法定义组件的UI布局
  build() {
    // 使用Column布局,其中space属性设置子元素间的间距
    Column({space: 10}) {
      // 创建一个Text组件,显示"图书管理系统"文字
      Text("图书管理系统")
        // 设置字体大小为50
        .fontSize("50")
          // 设置底部外边距为30
        .margin({
          bottom:30
        })

      // 创建一个Button组件,用于查看书籍
      Button('查看书籍')
        // 设置字体大小为20
        .fontSize("20")
          // 设置宽度为父容器宽度的70%
        .width("70%")
          // 设置高度为7%
        .height("7%")
          // 设置点击事件处理函数,点击时导航到BooksListPage页面
        .onClick(() => {
          router.pushUrl({
            url: 'pages/BooksListPage',
            params: {
              state: 1
            }
          })
        })

      // 创建一个Button组件,用于添加书籍
      Button('添加书籍')
        // 设置字体大小为20
        .fontSize("20")
          // 设置宽度为父容器宽度的70%
        .width("70%")
          // 设置高度为7%
        .height("7%")
          // 设置点击事件处理函数,点击时导航到AddBookPage页面
        .onClick(() => {
          router.pushUrl({
            url: 'pages/AddBookPage'
          })
        })

      // 创建一个Button组件,用于删除书籍
      Button('删除书籍')
        // 设置字体大小为20
        .fontSize("20")
          // 设置宽度为父容器宽度的70%
        .width("70%")
          // 设置高度为7%
        .height("7%")
          // 设置点击事件处理函数,点击时导航到BooksListPage页面,并传递参数state为3
        .onClick(() => {
          router.pushUrl({
            url: 'pages/BooksListPage',
            params: {
              state: 3
            }
          })
        })

      // 创建一个Button组件,用于修改书籍
      Button('修改书籍')
        // 设置字体大小为20
        .fontSize("20")
          // 设置宽度为父容器宽度的70%
        .width("70%")
          // 设置高度为7%
        .height("7%")
          // 设置点击事件处理函数,点击时导航到BooksListPage页面,并传递参数state为2
        .onClick(() => {
          router.pushUrl({
            url: 'pages/BooksListPage',
            params: {
              state: 2
            }
          })
        })
    }
    // 设置Column布局的对齐方式为水平居中
    .alignItems(HorizontalAlign.Center)
    // 设置Column布局的对齐方式为垂直居中
    .justifyContent(FlexAlign.Center)
    // 设置Column布局的宽度为100%
    .width('100%')
    // 设置Column布局的高度为100%
    .height('100%')
  }
}

代码总结:

  1. 导入必要的模块和类,如路由和数据库表操作。
  2. 定义一个名为Index的组件,它是这个应用的入口点。
  3. 在组件的aboutToAppear生命周期中,尝试创建BooksTable实例,并根据创建结果输出相应的日志信息。
  4. 在组件的build方法中,定义了用户界面的布局,包括一个标题文本和四个按钮,分别用于查看、添加、删除和修改书籍。
  5. 每个按钮都绑定了一个点击事件处理函数,用于导航到不同的页面,并传递必要的参数。

AddBookPage.ets

添加图书的页面

// 引入路由模块,用于页面间的导航
import router from '@ohos.router'
// 引入BooksTable类,用于数据库操作
// import BooksTable from '../common/database/tables/BooksTable'
// 引入Book模型,代表书籍数据结构
import Book from '../model/Book'

// 使用@Entry装饰器标记该组件为程序入口
@Entry
  // 使用@Component装饰器定义一个组件
@Component
  // 定义一个名为AddBookPage的结构体,用作添加书籍页面的组件
struct AddBookPage {
  // 使用@State装饰器声明响应式状态变量,用于存储用户输入的书籍信息
  @State title: string = ''
  @State author: string = ''
  @State publicationYear: string = ''
  @State type: string = ''

  // build方法定义组件的UI布局
  build() {
    // 使用Column布局组件来垂直排列子组件
    Column() {
      // 创建一个Text组件显示"添加图书信息"标题
      Text('添加图书信息')
        // 设置标题的字体大小为40
        .fontSize(40)
          // 设置标题下方的外边距为20
        .margin({
          bottom: 20
        })
      // 创建一个Column布局组件来垂直排列表单元素
      Column() {
        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"标题: "标签
          Text('标题: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入标题
          TextInput()
            // 设置输入变化时的回调函数,更新title状态变量
            .onChange((value) => {
              this.title = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及上下的外边距
        .height("7%")
        .margin({ top: 10, bottom: 10 })
        .width("100%")

        // 以下Row组件重复上述步骤,分别用于输入作者、出版年份和书籍类型

        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"作者: "标签
          Text('作者: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入作者
          TextInput()
            // 设置输入变化时的回调函数,更新author状态变量
            .onChange((value) => {
              this.author = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及下方的外边距
        .height("7%")
        .margin({ bottom: 10 })
        .width("100%")

        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"出版年份: "标签
          Text('出版年份: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入出版年份
          TextInput()
            // 设置输入变化时的回调函数,更新publicationYear状态变量
            .onChange((value) => {
              this.publicationYear = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及下方的外边距
        .height("7%")
        .margin({ bottom: 10 })
        .width("100%")

        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"书籍类型: "标签
          Text('书籍类型: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入书籍类型
          TextInput()
            // 设置输入变化时的回调函数,更新type状态变量
            .onChange((value) => {
              this.type = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及下方的外边距
        .height("7%")
        .margin({ bottom: 10 })
        .width("100%")
      }
      // 设置内部Column布局的对齐方式、背景颜色、边框圆角、宽度和下方外边距
      .alignItems(HorizontalAlign.End)
      .backgroundColor("#FFFFFF")
      .borderRadius("24")
      .width('90%')
      .margin({
        bottom: 20
      })

      // 创建一个Row布局组件来水平排列按钮
      Row() {
        // 创建一个Button组件用于取消操作
        Button('取消')
          // 设置点击事件处理函数,点击时返回上一页
          .onClick(() => {
            router.back()
          })
            // 设置按钮宽度为父容器宽度的35%,以及右侧外边距
          .width("35%")
          .margin({ right: 20 })
        // 创建一个Button组件用于添加书籍
        Button('添加')
          // 设置点击事件处理函数,点击时创建Book对象并插入数据库
          .onClick(() => {
            // 创建一个Book对象,包含用户输入的书籍信息
            const book: Book = {
              id: null,
              title: this.title,
              author: this.author,
              publicationYear: this.publicationYear,
              type: this.type
            }
            // 调用BooksTable的insertData方法插入书籍数据,并处理回调结果
            // BooksTable.booksTable.insertData(book, (flag) => {
            //   // 如果flag为true,表示添加成功,输出成功信息
            //   if (flag) {
            //     console.info('添加成功')
            //   } else {
            //     // 如果flag为false,表示添加失败,输出失败信息
            //     console.info('添加失败')
            //   }
            // })
            // 添加完成后返回上一页
            router.back()
          })
            // 设置按钮宽度为父容器宽度的35%
          .width("35%")
      }
    }
    // 设置外部Column布局的对齐方式、高度、宽度和背景颜色
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
    .backgroundColor("#F1F3F5")
  }
}

代码总结:

  1. 包含了表单输入和按钮操作,允许用户输入书籍的标题、作者、出版年份和类型,并将这些信息添加到数据库中。
  2. 通过声明响应式状态变量,该页面能够实时响应用户的输入。当用户完成输入并点击添加按钮时,会创建一个新的Book对象,并通过BooksTable类的insertData方法将其保存到数据库。
  3. 如果操作成功,会输出"添加成功"的日志信息;如果失败,则输出"添加失败"。此外,还有一个取消按钮允许用户返回上一页。

DeleteBookPage.ets

删除图书的页面

// 引入路由模块,用于页面间的导航
import router from '@ohos.router'
// 引入BooksTable类,用于数据库操作
// import BooksTable from '../common/database/tables/BooksTable'

// 使用@Entry装饰器标记该组件为程序入口
@Entry
  // 使用@Component装饰器定义一个组件
@Component
  // 定义一个名为DeleteBookPage的结构体,用作删除书籍页面的组件
struct DeleteBookPage {
  // 私有变量bookId,用于存储要删除的书籍ID
  private bookId: number
  // 使用@State装饰器声明响应式状态变量,用于存储书籍信息
  @State title: string = ''
  @State author: string = ''
  @State publicationYear: string = ''
  @State type: string = ''

  // onPageShow生命周期钩子,在页面显示时触发
  onPageShow() {
    // 从路由参数中获取书籍ID和其他信息,并赋值给相应的状态变量
    this.bookId = router.getParams()['bookId']
    this.title = router.getParams()['title']
    this.author = router.getParams()['author']
    this.publicationYear = router.getParams()['publicationYear']
    this.type = router.getParams()['type']
  }

  // build方法定义组件的UI布局
  build() {
    // 使用Column布局组件来垂直排列子组件
    Column() {
      // 创建一个Text组件显示"删除图书信息"标题
      Text('删除图书信息')
        // 设置标题的字体大小为40
        .fontSize(40)
          // 设置标题下方的外边距为20
        .margin({
          bottom: 20
        })
      // 创建一个Column布局组件来垂直排列书籍信息
      Column({ space: 10 }) {
        // 创建一个Row布局组件来水平排列标签和书籍名称
        Row() {
          // 创建一个Text组件显示"书名: "标签
          Text('书名: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的30%
            .width("30%")
          // 创建一个Text组件显示书籍名称
          Text(this.title)
        }
        // 设置Row布局的高度为5%,以及上下的外边距
        .height("5%")
        .width('100%')
        .margin({ top: 10, bottom: 10 })

        // 以下Row组件重复上述步骤,分别显示作者、出版年份和书籍类型

        // 创建一个Row布局组件来水平排列标签和作者名称
        Row() {
          // 创建一个Text组件显示"作者: "标签
          Text('作者: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的30%
            .width("30%")
          // 创建一个Text组件显示作者名称
          Text(this.author)
        }
        // 设置Row布局的宽度为100%,高度为5%,以及下方的外边距
        .width('100%')
        .height("5%")
        .margin({ bottom: 10 })

        // 创建一个Row布局组件来水平排列标签和出版年份
        Row() {
          // 创建一个Text组件显示"出版年份: "标签
          Text('出版年份: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的30%
            .width("30%")
          // 创建一个Text组件显示出版年份
          Text(this.publicationYear)
        }
        // 设置Row布局的高度为7%,以及宽度为100%
        .height("7%")
        .width('100%')

        // 创建一个Row布局组件来水平排列标签和书籍类型
        Row() {
          // 创建一个Text组件显示"书籍类型: "标签
          Text('书籍类型: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的30%
            .width("30%")
          // 创建一个Text组件显示书籍类型
          Text(this.type)
        }
        // 设置Row布局的高度为7%,以及宽度为100%和下方的外边距
        .height("7%")
        .width('100%')
        .margin({ bottom: 10 })
      }
      // 设置内部Column布局的对齐方式、背景颜色、边框圆角、宽度
      .alignItems(HorizontalAlign.Start)
      .backgroundColor("#FFFFFF")
      .borderRadius("24")
      .width('90%')

      // 创建一个Row布局组件来水平排列按钮
      Row() {
        // 创建一个Button组件用于取消操作
        Button('取消')
          // 设置点击事件处理函数,点击时返回上一页
          .onClick(() => {
            router.back()
          })
            // 设置按钮宽度为父容器宽度的35%,以及右侧外边距
          .width("35%")
          .margin({ right: 20 })
        // 创建一个Button组件用于删除书籍
        Button('删除')
          // 设置点击事件处理函数,点击时调用BooksTable的deleteById方法删除书籍
          .onClick(() => {
            // 调用BooksTable的deleteById方法,传入bookId删除书籍,并处理回调结果
            // BooksTable.booksTable.deleteById(this.bookId, (flag) => {
            //   // 如果flag为true,表示删除成功,输出成功信息
            //   if (flag) {
            //     console.info('删除成功')
            //   } else {
            //     // 如果flag为false,表示删除失败,输出失败信息
            //     console.info('删除失败')
            //   }
            // })
            // 删除完成后返回上一页
            router.back()
          })
            // 设置按钮宽度为父容器宽度的35%
          .width("35%")
      }
      // 设置Row布局的对齐方式为两端对齐,并设置上方外边距
      .justifyContent(FlexAlign.SpaceBetween)
      .margin({ top: 20 })
    }
    // 设置外部Column布局的对齐方式、高度、宽度和背景颜色
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .width('100%')
    .height('100%')
    .backgroundColor("#F1F3F5")
  }
}

代码总结:

  1. 展示了如何使用路由参数来获取并显示要删除的书籍信息,并提供了取消和删除操作的按钮。
  2. 当用户点击删除按钮时,会调用BooksTable类的deleteById方法来从数据库中删除相应的书籍记录。
  3. 如果删除成功,会输出"删除成功"的日志信息;如果失败,则输出"删除失败"。

ModifyBookPage.ets

修改图书的页面。

// 引入路由模块,用于页面间的导航
import router from '@ohos.router'
// 引入BooksTable类,用于数据库操作
// import BooksTable from '../common/database/tables/BooksTable'
// 引入Book模型,代表书籍数据结构
import Book from '../model/Book'

// 使用@Entry装饰器标记该组件为程序入口
@Entry
  // 使用@Component装饰器定义一个组件
@Component
  // 定义一个名为ModifyBookPage的结构体,用作修改书籍页面的组件
struct ModifyBookPage {
  // 私有变量bookId,用于存储要修改的书籍ID
  private bookId: number
  // 使用@State装饰器声明响应式状态变量,用于存储书籍信息
  @State title: string = ''
  @State author: string = ''
  @State publicationYear: string = ''
  @State type: string = ''

  // onPageShow生命周期钩子,在页面显示时触发
  onPageShow() {
    // 从路由参数中获取书籍ID和其他信息,并赋值给相应的状态变量
    this.bookId = router.getParams()['bookId']
    this.title = router.getParams()['title']
    this.author = router.getParams()['author']
    this.publicationYear = router.getParams()['publicationYear']
    this.type = router.getParams()['type']
  }

  // build方法定义组件的UI布局
  build() {
    // 使用Column布局组件来垂直排列子组件
    Column() {
      // 创建一个Text组件显示"修改图书信息"标题
      Text("修改图书信息")
        // 设置标题的字体大小为40
        .fontSize(40)
          // 设置标题下方的外边距为20
        .margin({
          bottom: 20
        })
      // 创建一个Column布局组件来垂直排列输入字段
      Column() {
        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"书名: "标签
          Text('书名: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入书名
          TextInput({ text: this.title })
            // 设置输入变化时的回调函数,更新title状态变量
            .onChange((value) => {
              this.title = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及上下的外边距
        .height("7%")
        .margin({ top: 10, bottom: 10 })
        .width("100%")

        // 以下Row组件重复上述步骤,分别用于输入作者、出版年份和书籍类型

        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"作者: "标签
          Text('作者: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入作者
          TextInput({ text: this.author })
            // 设置输入变化时的回调函数,更新author状态变量
            .onChange((value) => {
              this.author = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及下方的外边距
        .height("7%")
        .margin({ bottom: 10 })
        .width("100%")

        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"出版年份: "标签
          Text('出版年份: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入出版年份
          TextInput({ text: this.publicationYear.toString() })
            // 设置输入变化时的回调函数,更新publicationYear状态变量
            .onChange((value) => {
              this.publicationYear = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及下方的外边距
        .height("7%")
        .margin({ bottom: 10 })
        .width("100%")

        // 创建一个Row布局组件来水平排列标签和输入框
        Row() {
          // 创建一个Text组件显示"书籍类型: "标签
          Text('书籍类型: ')
            // 设置标签的文本对齐方式为居中
            .textAlign(TextAlign.Center)
              // 设置标签占据父容器宽度的25%
            .width("25%")
          // 创建一个TextInput组件用于输入书籍类型
          TextInput({ text: this.type })
            // 设置输入变化时的回调函数,更新type状态变量
            .onChange((value) => {
              this.type = value
            })
              // 设置输入框占据父容器宽度的71%
            .width("71%")
        }
        // 设置Row布局的高度为7%,以及下方的外边距
        .height("7%")
        .margin({ bottom: 10 })
        .width("100%")
      }
      // 设置内部Column布局的对齐方式、背景颜色、边框圆角、宽度和下方外边距
      .alignItems(HorizontalAlign.End)
      .backgroundColor("#FFFFFF")
      .borderRadius("24")
      .width('90%')
      .margin({
        bottom: 20
      })

      // 创建一个Row布局组件来水平排列按钮
      Row() {
        // 创建一个Button组件用于返回上一页
        Button('返回')
          // 设置点击事件处理函数,点击时返回上一页
          .onClick(() => {
            router.back()
          })
            // 设置按钮宽度为父容器宽度的35%,以及右侧外边距
          .width("35%")
          .margin({ right: 20 })
        // 创建一个Button组件用于提交修改
        Button('修改')
          // 设置点击事件处理函数,点击时创建Book对象并更新数据库
          .onClick(() => {
            // 创建一个Book对象,包含用户输入的书籍信息
            const book: Book = {
              id: this.bookId,
              title: this.title,
              author: this.author,
              publicationYear: this.publicationYear,
              type: this.type
            }
            // 调用BooksTable的updateData方法更新书籍数据,并处理回调结果
            // BooksTable.booksTable.updateData(book, (flag) => {
            //   // 如果flag为true,表示修改成功,输出成功信息
            //   if (flag) {
            //     console.info('修改成功')
            //   } else {
            //     // 如果flag为false,表示修改失败,输出失败信息
            //     console.info('修改失败')
            //   }
            // })
            // 修改完成后返回上一页
            router.back()
          })
            // 设置按钮宽度为父容器宽度的35%
          .width("35%")
      }
    }
    // 设置外部Column布局的对齐方式、高度、宽度和背景颜色
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
    .backgroundColor("#F1F3F5")
  }
}

代码总结:

  1. 展示了如何使用路由参数来获取并显示要修改的书籍信息,并提供了输入框让用户更新信息。
  2. 当用户点击修改按钮时,会创建一个新的Book对象,并通过BooksTable类的updateData方法将其保存到数据库中。修改成功或失败都会在日志文件中提示

BooksListPage.ets

展示图书

// 导入HarmonyOS的路由模块,用于页面跳转
import router from '@ohos.router'
// 导入BooksTable类,用于数据库操作
// import BooksTable from '../common/database/tables/BooksTable'
// 导入Book模型,代表书籍数据结构
import Book from '../model/Book'

// 使用@Entry装饰器标记该组件为程序入口
@Entry
// 使用@Component装饰器定义一个组件
@Component
// 定义一个名为BooksListPage的结构体,用作显示书籍列表的页面组件
struct BooksListPage {
  // 使用@State装饰器声明响应式状态变量booksList,用于存储书籍列表
  @State booksList: Book[] = []
  // 私有变量state,用于表示不同的页面状态:查看状态1、修改状态2、删除状态3
  private state: number

  // onPageShow生命周期钩子,在页面显示时触发
  onPageShow() {
    // 从路由参数中获取页面状态,并赋值给state变量
    this.state = router.getParams()['state']
    // 调用load方法加载书籍列表
    this.load()
  }

  // load方法用于从数据库中查询所有书籍信息,并更新booksList状态变量
  load() {
    // 调用BooksTable的queryAll方法查询所有书籍信息
    // BooksTable.booksTable.queryAll((result) => {
    //   // 将查询结果赋值给booksList状态变量
    //   this.booksList = result
    // })
  }

  // modifyBook方法用于处理修改书籍信息的操作
  modifyBook(id: number, title: string, author: string, publicationYear: string, type: string) {
    // 使用router的pushUrl方法跳转到ModifyBookPage页面,并传递书籍信息作为参数
    router.pushUrl({
      url: 'pages/ModifyBookPage',
      params: {
        bookId: id,
        title: title,
        author: author,
        publicationYear: publicationYear,
        type: type
      }
    })
  }

  // deleteBook方法用于处理删除书籍信息的操作
  deleteBook(id: number, title: string, author: string, publicationYear: string, type: string) {
    // 使用router的pushUrl方法跳转到DeleteBookPage页面,并传递书籍信息作为参数
    router.pushUrl({
      url: 'pages/DeleteBookPage',
      params: {
        bookId: id,
        title: title,
        author: author,
        publicationYear: publicationYear,
        type: type
      }
    })
  }

  // build方法定义组件的UI布局
  build() {
    // 使用Column布局组件来垂直排列子组件
    Column() {
      // 使用List组件来显示书籍列表
      List() {
        // 使用ForEach组件遍历booksList状态变量中的每本书
        ForEach(this.booksList, (item: Book) => {
          // 使用ListItem组件来定义列表中的每一项
          ListItem() {
            // 使用Column布局组件来垂直排列书籍信息
            Column() {
              // 使用Row布局组件来水平排列书籍图标和文本信息
              Row() {
                // 使用Image组件来显示书籍图标
                Image($r('app.media.book_icon'))
                  // 设置图像填充方式为填满
                  .objectFit(ImageFit.Fill)
                  // 设置图像宽度为10%
                  .width("10%")
                  // 设置图像高度为45%
                  .height("45%")
                // 使用Column布局组件来垂直排列书名和作者信息
                Column({ space: 10 }) {
                  // 使用Text组件来显示书名
                  Text('书名: ' + item.title)
                    // 设置字体大小为20
                    .fontSize("20")
                    // 设置文本溢出处理为省略号
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    // 设置最大行数为1
                    .maxLines(1)
                  // 使用Text组件来显示作者
                  Text('作者: ' + item.author)
                    // 设置字体大小为20
                    .fontSize("20")
                    // 设置文本溢出处理为省略号
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    // 设置最大行数为1
                    .maxLines(1)
                }
                // 设置内部Column布局的宽度为30%并开始对齐
                .width('30%')
                .alignItems(HorizontalAlign.Start)

                // 使用Column布局组件来垂直排列出版年份和书籍类型信息
                Column({ space: 10 }) {
                  // 使用Text组件来显示出版年份
                  Text('出版年份: ' + item.publicationYear)
                    // 设置字体大小为20
                    .fontSize("20")
                    // 设置文本溢出处理为省略号
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    // 设置最大行数为1
                    .maxLines(1)
                  // 使用Text组件来显示书籍类型
                  Text('书籍类型: ' + item.type)
                    // 设置字体大小为20
                    .fontSize("20")
                    // 设置文本溢出处理为省略号
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    // 设置最大行数为1
                    .maxLines(1)
                }
                // 设置内部Column布局的宽度为40%并开始对齐
                .width('40%')
                .alignItems(HorizontalAlign.Start)
              }
              // 设置Row布局的内边距、宽度、高度、边框圆角和背景颜色
              .padding(10)
              .width('90%')
              .height("13%")
              .borderRadius("24")
              .backgroundColor('#FFFFFF')
              // 设置内容在Row布局中的分布方式为两端对齐
              .justifyContent(FlexAlign.SpaceAround)
            }
            // 设置ListItem组件的上边距
            .margin({
              top: 20
            })
          }
          // 设置ListItem组件的点击事件处理函数
          .onClick(() => {
            // 使用switch语句根据state变量的值执行不同的操作
            switch (this.state) {
              // 如果state为1,表示查看状态
              case 1:
                // 输出查看状态的日志信息
                console.info('查看转态')
                break;
              // 如果state为2,表示修改状态
              case 2:
                // 输出修改状态的日志信息
                console.info('修改转态')
                // 调用modifyBook方法处理修改操作
                this.modifyBook(item.id, item.title, item.author, item.publicationYear, item.type)
                break;
              // 如果state为3,表示删除状态
              case 3:
                // 输出删除状态的日志信息
                console.info('删除转态')
                // 调用deleteBook方法处理删除操作
                this.deleteBook(item.id, item.title, item.author, item.publicationYear, item.type)
                break;
            }
          })
        })
      }
      // 设置List组件的列表项对齐方式、宽度和高度
      .alignListItem(ListItemAlign.Center)
      .width('100%')
      .height('100%')
    }
    // 设置外部Column布局的背景颜色
    .backgroundColor("#F1F3F5")
  }
}

代码总结:

  1. 主要负责显示数据库中所有书籍的信息,并根据用户的不同操作状态(查看、修改、删除)提供相应的功能。
  2. 代码中使用了HarmonyOS的UI组件和布局来构建一个响应式的用户界面,使得用户可以方便地浏览和管理书籍。
  3. 通过路由参数传递,该页面能够处理不同的用户交互,如跳转到修改或删除书籍的页面。
弹窗设计

在展示图书的代码上加入以下代码

// 导入HarmonyOS的路由模块,用于页面跳转
import router from '@ohos.router'
// 导入BooksTable类,用于数据库操作
import BooksTable from '../common/database/tables/BooksTable'
// 导入Book模型,代表书籍数据结构
import Book from '../model/Book'

// 使用@Entry装饰器标记该组件为程序入口
@Entry
// 使用@Component装饰器定义一个组件
@Component
// 定义一个名为BooksListPage的结构体,用作显示书籍列表的页面组件
struct BooksListPage {
  // 使用@State装饰器声明响应式状态变量booksList,用于存储书籍列表
  @State booksList: Book[] = []
  // 私有变量state,用于表示不同的页面状态:查看状态1、修改状态2、删除状态3
  private state: number
  // 私有变量title,用于存储当前选中书籍的标题
  private title: string = ""
  // 私有变量author,用于存储当前选中书籍的作者
  private author: string = ""
  // 私有变量publicationYear,用于存储当前选中书籍的出版年份
  private publicationYear: string = ""
  // 私有变量type,用于存储当前选中书籍的类型
  private type: string = ""

  // 省略部分代码...

  // 定义一个CustomDialogController对象,用于控制自定义对话框
  dialogController: CustomDialogController = new CustomDialogController({
    // 使用CustomDialogExample构建器来创建对话框内容
    builder: CustomDialogExample({
      // 将当前选中书籍的信息传递给对话框
      title: this.title,
      author: this.author,
      publicationYear: this.publicationYear,
      type: this.type
    }),
    // 设置对话框的对齐方式为居中
    alignment: DialogAlignment.Center
  })

  // build方法定义组件的UI布局
  build() {
    // 使用Column布局组件来垂直排列子组件
    Column() {
      // 使用List组件来显示书籍列表
      List() {
        // 使用ForEach组件遍历booksList状态变量中的每本书
        ForEach(this.booksList, (item: Book) => {
          // 省略部分代码...

          // 设置ListItem组件的点击事件处理函数
          .onClick(() => {
            // 更新当前选中书籍的信息
            this.title = item.title
            this.author = item.author
            this.publicationYear = item.publicationYear
            this.type = item.type
            // 使用switch语句根据state变量的值执行不同的操作
            switch (this.state) {
              // 如果state为1,表示查看状态
              case 1:
                // 输出查看状态的日志信息
                console.info('查看转态')
                // 打开自定义对话框
                this.dialogController.open()
                break;
              // 如果state为2,表示修改状态
              case 2:
                // 输出修改状态的日志信息
                console.info('修改转态')
                // 调用modifyBook方法处理修改操作
                this.modifyBook(item.id, item.title, item.author, item.publicationYear, item.type)
                break;
              // 如果state为3,表示删除状态
              case 3:
                // 输出删除状态的日志信息
                console.info('删除转态')
                // 调用deleteBook方法处理删除操作
                this.deleteBook(item.id, item.title, item.author, item.publicationYear, item.type)
                break;
            }
          })
        })
      }
      // 设置List组件的列表项对齐方式、宽度和高度
      .alignListItem(ListItemAlign.Center)
      .width('100%')
      .height('100%')
    }
    // 设置外部Column布局的背景颜色
    .backgroundColor("#F1F3F5")
  }
}

// 使用@CustomDialog装饰器定义一个自定义对话框
@CustomDialog
struct CustomDialogExample {
  // CustomDialogController对象,用于控制对话框
  controller: CustomDialogController
  // 私有变量title,用于存储书籍的标题
  private title;
  // 私有变量author,用于存储书籍的作者
  private author;
  // 私有变量publicationYear,用于存储书籍的出版年份
  private publicationYear;
  // 私有变量type,用于存储书籍的类型
  private type;

  // build方法定义对话框的UI布局
  build() {
    // 使用Column布局组件来垂直排列子组件
    Column({ space: 10 }) {
      // 使用Row布局组件来水平排列文本标签和书籍标题
      Row() {
        // 使用Text组件来显示"书名: "标签
        Text('书名: ')
          // 设置文本对齐方式为居中
          .textAlign(TextAlign.Center)
          // 设置文本宽度为30%
          .width("30%")
        // 使用Text组件来显示书籍标题
        Text(this.title)
      }
      // 设置Row布局的高度、宽度和上下边距
      .height("5%")
      .width('100%')
      .margin({ top: 10, bottom: 10 })

      // 使用Row布局组件来水平排列文本标签和书籍作者
      Row() {
        // 使用Text组件来显示"作者: "标签
        Text('作者: ')
          // 设置文本对齐方式为居中
          .textAlign(TextAlign.Center)
          // 设置文本宽度为30%
          .width("30%")
        // 使用Text组件来显示书籍作者
        Text(this.author)
      }
      // 设置Row布局的宽度、高度和下边距
      .width('100%')
      .height("5%")
      .margin({ bottom: 10 })

      // 使用Row布局组件来水平排列文本标签和书籍出版年份
      Row() {
        // 使用Text组件来显示"出版年份: "标签
        Text('出版年份: ')
          // 设置文本对齐方式为居中
          .textAlign(TextAlign.Center)
          // 设置文本宽度为30%
          .width("30%")
        // 使用Text组件来显示书籍出版年份
        Text(this.publicationYear)
      }
      // 设置Row布局的高度和宽度
      .height("7%")
      .width('100%')

      // 使用Row布局组件来水平排列文本标签和书籍类型
      Row() {
        // 使用Text组件来显示"书籍类型: "标签
        Text('书籍类型: ')
          // 设置文本对齐方式为居中
          .textAlign(TextAlign.Center)
          // 设置文本宽度为30%
          .width("30%")
        // 使用Text组件来显示书籍类型
        Text(this.type)
      }
      // 设置Row布局的高度、宽度和下边距
      .height("7%")
      .width('100%')
      .margin({ bottom: 10 })
    }
    // 设置外部Column布局的对齐方式、背景颜色、边框圆角和宽度
    .alignItems(HorizontalAlign.Start)
    .backgroundColor("#FFFFFF")
    .borderRadius("24")
    .width('90%')
  }
}

代码总结:

这段代码设计了一个自定义弹窗,内容为列表的上的图书信息,详细可以查看超链接

Rdb.ets文件的设计:

作用介绍:

Rdb.ets 文件设计的目的是为了对关系型数据库(RelationalStore)进行二次封装,以便于系统中其他模块更便捷地进行数据库操作。该文件保存了 relationalStore.RdbStore 的实例,以提高系统中数据库操作的效率和一致性。

1. 创建文件Rdb.ets:

  1. 在目录common/database,创建文件:

2. 导入模块:

import relationalStore from '@ohos.data.relationalStore'; // 导入模块 
import CommonConstants from '../constants/CommonConstants';// 导入常量类

导入模块 `relationalStore` 是用于处理关系型数据操作的模块,提供了对关系型数据库的访问和操作。

而导入 `CommonConstants` 模块则是为了获取在创建数据库时所需的常量参数。

3. 定义类Rdb:

import relationalStore from '@ohos.data.relationalStore';
import CommonConstants from '../constants/CommonConstants';

export default class Rdb {
  private rdbStore: relationalStore.RdbStore | null = null;//用于保存数据库存储对象实例。

  constructor(sqlCreateTable: string, callback: Function) {
    // 获取上下文对象
    let context: Context = getContext(this) as Context;
    // 获取关系型数据库存储对象
    relationalStore.getRdbStore(context, CommonConstants.STORE_CONFIG, (err, rdb) => {
      // 如果出现错误,则记录错误日志
      if (err) {
        callback(false);
        return;
      }
      this.rdbStore = rdb; // 将获取的关系型数据库存储对象赋值给成员变量

      // 调用 this.createTable 方法创建表结构,这里我暂不提供该代码,只是简单了解即可,后面重点说明
      this.createTable(sqlCreateTable, (flag) => {
        callback(flag); // 通过回调函数返回执行情况
      });
    });
  }
  // 其他方法(如创建表、插入数据、更新删除数据等操作)省略
}
  1. rdbStore 属性:
    • rdbStorerelationalStore.RdbStore 类型的私有属性,用于保存数据库存储对象实例。
  1. 构造函数 (constructor):
    • 接收两个参数:sqlCreateTablecallback
    • sqlCreateTable 是用来创建表结构的 SQL 语句。
    • callback 是在创建 Rdb 实例完成后调用的回调函数,用于通知调用者实例创建的情况。
  1. createTable 方法:
    • 该方法用于创建表结构,接收一个 SQL 创建表的语句 (sqlCreateTable) 和一个回调函数 (callback),回调函数用来返回该方法的执行情况 ( flag )。
    • 该方法在上述代码中并不提供,只需简单理解即可,后面会重点介绍。
  1. 错误处理:
    • 在获取数据库存储对象时,如果出现错误,会通过回调函数返回 false,并记录错误日志。
  1. 异步操作:
    • 整个过程是异步的,因为涉及到数据库操作,可能需要一些时间来完成。因此,采用回调函数的方式来处理结果,确保在完成操作后通知调用者。

relationalStore.getRdbStore接口说明:

getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): void

功能:获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作,使用callback异步回调。

参数介绍:

参数名

类型

必填

说明

context

Context

应用的上下文。

config

StoreConfig

与此RDB存储相关的数据库配置。

callback

AsyncCallback<RdbStore>

指定callback回调函数,返回RdbStore对象。

4. createTable方法:

Rdb类中,我们提供了一个名为 createTable 的方法,用于在数据库中创建表结构。当我们获取到 relationalStore.RdbStore 实例后,即数据库已经准备好,但尚未具体定义表结构。因此,我们需要调用 createTable 方法来定义所需的表结构。

import relationalStore from '@ohos.data.relationalStore'; 
import CommonConstants from '../constants/CommonConstants';

export default class Rdb {
  private rdbStore: relationalStore.RdbStore | null = null;
  
  ... 

  /**
 * 创建数据表的方法,通过执行传入的 SQL 语句来创建表结构。
 * @param sqlCreateTable - 用于创建表结构的 SQL 语句。
 * @param callback - 创建表结构完成后的回调函数,用于通知调用者操作的结果。
 */
createTable(sqlCreateTable: string, callback: Function) {
  // 检查传入的 SQL 语句是否为空或者数据库存储对象是否存在,若是则无法执行创建表结构操作。
  if (sqlCreateTable == "" || sqlCreateTable == null || !this.rdbStore) {
    // 调用回调函数,通知调用者创建表结构失败,并返回 false。
    callback(false);
    return;
  }
  // 使用数据库存储对象执行 SQL 语句来创建表结构。
  this.rdbStore.executeSql(sqlCreateTable)
    .then(() => {
      // 如果执行 SQL 成功,则调用回调函数,通知调用者创建表结构成功,并返回 true。
      callback(true);
    })
    .catch(() => {
      // 如果执行 SQL 失败,则调用回调函数,通知调用者创建表结构失败,并返回 false。
      callback(false);
    });
}
}
  1. 方法签名
createTable(sqlCreateTable: string, callback: Function)
  1. 参数
    • sqlCreateTable:用于创建表结构的 SQL 语句。
    • callback:在创建表结构完成后调用的回调函数,用于通知调用者操作的结果。
  1. 方法功能
    • 当数据库已准备就绪后,通过执行传入的 SQL 语句来创建表结构。
    • 在创建过程中,会通过回调函数通知调用者操作的结果。
  1. 方法逻辑
    • 首先检查传入的 SQL 语句是否为空或者数据库存储对象是否存在,如果不符合条件,则无法执行创建表结构操作,直接调用回调函数通知调用者操作失败。
    • 如果传入的 SQL 语句和数据库存储对象都是有效的,就使用数据库存储对象的 executeSql 方法执行 SQL 语句。
    • 如果执行成功,则调用回调函数通知调用者操作成功;如果执行失败,则通知操作失败。

RdbStore.executeSql接口说明:

executeSql(sql: string, bindArgs?: Array<ValueType>):Promise<void>

执行包含指定参数但不返回值的SQL语句,使用Promise异步回调。

参数:

参数名

类型

必填

说明

sql

string

指定要执行的SQL语句。

bindArgs

Array<ValueType>

SQL语句中参数的值。当sql参数语句完整时,该参数不填。

5. insertData方法:

在 Rdb 类中,我们提供了一个名为 insertData 的方法,用于向数据库表中插入数据。当我们已经创建了数据库和数据表之后,接下来的操作就是向表中添加数据,而 insertData 方法就是用来实现这个功能的。


// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import CommonConstants from '../constants/CommonConstants';


// 定义类 Rdb
export default class Rdb {
  private rdbStore: relationalStore.RdbStore | null = null; // 关系型数据库存储对象
  
  ... 
  
  // 插入数据的方法,接受数据和回调函数作为参数
  insertData(data: relationalStore.ValuesBucket, tableName: string, callback: Function) {
    if (this.rdbStore) {
      this.rdbStore.insert(tableName, data, (err, ret) => {
        // 如果出现错误,则记录错误日志
        if (err) {
          callback(false); // 执行回调函数
          return;
        }
        callback(ret); // 执行回调函数
      });
    }
  }
}
  1. 方法签名
insertData(data: relationalStore.ValuesBucket, tableName: string, callback: Function)
  1. 参数
    • datarelationalStore.ValuesBucket 类型的数据,用于存储要插入的数据的键值对。
    • tableName:表名,字符串类型,指定要插入数据的目标表。
    • callback:在插入数据操作完成后调用的回调函数,用于反馈该方法的执行情况。
  1. 方法功能
    • 当数据库已准备就绪后,通过执行 insertData 方法来向指定的数据表中插入数据。
    • 在插入过程中,会通过回调函数通知调用者操作的结果。
  1. 方法逻辑
    • 首先检查是否存在数据库存储对象 (rdbStore),如果不存在则无法执行插入数据操作,直接返回。
    • 使用数据库存储对象的 insert 方法,向指定的表 (tableName) 插入数据 (data)。
    • 在插入操作完成后,通过回调函数通知调用者操作的结果。如果操作成功,则将结果传递给回调函数;如果操作失败,则返回 false

RdbStore.insert接口说明:

insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void

功能:向目标表中插入一行数据,使用callback异步回调。

参数:

参数名

类型

必填

说明

table

string

指定的目标表名。

values

ValuesBucket

表示要插入到表中的数据行。

callback

AsyncCallback<number>

指定callback回调函数。如果操作成功,返回行ID;否则返回-1。

values: ValuesBucket使用方法

let obj: relationalStore.ValuesBucket = {};
  obj.id = book.id;
  obj.title = book.title;
  obj.author = book.author;
  obj.publicationYear = book.publicationYear;
  obj.type = book.type;

6. deleteData 方法:

在 Rdb 类中,我们提供了一个名为 deleteData 的方法,用于从数据库表中删除数据。这个方法接收两个参数,一个是用于指定删除条件的 predicates,另一个是在操作完成后调用的 callback 回调函数。

// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import CommonConstants from '../constants/CommonConstants';


// 定义类 Rdb
export default class Rdb {
  private rdbStore: relationalStore.RdbStore | null = null; // 关系型数据库存储对象
  
  ... //构造方法、创建表、添加数据的方法
  
    // 删除数据的方法,接受谓词和回调函数作为参数
  deleteData(predicates: relationalStore.RdbPredicates, callback: Function) {
    // 如果关系型数据库存储对象存在,则执行删除操作
    if (this.rdbStore) {
      this.rdbStore.delete(predicates, (err, ret) => {
        // 如果出现错误,则记录错误日志
        if (err) {
          callback(false); // 执行回调函数
          return;
        }
        callback(ret); // 执行回调函数
      });
    }
  }
}
  1. 方法签名
deleteData(predicates: relationalStore.RdbPredicates, callback: Function)
  1. 参数
    • predicatesrelationalStore.RdbPredicates 类型的对象,用于指定需要删除的条件。这个条件由上一层调用该方法的提供。
    • callback:在删除数据操作完成后调用的回调函数,用于反馈执行结果的成功与否。
  1. 方法功能
    • 当数据库已准备就绪后,通过执行 deleteData 方法来从指定的数据表中删除数据。
    • 在删除过程中,会通过回调函数通知调用者操作的结果。
  1. 方法逻辑
    • 首先检查是否存在数据库存储对象 (rdbStore),如果不存在则无法执行删除数据操作,直接返回。
    • 使用数据库存储对象的 delete 方法,根据指定的条件 predicates 来删除数据。
    • 在删除操作完成后,通过回调函数通知调用者操作的结果。如果操作成功,则将结果传递给回调函数;如果操作失败,则返回 false

RdbStore.delete接口说明

delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void

功能:根据RdbPredicates的指定实例对象从数据库中删除数据,使用callback异步回调。

参数:

参数名

类型

必填

说明

predicates

RdbPredicates

RdbPredicates的实例对象指定的删除条件。

callback

AsyncCallback<number>

指定callback回调函数。返回受影响的行数。

7. updateData 方法说明:

Rdb 类中,我们提供了一个名为 updateData 的方法,用于更新数据库表中的数据。这个方法接收三个参数,分别是用于指定更新条件的 predicates、需要更新的数据 data,以及在操作完成后调用的 callback 回调函数。


// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块

// 定义类 Rdb
export default class Rdb {
  private rdbStore: relationalStore.RdbStore | null = null; // 关系型数据库存储对象
  
  ... 
  
  // 更新数据的方法,接受谓词、数据和回调函数作为参数
  updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function) {
    // 如果关系型数据库存储对象存在,则执行更新操作
    if (this.rdbStore) {
      this.rdbStore.update(data, predicates, (err, ret) => {
        // 如果出现错误,则记录错误日志
        if (err) {
          callback(false); // 执行回调函数
          return;
        }
        callback(ret); // 执行回调函数
      });
    }
  }
}
  1. 方法签名
updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function)
  1. 参数
    • predicatesrelationalStore.RdbPredicates 类型的对象,指定需要做出更新的条件。
    • datarelationalStore.ValuesBucket 类型的对象,需要更新的数据。
    • callback:在更新数据操作完成后调用的回调函数,用于反馈执行结果的成功与否。
  1. 方法功能
    • 当数据库已准备就绪后,通过执行 updateData 方法来更新指定的数据表中的数据。
    • 在更新过程中,会通过回调函数通知调用者操作的结果。
  1. 方法逻辑
    • 首先检查是否存在数据库存储对象 (rdbStore),如果不存在则无法执行更新数据操作,直接返回。
    • 使用数据库存储对象的 update 方法,根据指定的更新条件 predicates 和更新的数据 data 来进行数据更新。
    • 在更新操作完成后,通过回调函数通知调用者操作的结果。如果操作成功,则将受影响的行数传递给回调函数;如果操作失败,则返回 false

RdbStore.update接口说明:

update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void

功能:根据RdbPredicates的指定实例对象更新数据库中的数据,使用callback异步回调。

参数:

参数名

类型

必填

说明

values

ValuesBucket

values指示数据库中要更新的数据行。键值对与数据库表的列名相关联。

predicates

RdbPredicates

RdbPredicates的实例对象指定的更新条件。

callback

AsyncCallback<number>

指定的callback回调方法。返回受影响的行数。

8. query 方法说明:

Rdb 类中,我们提供了一个名为 query 的方法,用于从数据库表中查询数据。这个方法接收两个参数,一个是用于指定查询条件的 predicates,另一个是在查询完成后调用的 callback 回调函数。


// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块


// 定义类 Rdb
export default class Rdb {
  private rdbStore: relationalStore.RdbStore | null = null; // 关系型数据库存储对象
  
  ... 
  
  // 查询数据的方法,接受谓词和回调函数作为参数
  query(predicates: relationalStore.RdbPredicates, callback: Function) {
    // 如果关系型数据库存储对象存在,则执行查询操作
    if (this.rdbStore) {
      this.rdbStore.query(predicates).then((resultSet) => {
        callback(resultSet); // 执行回调函数
        resultSet.close(); // 关闭结果集
      }).catch(() => {
        callback(false)
      })
    }
  }
  
}
  1. 方法签名
query(predicates: relationalStore.RdbPredicates, callback: Function)
  1. 参数
    • predicatesrelationalStore.RdbPredicates 类型的对象,用于指定查询条件。
    • callback:在查询数据操作完成后调用的回调函数,用于返回查询的结果。
  1. 方法功能
    • 当数据库已准备就绪后,通过执行 query 方法来从指定的数据表中查询数据。
    • 在查询过程中,会通过回调函数通知调用者查询的结果。
  1. 方法逻辑
    • 首先检查是否存在数据库存储对象 (rdbStore),如果不存在则无法执行查询操作,直接返回。
    • 使用数据库存储对象的 query 方法,根据指定的查询条件 predicates 来执行查询操作。
    • 查询操作返回一个 Promise,通过 .then() 方法获取查询结果 resultSet
    • 在获取到结果后,通过回调函数将查询结果传递给调用者,并关闭结果集。
    • 如果查询操作出现异常,则通过回调函数通知调用者查询失败,并返回 false

RdbStore.delete接口说明:

query(predicates: RdbPredicates, columns?: Array<string>):Promise<ResultSet>

功能:根据指定条件查询数据库中的数据,使用Promise异步回调。

参数:

参数名

类型

必填

说明

predicates

RdbPredicates

RdbPredicates的实例对象指定的查询条件。

columns

Array<string>

表示要查询的列。如果值为空,则查询应用于所有列。

BooksTable.ets文件的设计:

在数据库操作模块中,我们已经设计了 Rdb.ets 文件,它负责数据库的整体操作,如创建表结构、删除数据、添加数据等。但是,这些操作是针对整个数据库的,缺乏对具体表的细节处理。因此,我们需要对 Rdb.ets 进行二次封装,以便针对各个表进行更具体的操作。BooksTable.ets 就是对 Rdb.ets 的二次封装,专门用于处理与 books 表相关的操作。通过这样的封装,我们能够更好地适应复杂的业务功能,提高代码的可读性和灵活性。

作用介绍:

  1. 针对 books 表的操作: BooksTable.ets 主要用于对 books 表进行各种操作,如插入数据、更新数据、删除数据、查询数据等。
  2. 封装具体的业务逻辑: BooksTable.ets 将具体的业务逻辑封装在对应的方法中,使代码更加清晰和易于理解。
  3. 提高代码复用性: 将与 books 表相关的操作封装在一个文件中,可以在不同的地方重复使用,提高代码的复用性和可维护性。

1. 新建BooksTable.ets文件

common/database/tables/ 目录下新建 BooksTable.ets 文件。

2. 定义BooksTable类:

// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import Book from '../../../model/Book'; // 导入书籍模型
import CommonConstants from '../../constants/CommonConstants'; // 导入通用常量
import Rdb from '../Rdb'; // 导入数据库操作模块

// 定义 BooksTable 类
export default class BooksTable {
  public static booksTable: BooksTable = null; // 静态属性,用于保存 BooksTable 类的单例实例
  private booksRdb: Rdb; // 用于存储与数据库相关的操作实例
  private tableName: string = CommonConstants.BOOKS_TABLE.tableName; // 存储书籍表的名称
  private sqlCreateTable: string = CommonConstants.BOOKS_TABLE.sqlCreate; // 存储创建表的 SQL 语句

  // 构造函数,接收一个回调函数作为参数
  constructor(callback: Function) {
    // 创建 Rdb 实例,传入创建表的 SQL 语句和回调函数
    this.booksRdb = new Rdb(this.sqlCreateTable, (flag) => {
      // 将操作结果通过回调函数返回给调用者
      callback(flag);
    });
  }
}

属性说明:

  1. 静态属性 booksTable 这是一个静态属性,用于保存 BooksTable 类的单例实例。单例模式确保在整个应用程序中只有一个 BooksTable 实例,这样可以节省资源并且方便管理。
  2. 属性 booksRdb 这是一个私有属性,用于存储与数据库相关的操作实例。我们将通过 Rdb 类来执行与数据库表 books 相关的操作,例如创建表、插入数据等。
  3. 属性 tableNamesqlCreateTable 这两个属性分别用于存储书籍表的名称和创建表的 SQL 语句。表名和 SQL 语句都是从 CommonConstants 常量模块中获取的,这样可以保持代码的清晰性和可维护性。

构造函数:

  1. 构造函数接收一个回调函数作为参数。在构造函数中,我们创建了一个 Rdb 实例,传入创建表的 SQL 语句和一个回调函数。当操作完成后,通过回调函数将操作结果返回给调用者。

3. insertData方法:

// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import Book from '../../../model/Book'; // 导入书籍模型
import CommonConstants from '../../constants/CommonConstants'; // 导入通用常量
import Rdb from '../Rdb'; // 导入数据库操作模块

// 定义 BooksTable 类
export default class BooksTable {
  public static booksTable: BooksTable = null; // 静态属性,用于保存 BooksTable 类的单例实例
  private booksRdb: Rdb; // 用于存储与数据库相关的操作实例
  private tableName: string = CommonConstants.BOOKS_TABLE.tableName; // 存储书籍表的名称
  private sqlCreateTable: string = CommonConstants.BOOKS_TABLE.sqlCreate; // 存储创建表的 SQL 语句
  
  ... 

  // 插入数据的方法,接受书籍对象和回调函数作为参数
  insertData(book: Book, callback: Function) {
    // 将 Book 类型转换为 relationalStore.ValuesBucket 类型
    const valueBucket: relationalStore.ValuesBucket = generateBucket(book);
    // 调用 Rdb 实例的 insertData 方法,插入数据到指定的表中,并传入回调函数
    this.booksRdb.insertData(valueBucket, this.tableName, callback);

  1. insertData 方法: 这个方法用于将书籍对象插入到数据库表中。它接收两个参数:bookcallback
  2. book 参数: 这是一个 Book 类型的对象,表示需要插入的书籍信息。
  3. callback 参数: 这是一个回调函数,用于在插入数据操作完成后通知调用者操作的结果。
  4. generateBucket方法:在方法中,我们通过调用 generateBucket 方法将 Book 类型的对象转换为 relationalStore.ValuesBucket 类型的对象。这是因为数据库操作需要使用特定的数据类型来插入数据。

4. updateData方法:

// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import Book from '../../../model/Book'; // 导入书籍模型
import CommonConstants from '../../constants/CommonConstants'; // 导入通用常量
import Rdb from '../Rdb'; // 导入数据库操作模块

// 定义 BooksTable 类
export default class BooksTable {
  public static booksTable: BooksTable = null; // 静态属性,用于保存 BooksTable 类的单例实例
  private booksRdb: Rdb; // 用于存储与数据库相关的操作实例
  private tableName: string = CommonConstants.BOOKS_TABLE.tableName; // 存储书籍表的名称
  private sqlCreateTable: string = CommonConstants.BOOKS_TABLE.sqlCreate; // 存储创建表的 SQL 语句

  ... 

  // 更新数据的方法,接受书籍对象和回调函数作为参数
  updateData(book: Book, callback: Function) {
    // 将 Book 类型转换为 relationalStore.ValuesBucket 类型
    const valueBucket: relationalStore.ValuesBucket = generateBucket(book);

    // 创建查询条件,指定需要更新的数据行
    let predicates = new relationalStore.RdbPredicates(this.tableName);
    predicates.equalTo('id', book.id); // 指定更新的条件,这里以书籍的 id 为条件

    // 调用 Rdb 实例的 updateData 方法,更新数据,并传入回调函数
    this.booksRdb.updateData(predicates, valueBucket, callback);
  }
}

  1. updateData 方法: 这个方法用于更新书籍表中的数据。它接收两个参数:bookcallback
    • book 参数: 这是一个 Book 类型的对象,表示需要更新的书籍信息。
    • callback 参数: 这是一个回调函数,用于在更新数据操作完成后通知调用者操作的结果。
  1. 转换类型: 在方法中,我们通过调用 generateBucket 方法将 Book 类型的对象转换为 relationalStore.ValuesBucket 类型的对象。这是因为数据库操作需要使用特定的数据类型来更新数据。
  2. 创建查询条件: 我们使用 relationalStore.RdbPredicates 类创建了一个查询条件,用于指定需要更新的数据行。这里我们以书籍的 id 为条件,表示只更新指定 id 的书籍信息。
  3. 更新数据操作: 最后,我们调用 Rdb 实例的 updateData 方法,将转换后的数据和查询条件传入,以执行更新数据的操作。同时,将回调函数传递给 updateData 方法,以便在操作完成后通知调用者操作的结果。

5. deleteById方法:


// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import Book from '../../../model/Book'; // 导入书籍模型
import CommonConstants from '../../constants/CommonConstants'; // 导入通用常量
import Rdb from '../Rdb'; // 导入数据库操作模块

// 定义 BooksTable 类
export default class BooksTable {
  public static booksTable: BooksTable = null; // 静态属性,用于保存 BooksTable 类的单例实例
  private booksRdb: Rdb; // 用于存储与数据库相关的操作实例
  private tableName: string = CommonConstants.BOOKS_TABLE.tableName; // 存储书籍表的名称
  private sqlCreateTable: string = CommonConstants.BOOKS_TABLE.sqlCreate; // 存储创建表的 SQL 语句

  ... 

// 删除数据的方法,接受账户数据对象和回调函数作为参数
  deleteById(id: number, callback: Function) {
    let predicates = new relationalStore.RdbPredicates(this.tableName);
    predicates.equalTo('id', id);
    this.booksRdb.deleteData(predicates, callback);
  }
  1. deleteById方法: 这个方法用于删除书籍表中的数据。它接收两个参数:bookcallback
    • id: number:这是一个整数类型的参数,代表要删除的图书的ID。通过该参数,方法可以确定要删除哪一本图书。
    • callback: Function:这是一个回调函数,用于处理删除操作的结果。在删除操作完成后,将通过该回调函数返回结果。
  1. 创建谓词对象:方法内部首先创建了一个谓词对象 predicates,用于指定删除操作的条件。在这里,通过指定图书表的名称和传入的图书 ID,构建了一个条件,即删除满足特定 ID 的图书。
  2. 调用数据库操作:接下来,调用了 booksRdb 对象的 deleteData 方法执行删除操作。该方法接受谓词对象和回调函数作为参数。删除操作会根据谓词对象指定的条件,在数据库中删除相应的图书记录。
  3. 回调处理结果:删除操作完成后,通过回调函数 callback 返回结果。如果删除操作成功,则返回 true;如果删除操作失败,则返回 false

6. query方法:

// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import Book from '../../../model/Book'; // 导入书籍模型
import CommonConstants from '../../constants/CommonConstants'; // 导入通用常量
import Rdb from '../Rdb'; // 导入数据库操作模块

// 定义 BooksTable 类
export default class BooksTable {
  public static booksTable: BooksTable = null; // 静态属性,用于保存 BooksTable 类的单例实例
  private booksRdb: Rdb; // 用于存储与数据库相关的操作实例
  private tableName: string = CommonConstants.BOOKS_TABLE.tableName; // 存储书籍表的名称
  private sqlCreateTable: string = CommonConstants.BOOKS_TABLE.sqlCreate; // 存储创建表的 SQL 语句

  ... 

  // 查询数据的方法,接受书籍 ID、回调函数和是否查询全部数据的标志作为参数
  query(id: number, callback: Function, isAll: boolean = true) {
    // 创建 RdbPredicates 实例,指定查询条件为表名
    let predicates = new relationalStore.RdbPredicates(this.tableName);

    // 如果不需要查询全部数据,则添加条件:id = 参数 id
    if (!isAll) {
      predicates.equalTo('id', id);
    }

    // 调用 Rdb 实例的 query 方法进行查询,并处理查询结果
    this.booksRdb.query(predicates, (resultSet: relationalStore.ResultSet) => {
      // 获取结果集的行数
      let count: number = resultSet.rowCount;

      // 如果结果集为空或者行数类型不是数字,输出日志并执行回调函数返回空数组
      if (count === 0 || typeof count === 'string') {
        console.log('Query no results!');
        callback([]);
      } else {
        // 移动结果集指针到第一行
        resultSet.goToFirstRow();

        // 创建空数组用于存储查询结果
        const result: Book[] = [];

        // 遍历结果集,获取每一行的数据并存储到 result 数组中
        for (let i = 0; i < count; i++) {
          let tmp: Book = {
            id: 0, title: '', author: '', publicationYear: '', type: ''
          };

          // 根据列名获取对应的值并赋给临时对象 tmp
          tmp.id = resultSet.getDouble(resultSet.getColumnIndex('id'));
          tmp.title = resultSet.getString(resultSet.getColumnIndex('title'));
          tmp.author = resultSet.getString(resultSet.getColumnIndex('author'));
          tmp.publicationYear = resultSet.getString(resultSet.getColumnIndex('publicationYear'));
          tmp.type = resultSet.getString(resultSet.getColumnIndex('type'));

          // 将临时对象 tmp 存入结果数组 result 中
          result[i] = tmp;

          // 移动结果集指针到下一行
          resultSet.goToNextRow();
        }

        // 执行回调函数,将查询结果数组传递给调用者
        callback(result);
      }
    });
  }
}

  1. query 方法的作用是从数据库中查询书籍信息。它接受三个参数:
    • id:要查询的书籍 ID。如果 isAll 参数为 false,则只查询指定 ID 的书籍。
    • callback:查询完成后执行的回调函数,用于处理查询结果。
    • isAll:一个可选参数,用于指示是否查询所有数据,默认为 true。如果为 false,则只查询指定 ID 的书籍。

在方法内部,首先创建了一个 RdbPredicates 对象 predicates,用于指定查询条件。如果 isAllfalse,则添加一个条件,要求查询结果中的书籍 ID 等于提供的 id 参数。

然后,调用 this.booksRdb.query 方法执行查询操作。查询结果通过回调函数返回,并以 resultSet 参数的形式传递给回调函数。

在回调函数内部,首先检查结果集的行数,以确定查询是否返回了结果。如果结果为空或行数不正确,则通过回调函数返回一个空数组。

如果有结果,则遍历结果集,逐行提取书籍的信息。对于每一行数据,创建一个临时的 Book 对象,并从结果集中提取相应的列值赋给临时对象。然后将临时对象添加到结果数组中。

最后,通过回调函数返回结果数组,完成了查询操作。

7. 二次封装query方法:

// 导入相关依赖
import relationalStore from '@ohos.data.relationalStore'; // 导入关系型存储模块
import Book from '../../../model/Book'; // 导入书籍模型
import CommonConstants from '../../constants/CommonConstants'; // 导入通用常量
import Rdb from '../Rdb'; // 导入数据库操作模块

// 定义 BooksTable 类
export default class BooksTable {
  public static booksTable: BooksTable = null; // 静态属性,用于保存 BooksTable 类的单例实例
  private booksRdb: Rdb; // 用于存储与数据库相关的操作实例
  private tableName: string = CommonConstants.BOOKS_TABLE.tableName; // 存储书籍表的名称
  private sqlCreateTable: string = CommonConstants.BOOKS_TABLE.sqlCreate; // 存储创建表的 SQL 语句

  ... 

  
  queryById(id: number, callback: Function) {
    this.query(id, callback, false);
  }

  queryAll(callback: Function) {
    this.query(null, callback, true)
  }
}

在这段代码中,我们对 query 方法进行了二次封装,创建了两个新的方法:queryByIdqueryAll。这些方法允许更简单地执行常见的查询操作,而无需手动指定参数。

  • queryById 方法接受一个书籍 ID 和一个回调函数作为参数,用于查询指定 ID 的书籍信息。它内部调用了原始的 query 方法,并将 isAll 参数设置为 false,以确保只查询特定 ID 的书籍。
  • queryAll 方法接受一个回调函数作为参数,用于查询所有书籍的信息。它内部也调用了原始的 query 方法,但将 isAll 参数设置为 true,以查询所有书籍。

  • 21
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值