🌟 HarmonyOS应用开发实战 | ArkTS中SQLite数据库初始化详解

💫 坚果派·红目香薰 倾情分享
🎯 用心打造每一个技术细节,为开发者创造更多价值
📱 让HarmonyOS开发变得更简单、更有趣


✨ 写在前面的话

嗨,亲爱的技术朋友们!👋

我是来自坚果派的红目香薰,一个热爱技术、专注HarmonyOS开发的程序媛。在这个数字化飞速发展的时代,HarmonyOS作为华为自主研发的操作系统,正在改变着我们的数字生活体验。

🌈 为什么要写这个系列?

  • 💡 让复杂的技术变得简单易懂
  • 🚀 帮助更多开发者快速上手HarmonyOS
  • 💝 分享实战经验,避免踩坑
  • 🌟 用代码创造美好,用技术传递温暖

每一个Demo都是我精心设计和反复测试的结果,希望能够为你的HarmonyOS开发之路点亮一盏明灯。✨


📋 Demo功能说明

img

🎯 核心功能

本Demo主要展示如何在HarmonyOS应用中初始化和使用SQLite数据库,实现一个简单的待办事项管理功能。通过这个Demo,你将学习到如何创建数据库、定义表结构、执行基本的CRUD操作以及处理数据库事务。

✨ 特色亮点

  • 🔄 使用ArkTS的关系型存储API进行数据库操作
  • 📊 实现数据的增删改查全流程
  • 🛡️ 采用事务机制确保数据一致性
  • 🧩 模块化设计,便于代码复用和维护

🎨 界面展示

一个简洁的待办事项列表应用,支持添加、标记完成和删除待办事项。

📱 适用场景

  • 需要本地持久化存储的应用
  • 待办事项、笔记、个人信息管理类应用
  • 需要离线数据访问的应用场景

🔧 核心代码说明

📁 项目结构

TodoApp/
├── src/
│   ├── main/
│   │   ├── ets/
│   │   │   ├── pages/
│   │   │   │   └── Index.ets  // 主页面
│   │   │   └── model/
│   │   │       └── TodoItem.ets  // 数据模型
│   │   └── resources/
│   └── ...

注:所有代码均放在src/main/ets/pages/Index.ets文件中,图片放在src/main/resources/base/media目录下

🎯 关键技术点

1. SQLite数据库初始化

在本Demo中,我们将学习如何初始化SQLite数据库,创建表结构,并设置必要的配置。

2. 数据模型定义

定义待办事项的数据模型,包括ID、标题、描述、完成状态等字段。

3. 数据库操作封装

封装常用的数据库操作,如插入、查询、更新和删除,提高代码复用性。

4. 界面与数据绑定

将数据库操作与界面元素绑定,实现数据的动态展示和交互。

💡 技术要点解析

SQLite数据库初始化流程

  1. 导入必要的模块
  2. 定义数据库配置(名称、版本等)
  3. 创建数据库连接
  4. 定义表结构并创建表
  5. 处理数据库版本升级

📝 完整Demo代码

🏠 主页面代码

// src/main/ets/pages/Index.ets

import relationalStore from '@ohos.data.relationalStore';
import promptAction from '@ohos.promptAction';
import { BusinessError } from '@ohos.base';

// 待办事项数据接口
interface TodoItem {
  id?: number;
  title: string;
  description: string;
  isCompleted: boolean;
  createTime: number;
}

// 示例数据项接口
interface SampleItem {
  title: string;
  description: string;
  isCompleted: boolean;
}

@Entry
@Component
struct Index {
  @State todoItems: TodoItem[] = [];
  @State newTodoText: string = '';
  private storeConfig: relationalStore.StoreConfig = {
    name: 'TodoDatabase.db',
    securityLevel: relationalStore.SecurityLevel.S1
  };
  private rdbStore: relationalStore.RdbStore | null = null;
  private tableName: string = 'TodoTable';

  aboutToAppear() {
    // 初始化数据库
    this.initDatabase();
  }

  async initDatabase() {
    try {
      // 获取上下文
      const context = getContext(this);

      // 获取RDB数据库实例
      this.rdbStore = await relationalStore.getRdbStore(context, this.storeConfig);

      // 创建表SQL语句
      const createTableSql = `
        CREATE TABLE IF NOT EXISTS ${this.tableName} (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          title TEXT NOT NULL,
          description TEXT,
          isCompleted INTEGER DEFAULT 0,
          createTime INTEGER
        )`;

      // 执行创建表操作
      await this.rdbStore.executeSql(createTableSql);
      console.info('Database and table created successfully');

      // 加载待办事项
      this.loadTodoItems();

      // 如果没有数据,添加一些示例数据
      if (this.todoItems.length === 0) {
        await this.addSampleData();
      }
    } catch (error) {
      const err = error as BusinessError;
      console.error(`Failed to initialize database: ${err.message}`);
      promptAction.showToast({
        message: '初始化数据库失败',
        duration: 2000
      });
    }
  }

  async addSampleData() {
    if (!this.rdbStore) return;

    // 定义示例数据,使用SampleItem接口
    const sampleItems: SampleItem[] = [
      { title: '完成项目报告', description: '周五之前提交给经理', isCompleted: false },
      { title: '购买生日礼物', description: '妈妈的生日在下周三', isCompleted: true },
      { title: '预约医生', description: '年度体检', isCompleted: false },
      { title: '学习HarmonyOS开发', description: '完成ArkTS基础课程', isCompleted: false }
    ];

    try {
      for (const item of sampleItems) {
        const valuesBucket: relationalStore.ValuesBucket = {
          'title': item.title,
          'description': item.description,
          'isCompleted': item.isCompleted ? 1 : 0,
          'createTime': new Date().getTime() - Math.floor(Math.random() * 86400000) // 随机时间
        };

        await this.rdbStore.insert(this.tableName, valuesBucket);
      }

      console.info('Sample data added successfully');
      this.loadTodoItems();
    } catch (error) {
      const err = error as BusinessError;
      console.error(`Failed to add sample data: ${err.message}`);
    }
  }

  async loadTodoItems() {
    if (!this.rdbStore) return;

    try {
      // 定义查询条件
      const predicates = new relationalStore.RdbPredicates(this.tableName);
      predicates.orderByDesc('createTime');

      // 执行查询
      const resultSet = await this.rdbStore.query(predicates, ['id', 'title', 'description', 'isCompleted', 'createTime']);

      // 清空当前列表
      this.todoItems = [];

      // 遍历结果集
      while (resultSet.goToNextRow()) {
        const id = resultSet.getLong(resultSet.getColumnIndex('id'));
        const title = resultSet.getString(resultSet.getColumnIndex('title'));
        const description = resultSet.getString(resultSet.getColumnIndex('description'));
        const isCompleted = resultSet.getLong(resultSet.getColumnIndex('isCompleted')) === 1;
        const createTime = resultSet.getLong(resultSet.getColumnIndex('createTime'));

        // 添加到列表
        this.todoItems.push({
          id: id,
          title: title,
          description: description,
          isCompleted: isCompleted,
          createTime: createTime
        });
      }

      // 关闭结果集
      resultSet.close();
    } catch (error) {
      const err = error as BusinessError;
      console.error(`Failed to load todo items: ${err.message}`);
      promptAction.showToast({
        message: '加载待办事项失败',
        duration: 2000
      });
    }
  }

  async addTodoItem() {
    if (!this.rdbStore) return;

    if (!this.newTodoText.trim()) {
      promptAction.showToast({
        message: '请输入待办事项内容',
        duration: 2000
      });
      return;
    }

    try {
      // 创建待办事项数据
      const valuesBucket: relationalStore.ValuesBucket = {
        'title': this.newTodoText,
        'description': '',
        'isCompleted': 0,
        'createTime': new Date().getTime()
      };

      // 插入数据
      const rowId = await this.rdbStore.insert(this.tableName, valuesBucket);
      console.info(`Inserted row ID: ${rowId}`);

      // 清空输入框
      this.newTodoText = '';

      // 重新加载数据
      this.loadTodoItems();

      promptAction.showToast({
        message: '添加成功',
        duration: 2000
      });
    } catch (error) {
      const err = error as BusinessError;
      console.error(`Failed to add todo item: ${err.message}`);
      promptAction.showToast({
        message: '添加待办事项失败',
        duration: 2000
      });
    }
  }

  async toggleTodoItem(id: number, isCompleted: boolean) {
    if (!this.rdbStore) return;

    try {
      // 创建更新数据
      const valuesBucket: relationalStore.ValuesBucket = {
        'isCompleted': isCompleted ? 0 : 1
      };

      // 定义更新条件
      const predicates = new relationalStore.RdbPredicates(this.tableName);
      predicates.equalTo('id', id);

      // 执行更新
      const rows = await this.rdbStore.update(valuesBucket, predicates);
      console.info(`Updated rows: ${rows}`);

      // 重新加载数据
      this.loadTodoItems();
    } catch (error) {
      const err = error as BusinessError;
      console.error(`Failed to update todo item: ${err.message}`);
      promptAction.showToast({
        message: '更新待办事项失败',
        duration: 2000
      });
    }
  }

  async deleteTodoItem(id: number) {
    if (!this.rdbStore) return;

    try {
      // 定义删除条件
      const predicates = new relationalStore.RdbPredicates(this.tableName);
      predicates.equalTo('id', id);

      // 执行删除
      const rows = await this.rdbStore.delete(predicates);
      console.info(`Deleted rows: ${rows}`);

      // 重新加载数据
      this.loadTodoItems();

      promptAction.showToast({
        message: '删除成功',
        duration: 2000
      });
    } catch (error) {
      const err = error as BusinessError;
      console.error(`Failed to delete todo item: ${err.message}`);
      promptAction.showToast({
        message: '删除待办事项失败',
        duration: 2000
      });
    }
  }

  build() {
    Column() {
      // 标题
      Text('待办事项')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 20 })

      // 输入框和添加按钮
      Row() {
        TextInput({ placeholder: '请输入待办事项...', text: this.newTodoText })
          .onChange((value) => {
            this.newTodoText = value;
          })
          .layoutWeight(1)
          .height(50)
          .margin({ right: 10 })
          .backgroundColor('#F0F0F0')
          .borderRadius(25)
          .padding({ left: 20, right: 20 })

        Button('添加')
          .onClick(() => {
            this.addTodoItem();
          })
          .height(50)
          .width(80)
          .borderRadius(25)
          .backgroundColor('#0A59F7')
      }
      .width('90%')
      .margin({ bottom: 20 })

      // 待办事项列表
      List({ space: 8, initialIndex: 0 }) {
        ForEach(this.todoItems, (item: TodoItem) => {
          ListItem() {
            Column() {
              Row() {
                Checkbox()
                  .select(item.isCompleted)
                  .onChange(() => {
                    this.toggleTodoItem(item.id!, item.isCompleted);
                  })
                  .margin({ right: 10 })

                Text(item.title)
                  .fontSize(16)
                  .decoration({ type: item.isCompleted ? TextDecorationType.LineThrough : TextDecorationType.None })
                  .fontColor(item.isCompleted ? '#999999' : '#333333')
                  .layoutWeight(1)

                Button({ type: ButtonType.Circle, stateEffect: true }) {
                  Row() {
                    Text('删')
                      .fontSize(14)
                      .fontWeight(FontWeight.Normal)
                      .fontColor('#FFFFFF')
                  }
                  .width('100%')
                  .height('100%')
                  .justifyContent(FlexAlign.Center)
                }
                .width(36)
                .height(36)
                .backgroundColor('#FF4757')
                .onClick(() => {
                  this.deleteTodoItem(item.id!);
                })
              }
              .width('100%')
              .alignItems(VerticalAlign.Center)

              if (item.description) {
                Text(item.description)
                  .fontSize(14)
                  .fontColor('#666666')
                  .margin({ top: 5, left: 34 })
                  .opacity(item.isCompleted ? 0.6 : 1.0)
              }
            }
            .width('100%')
            .padding(15)
            .borderRadius(12)
            .backgroundColor(Color.White)
            .shadow({
              radius: 6,
              color: '#20000000',
              offsetX: 0,
              offsetY: 2
            })
          }
        })
      }
      .width('90%')
      .layoutWeight(1)

      // 底部状态栏
      Row() {
        Text(`共 ${this.todoItems.length} 项`)
          .fontSize(14)
          .fontColor('#666666')

        Text(`已完成 ${this.todoItems.filter(item => item.isCompleted).length} 项`)
          .fontSize(14)
          .fontColor('#27AE60')
      }
      .width('90%')
      .justifyContent(FlexAlign.SpaceBetween)
      .padding({ top: 10, bottom: 10 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F3F5')
    .padding({ top: 10, bottom: 10 })
  }
}

🚀 运行效果

📱 界面展示

运行后,你将看到一个简洁的待办事项应用界面:

  • 顶部是应用标题"待办事项"
  • 中间是输入框和添加按钮,用于创建新的待办事项
  • 下方是待办事项列表,每个项目包含复选框和删除按钮
  • 点击复选框可以标记待办事项为已完成/未完成
  • 点击删除按钮可以移除待办事项

✅ 功能验证

  1. 添加待办事项:在输入框中输入内容,点击"添加"按钮
  2. 标记完成:点击待办事项前的复选框
  3. 删除待办事项:点击待办事项右侧的删除按钮
  4. 数据持久化:关闭应用后重新打开,数据仍然存在

💡 开发小贴士

🎯 最佳实践

  • 💫 数据库操作异步化:所有数据库操作都应该是异步的,避免阻塞主线程
  • 🎨 错误处理:对所有数据库操作进行适当的错误处理,提高应用稳定性
  • 事务使用:对于多步操作,使用事务确保数据一致性
  • 🔧 关闭资源:及时关闭ResultSet等资源,避免内存泄漏

🚨 常见问题

  1. 数据库初始化失败:检查权限设置和存储空间

    // 在module.json5中添加数据权限
    "requestPermissions": [
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC"
      }
    ]
    
  2. 查询结果为空:检查表名和字段名是否正确,SQL语句是否有语法错误

    // 使用日志输出SQL语句进行调试
    console.info(`Executing SQL: ${sql}`);
    
  3. 版本升级问题:处理数据库版本升级时的表结构变更

    // 在StoreConfig中指定版本号
    const storeConfig = {
      name: 'TodoDatabase.db',
      securityLevel: relationalStore.SecurityLevel.S1,
      version: 2 // 指定版本号
    };
    

🎉 总结与展望

通过这个Demo,我们学习了:

  • ✨ 如何在ArkTS中初始化SQLite数据库
  • 🎯 如何执行基本的CRUD操作
  • 💡 如何将数据库操作与UI交互结合

SQLite数据库为HarmonyOS应用提供了强大的本地数据存储能力,适合各种需要持久化存储的应用场景。在实际开发中,我们可以进一步优化数据库结构,添加更多功能,如数据同步、搜索、分类等,打造更加完善的应用。


🔗 相关资源


🌟 如果这篇文章对你有帮助,请点赞支持!🌟

让我们一起在HarmonyOS的世界里创造更多可能!


© 2024 坚果派·红目香薰 | 用心分享,用技术创造价值

Logo

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

更多推荐