前言:
在这里,我们将着手开发一个图书管理系统,旨在帮助读者熟练运用关系型数据库(RelationalStore)。图书管理系统是一个常见的应用场景,涉及查看图书、修改图书、删除图书和添加图书等功能。通过这个项目,我们将学习如何利用常见的 UI 组件,并重点掌握关系型数据库(RelationalStore)的使用。
案例介绍:
图书管理系统是一个常见的应用,通过这个案例,我们将学习如何设计和开发一个完整的应用程序。系统的主要功能包括:
- 查看图书: 用户可以浏览系统中存储的图书信息,包括书名、作者、出版年份等内容。
- 修改图书: 用户可以对图书信息进行修改,例如修改书名、作者、出版年份等。
- 删除图书: 用户可以从系统中删除特定的图书记录。
- 添加图书: 用户可以向系统中添加新的图书信息。
通过实现这些功能,我们将学习如何设计用户界面、处理用户输入、管理数据以及与关系型数据库(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%')
}
}
代码总结:
- 导入必要的模块和类,如路由和数据库表操作。
- 定义一个名为Index的组件,它是这个应用的入口点。
- 在组件的aboutToAppear生命周期中,尝试创建BooksTable实例,并根据创建结果输出相应的日志信息。
- 在组件的build方法中,定义了用户界面的布局,包括一个标题文本和四个按钮,分别用于查看、添加、删除和修改书籍。
- 每个按钮都绑定了一个点击事件处理函数,用于导航到不同的页面,并传递必要的参数。
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")
}
}
代码总结:
- 包含了表单输入和按钮操作,允许用户输入书籍的标题、作者、出版年份和类型,并将这些信息添加到数据库中。
- 通过声明响应式状态变量,该页面能够实时响应用户的输入。当用户完成输入并点击添加按钮时,会创建一个新的Book对象,并通过BooksTable类的insertData方法将其保存到数据库。
- 如果操作成功,会输出"添加成功"的日志信息;如果失败,则输出"添加失败"。此外,还有一个取消按钮允许用户返回上一页。
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")
}
}
代码总结:
- 展示了如何使用路由参数来获取并显示要删除的书籍信息,并提供了取消和删除操作的按钮。
- 当用户点击删除按钮时,会调用BooksTable类的deleteById方法来从数据库中删除相应的书籍记录。
- 如果删除成功,会输出"删除成功"的日志信息;如果失败,则输出"删除失败"。
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")
}
}
代码总结:
- 展示了如何使用路由参数来获取并显示要修改的书籍信息,并提供了输入框让用户更新信息。
- 当用户点击修改按钮时,会创建一个新的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")
}
}
代码总结:
- 主要负责显示数据库中所有书籍的信息,并根据用户的不同操作状态(查看、修改、删除)提供相应的功能。
- 代码中使用了HarmonyOS的UI组件和布局来构建一个响应式的用户界面,使得用户可以方便地浏览和管理书籍。
- 通过路由参数传递,该页面能够处理不同的用户交互,如跳转到修改或删除书籍的页面。
弹窗设计
在展示图书的代码上加入以下代码
// 导入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:
- 在目录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); // 通过回调函数返回执行情况
});
});
}
// 其他方法(如创建表、插入数据、更新删除数据等操作)省略
}
- rdbStore 属性:
-
rdbStore
是relationalStore.RdbStore
类型的私有属性,用于保存数据库存储对象实例。
- 构造函数 (
constructor
):
-
- 接收两个参数:
sqlCreateTable
和callback
。 sqlCreateTable
是用来创建表结构的 SQL 语句。callback
是在创建Rdb
实例完成后调用的回调函数,用于通知调用者实例创建的情况。
- 接收两个参数:
- createTable 方法:
-
- 该方法用于创建表结构,接收一个 SQL 创建表的语句 (
sqlCreateTable
) 和一个回调函数 (callback
),回调函数用来返回该方法的执行情况 ( flag )。 - 该方法在上述代码中并不提供,只需简单理解即可,后面会重点介绍。
- 该方法用于创建表结构,接收一个 SQL 创建表的语句 (
- 错误处理:
-
- 在获取数据库存储对象时,如果出现错误,会通过回调函数返回
false
,并记录错误日志。
- 在获取数据库存储对象时,如果出现错误,会通过回调函数返回
- 异步操作:
-
- 整个过程是异步的,因为涉及到数据库操作,可能需要一些时间来完成。因此,采用回调函数的方式来处理结果,确保在完成操作后通知调用者。
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);
});
}
}
- 方法签名:
createTable(sqlCreateTable: string, callback: Function)
- 参数:
-
sqlCreateTable
:用于创建表结构的 SQL 语句。callback
:在创建表结构完成后调用的回调函数,用于通知调用者操作的结果。
- 方法功能:
-
- 当数据库已准备就绪后,通过执行传入的 SQL 语句来创建表结构。
- 在创建过程中,会通过回调函数通知调用者操作的结果。
- 方法逻辑:
-
- 首先检查传入的 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); // 执行回调函数
});
}
}
}
- 方法签名:
insertData(data: relationalStore.ValuesBucket, tableName: string, callback: Function)
- 参数:
-
data
:relationalStore.ValuesBucket
类型的数据,用于存储要插入的数据的键值对。tableName
:表名,字符串类型,指定要插入数据的目标表。callback
:在插入数据操作完成后调用的回调函数,用于反馈该方法的执行情况。
- 方法功能:
-
- 当数据库已准备就绪后,通过执行
insertData
方法来向指定的数据表中插入数据。 - 在插入过程中,会通过回调函数通知调用者操作的结果。
- 当数据库已准备就绪后,通过执行
- 方法逻辑:
-
- 首先检查是否存在数据库存储对象 (
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); // 执行回调函数
});
}
}
}
- 方法签名:
deleteData(predicates: relationalStore.RdbPredicates, callback: Function)
- 参数:
-
predicates
:relationalStore.RdbPredicates
类型的对象,用于指定需要删除的条件。这个条件由上一层调用该方法的提供。callback
:在删除数据操作完成后调用的回调函数,用于反馈执行结果的成功与否。
- 方法功能:
-
- 当数据库已准备就绪后,通过执行
deleteData
方法来从指定的数据表中删除数据。 - 在删除过程中,会通过回调函数通知调用者操作的结果。
- 当数据库已准备就绪后,通过执行
- 方法逻辑:
-
- 首先检查是否存在数据库存储对象 (
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); // 执行回调函数
});
}
}
}
- 方法签名:
updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function)
- 参数:
-
predicates
:relationalStore.RdbPredicates
类型的对象,指定需要做出更新的条件。data
:relationalStore.ValuesBucket
类型的对象,需要更新的数据。callback
:在更新数据操作完成后调用的回调函数,用于反馈执行结果的成功与否。
- 方法功能:
-
- 当数据库已准备就绪后,通过执行
updateData
方法来更新指定的数据表中的数据。 - 在更新过程中,会通过回调函数通知调用者操作的结果。
- 当数据库已准备就绪后,通过执行
- 方法逻辑:
-
- 首先检查是否存在数据库存储对象 (
rdbStore
),如果不存在则无法执行更新数据操作,直接返回。 - 使用数据库存储对象的
update
方法,根据指定的更新条件predicates
和更新的数据data
来进行数据更新。 - 在更新操作完成后,通过回调函数通知调用者操作的结果。如果操作成功,则将受影响的行数传递给回调函数;如果操作失败,则返回
false
。
- 首先检查是否存在数据库存储对象 (
RdbStore.update接口说明:
update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void
功能:根据RdbPredicates的指定实例对象更新数据库中的数据,使用callback异步回调。
参数:
参数名 | 类型 | 必填 | 说明 |
values | 是 | values指示数据库中要更新的数据行。键值对与数据库表的列名相关联。 | |
predicates | 是 | 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)
})
}
}
}
- 方法签名:
query(predicates: relationalStore.RdbPredicates, callback: Function)
- 参数:
-
predicates
:relationalStore.RdbPredicates
类型的对象,用于指定查询条件。callback
:在查询数据操作完成后调用的回调函数,用于返回查询的结果。
- 方法功能:
-
- 当数据库已准备就绪后,通过执行
query
方法来从指定的数据表中查询数据。 - 在查询过程中,会通过回调函数通知调用者查询的结果。
- 当数据库已准备就绪后,通过执行
- 方法逻辑:
-
- 首先检查是否存在数据库存储对象 (
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
表相关的操作。通过这样的封装,我们能够更好地适应复杂的业务功能,提高代码的可读性和灵活性。
作用介绍:
- 针对
books
表的操作:BooksTable.ets
主要用于对books
表进行各种操作,如插入数据、更新数据、删除数据、查询数据等。 - 封装具体的业务逻辑:
BooksTable.ets
将具体的业务逻辑封装在对应的方法中,使代码更加清晰和易于理解。 - 提高代码复用性: 将与
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);
});
}
}
属性说明:
- 静态属性
booksTable
: 这是一个静态属性,用于保存BooksTable
类的单例实例。单例模式确保在整个应用程序中只有一个BooksTable
实例,这样可以节省资源并且方便管理。 - 属性
booksRdb
: 这是一个私有属性,用于存储与数据库相关的操作实例。我们将通过Rdb
类来执行与数据库表books
相关的操作,例如创建表、插入数据等。 - 属性
tableName
和sqlCreateTable
: 这两个属性分别用于存储书籍表的名称和创建表的 SQL 语句。表名和 SQL 语句都是从CommonConstants
常量模块中获取的,这样可以保持代码的清晰性和可维护性。
构造函数:
- 构造函数接收一个回调函数作为参数。在构造函数中,我们创建了一个
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);
insertData
方法: 这个方法用于将书籍对象插入到数据库表中。它接收两个参数:book
和callback
。book
参数: 这是一个Book
类型的对象,表示需要插入的书籍信息。callback
参数: 这是一个回调函数,用于在插入数据操作完成后通知调用者操作的结果。- 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);
}
}
updateData
方法: 这个方法用于更新书籍表中的数据。它接收两个参数:book
和callback
。
-
book
参数: 这是一个Book
类型的对象,表示需要更新的书籍信息。callback
参数: 这是一个回调函数,用于在更新数据操作完成后通知调用者操作的结果。
- 转换类型: 在方法中,我们通过调用
generateBucket
方法将Book
类型的对象转换为relationalStore.ValuesBucket
类型的对象。这是因为数据库操作需要使用特定的数据类型来更新数据。 - 创建查询条件: 我们使用
relationalStore.RdbPredicates
类创建了一个查询条件,用于指定需要更新的数据行。这里我们以书籍的 id 为条件,表示只更新指定 id 的书籍信息。 - 更新数据操作: 最后,我们调用
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);
}
- deleteById方法: 这个方法用于删除书籍表中的数据。它接收两个参数:
book
和callback
。
-
id: number
:这是一个整数类型的参数,代表要删除的图书的ID。通过该参数,方法可以确定要删除哪一本图书。callback: Function
:这是一个回调函数,用于处理删除操作的结果。在删除操作完成后,将通过该回调函数返回结果。
- 创建谓词对象:方法内部首先创建了一个谓词对象
predicates
,用于指定删除操作的条件。在这里,通过指定图书表的名称和传入的图书 ID,构建了一个条件,即删除满足特定 ID 的图书。 - 调用数据库操作:接下来,调用了
booksRdb
对象的deleteData
方法执行删除操作。该方法接受谓词对象和回调函数作为参数。删除操作会根据谓词对象指定的条件,在数据库中删除相应的图书记录。 - 回调处理结果:删除操作完成后,通过回调函数
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);
}
});
}
}
query
方法的作用是从数据库中查询书籍信息。它接受三个参数:
-
id
:要查询的书籍 ID。如果isAll
参数为false
,则只查询指定 ID 的书籍。callback
:查询完成后执行的回调函数,用于处理查询结果。isAll
:一个可选参数,用于指示是否查询所有数据,默认为true
。如果为false
,则只查询指定 ID 的书籍。
在方法内部,首先创建了一个 RdbPredicates
对象 predicates
,用于指定查询条件。如果 isAll
为 false
,则添加一个条件,要求查询结果中的书籍 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
方法进行了二次封装,创建了两个新的方法:queryById
和 queryAll
。这些方法允许更简单地执行常见的查询操作,而无需手动指定参数。
queryById
方法接受一个书籍 ID 和一个回调函数作为参数,用于查询指定 ID 的书籍信息。它内部调用了原始的query
方法,并将isAll
参数设置为false
,以确保只查询特定 ID 的书籍。queryAll
方法接受一个回调函数作为参数,用于查询所有书籍的信息。它内部也调用了原始的query
方法,但将isAll
参数设置为true
,以查询所有书籍。