基于HarmonyOS的情侣美食管理应用开发实战(四)- 数据库优化与问题解决
·
基于HarmonyOS的情侣美食管理应用开发实战(四)- 数据库优化与问题解决
📖 系列文章目录
- (一)项目设计与角色管理
- (二)菜谱管理与订单系统
- (三)美食相册与设置功能
- (四)数据库优化与问题解决(本文)
一、数据库工具类封装
1.1 RdbUtil 工具类
封装数据库操作,简化代码:
export class RdbUtil {
private static rdbStore: relationalStore.RdbStore | null = null
// 初始化数据库
static async init(context: Context): Promise<void> {
const config: relationalStore.StoreConfig = {
name: 'MenuRecipe.db',
securityLevel: relationalStore.SecurityLevel.S1
}
// 删除旧数据库强制重建(开发阶段)
try {
await relationalStore.deleteRdbStore(context, config.name)
LogUtil.info('RdbUtil', '已删除旧数据库')
} catch (err) {
LogUtil.info('RdbUtil', '数据库不存在')
}
RdbUtil.rdbStore = await relationalStore.getRdbStore(context, config)
await RdbUtil.createTables()
}
// 插入数据
static async insert(
tableName: string,
values: relationalStore.ValuesBucket
): Promise<number> {
const rowId = await RdbUtil.rdbStore?.insert(tableName, values)
return rowId || 0
}
// 更新数据
static async update(
tableName: string,
values: relationalStore.ValuesBucket,
predicates: relationalStore.RdbPredicates
): Promise<number> {
const rowCount = await RdbUtil.rdbStore?.update(values, predicates)
return rowCount || 0
}
// 查询数据
static async query(
predicates: relationalStore.RdbPredicates
): Promise<relationalStore.ResultSet> {
return await RdbUtil.rdbStore?.query(predicates)
}
// 删除数据
static async delete(
predicates: relationalStore.RdbPredicates
): Promise<number> {
const rowCount = await RdbUtil.rdbStore?.delete(predicates)
return rowCount || 0
}
}
1.2 PreferencesUtil 工具类
封装偏好设置操作:
export class PreferencesUtil {
private static preferences: preferences.Preferences | null = null
// 初始化
static async init(context: Context) {
PreferencesUtil.preferences =
await preferences.getPreferences(context, 'my_preferences')
}
// 保存数据
static async put(key: string, value: string | number | boolean) {
await PreferencesUtil.preferences?.put(key, value)
await PreferencesUtil.preferences?.flush()
}
// 读取数据
static async get(key: string, defaultValue: string | number | boolean) {
return await PreferencesUtil.preferences?.get(key, defaultValue)
}
// 清除数据
static async clear() {
await PreferencesUtil.preferences?.clear()
await PreferencesUtil.preferences?.flush()
}
}
二、遇到的问题与解决方案
2.1 问题1:角色选择后显示"未命名"
问题描述:
选择角色并提交后,跳转到首页,左上角显示"主厨:未命名"或"食客:未命名",但数据库中已有数据。
问题分析:
- 数据库表结构与代码模型不匹配
- 原表缺少
space_name字段 - 数据插入失败但未报错

解决方案:
步骤1:修正数据库表结构
-- 修改前
CREATE TABLE IF NOT EXISTS couple_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chef_name TEXT NOT NULL,
diner_name TEXT NOT NULL,
chef_avatar TEXT,
diner_avatar TEXT,
create_time INTEGER NOT NULL,
update_time INTEGER NOT NULL
)
-- 修改后
CREATE TABLE IF NOT EXISTS couple_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
space_name TEXT NOT NULL, -- ✅ 新增字段
chef_name TEXT NOT NULL,
diner_name TEXT NOT NULL,
create_time INTEGER NOT NULL
)
步骤2:添加强制重建数据库逻辑
static async init(context: Context): Promise<void> {
// 删除旧数据库强制重建(开发阶段)
try {
await relationalStore.deleteRdbStore(context, CommonConstant.DB_NAME)
LogUtil.info(RdbUtil.TAG, '已删除旧数据库,将重新创建')
} catch (err) {
LogUtil.info(RdbUtil.TAG, '数据库不存在或删除失败,继续创建新数据库')
}
// 创建新数据库
RdbUtil.rdbStore = await relationalStore.getRdbStore(context, config)
await RdbUtil.createTables()
}
步骤3:添加详细日志
// RoleSelectPage.ets
async selectRole(role: string) {
LogUtil.info('RoleSelectPage', `选择角色: ${role}`)
LogUtil.info('RoleSelectPage', `空间: ${this.spaceName}, 主厨: ${this.chefName}, 食客: ${this.dinerName}`)
// ... 保存逻辑
LogUtil.info('RoleSelectPage', '角色保存成功')
}
// ChefHomePage.ets
async loadCoupleConfig() {
const resultSet = await RdbUtil.query(predicates)
LogUtil.info('ChefHomePage', `查询到 ${resultSet.rowCount} 条配置记录`)
if (resultSet.goToFirstRow()) {
const chefName = resultSet.getString(resultSet.getColumnIndex('chef_name'))
LogUtil.info('ChefHomePage', `主厨昵称: ${chefName}`)
}
}
2.2 问题2:清除缓存后角色丢失
问题描述:
用户清除应用缓存后,preferences被清除,但数据库数据还在,导致角色丢失但配置仍存在。
解决方案:
在角色选择页加载已有配置,自动填充表单:
// RoleSelectPage.ets
async loadExistingConfig() {
const predicates = new relationalStore.RdbPredicates('couple_config')
const resultSet = await RdbUtil.query(predicates)
if (resultSet.goToFirstRow()) {
this.spaceName = resultSet.getString(resultSet.getColumnIndex('space_name'))
this.chefName = resultSet.getString(resultSet.getColumnIndex('chef_name'))
this.dinerName = resultSet.getString(resultSet.getColumnIndex('diner_name'))
LogUtil.info('RoleSelectPage', '已加载历史配置')
}
resultSet.close()
}

2.3 问题3:数据库操作异常处理
问题描述:
数据库操作失败时没有友好的错误提示,导致用户不知道发生了什么。
解决方案:
添加统一的异常处理和日志记录:
static async insert(
tableName: string,
values: relationalStore.ValuesBucket
): Promise<number> {
if (!RdbUtil.rdbStore) {
LogUtil.error(RdbUtil.TAG, '数据库未初始化')
return 0
}
try {
const rowId = await RdbUtil.rdbStore.insert(tableName, values)
LogUtil.info(RdbUtil.TAG, `插入数据成功,rowId: ${rowId}`)
return rowId
} catch (err) {
LogUtil.error(RdbUtil.TAG, `插入数据失败: ${err}`)
return 0
}
}
三、性能优化建议
3.1 数据库优化
3.1.1 添加索引
为频繁查询的字段添加索引:
-- 为菜谱类型添加索引
CREATE INDEX idx_recipe_type ON recipe(type)
-- 为订单状态添加索引
CREATE INDEX idx_order_status ON order_record(status)
-- 为订单时间添加索引
CREATE INDEX idx_order_time ON order_record(order_time)
3.1.2 批量操作
使用事务处理批量操作:
static async batchInsert(
tableName: string,
valuesList: relationalStore.ValuesBucket[]
): Promise<number> {
if (!RdbUtil.rdbStore) return 0
try {
await RdbUtil.rdbStore.beginTransaction()
let successCount = 0
for (const values of valuesList) {
const rowId = await RdbUtil.rdbStore.insert(tableName, values)
if (rowId > 0) successCount++
}
await RdbUtil.rdbStore.commit()
return successCount
} catch (err) {
await RdbUtil.rdbStore.rollBack()
LogUtil.error(RdbUtil.TAG, `批量插入失败: ${err}`)
return 0
}
}
3.2 查询优化
3.2.1 分页查询
async loadRecipes(page: number, pageSize: number = 20) {
const predicates = new relationalStore.RdbPredicates('recipe')
predicates.orderByDesc('create_time')
predicates.limitAs(pageSize)
predicates.offsetAs((page - 1) * pageSize)
return await RdbUtil.query(predicates)
}
3.2.2 条件查询
async searchRecipes(keyword: string) {
const predicates = new relationalStore.RdbPredicates('recipe')
predicates.like('name', `%${keyword}%`)
predicates.orderByDesc('create_time')
return await RdbUtil.query(predicates)
}
四、开发经验总结
4.1 数据库设计原则
- 表结构清晰:每个表职责单一,字段命名规范
- 外键关联:使用外键保证数据完整性
- 冗余存储:适当冗余提高查询效率(如订单表存储菜谱名称)
- 时间戳:使用时间戳便于排序和展示
4.2 工具类设计原则
- 单一职责:每个工具类专注一个功能
- 静态方法:工具类使用静态方法,无需实例化
- 异常处理:统一异常处理,记录日志
- 返回值规范:失败返回默认值,避免空指针
4.3 调试技巧
- 日志输出:关键操作添加详细日志
- 错误捕获:使用 try-catch 捕获异常
- 数据验证:操作前验证数据有效性
- 数据库检查:使用 DevEco Studio 的数据库查看工具
五、成果展示

六、完整代码结构
MenuRecipe/
├── entry/
│ └── src/main/
│ ├── ets/
│ │ ├── common/
│ │ │ ├── CommonConstant.ets
│ │ │ ├── LogUtil.ets
│ │ │ ├── RdbUtil.ets
│ │ │ └── PreferencesUtil.ets
│ │ ├── model/
│ │ │ ├── Recipe.ets
│ │ │ ├── Order.ets
│ │ │ ├── GalleryImage.ets
│ │ │ └── CoupleConfig.ets
│ │ └── pages/
│ │ ├── SplashPage.ets
│ │ ├── RoleSelectPage.ets
│ │ ├── ChefHomePage.ets
│ │ ├── DinerHomePage.ets
│ │ ├── RecipeManagePage.ets
│ │ ├── OrderManagePage.ets
│ │ ├── GalleryPage.ets
│ │ └── SettingsPage.ets
│ └── resources/
│ └── base/
│ ├── media/ # 图片资源
│ └── element/ # 字符串资源
└── docs/ # 文档目录
├── 情侣美食管理应用开发实战(一)-项目设计与角色管理.md
├── 情侣美食管理应用开发实战(二)-菜谱管理与订单系统.md
├── 情侣美食管理应用开发实战(三)-美食相册与设置功能.md
└── 情侣美食管理应用开发实战(四)-数据库优化与问题解决.md
系列文章持续更新中! 🎉
感谢阅读本系列文章,希望对你的 HarmonyOS 开发有所帮助!
下一篇文章将讲解数据持久化与问题解决。
更多推荐


所有评论(0)