基于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:角色选择后显示"未命名"

问题描述:
选择角色并提交后,跳转到首页,左上角显示"主厨:未命名"或"食客:未命名",但数据库中已有数据。

问题分析:

  1. 数据库表结构与代码模型不匹配
  2. 原表缺少 space_name 字段
  3. 数据插入失败但未报错
    在这里插入图片描述

解决方案:

步骤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 数据库设计原则

  1. 表结构清晰:每个表职责单一,字段命名规范
  2. 外键关联:使用外键保证数据完整性
  3. 冗余存储:适当冗余提高查询效率(如订单表存储菜谱名称)
  4. 时间戳:使用时间戳便于排序和展示

4.2 工具类设计原则

  1. 单一职责:每个工具类专注一个功能
  2. 静态方法:工具类使用静态方法,无需实例化
  3. 异常处理:统一异常处理,记录日志
  4. 返回值规范:失败返回默认值,避免空指针

4.3 调试技巧

  1. 日志输出:关键操作添加详细日志
  2. 错误捕获:使用 try-catch 捕获异常
  3. 数据验证:操作前验证数据有效性
  4. 数据库检查:使用 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 开发有所帮助!

下一篇文章将讲解数据持久化与问题解决。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐