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


✨ 写在前面的话

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

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

🌈 为什么要写这个系列?

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

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

今天我们来深入学习HarmonyOS中最重要的本地存储技术之一——SQLite数据库。从基础的增删改查到复杂的数据关系管理,让我们一起打造高效可靠的数据存储应用!


📋 Demo功能说明

img

🎯 核心功能

本Demo展示了HarmonyOS中SQLite数据库的全面使用方法,包括:

  • 🗄️ 数据库的创建、连接和版本管理
  • 📝 数据表的设计和结构优化
  • ➕ 数据的增加(INSERT)操作和批量插入
  • 🔍 数据的查询(SELECT)和条件筛选
  • ✏️ 数据的更新(UPDATE)和字段修改
  • 🗑️ 数据的删除(DELETE)和清理操作
  • 🔄 事务处理和数据一致性保证
  • 📊 复杂查询和数据统计分析
  • 🌟 实际应用场景的完整实现

✨ 特色亮点

  • 🎨 界面精美:现代化的数据管理界面设计
  • 🚀 性能优秀:高效的数据库操作和查询优化
  • 📱 用户友好:直观的数据展示和操作反馈
  • 💡 功能完整:涵盖数据库应用的各种场景
  • 🌟 实用性强:可直接应用于实际项目开发

🎨 界面展示

界面采用现代化的数据管理应用设计:

  • 学生管理:学生信息的增删改查
  • 课程管理:课程数据的维护和查询
  • 成绩管理:成绩记录的统计和分析
  • 数据统计:图表展示和数据分析
  • 设置页面:数据库配置和维护工具

📱 适用场景

  • 📚 教育应用的学生和课程管理
  • 📝 笔记应用的内容存储和检索
  • 💰 财务应用的账单和交易记录
  • 📊 数据应用的本地缓存和分析
  • 🛍️ 电商应用的商品和订单管理
  • 📱 通讯录应用的联系人信息存储

img

img

img


🔧 核心代码说明

📁 项目结构

SQLiteDemo/
├── src/
│   ├── main/
│   │   ├── ets/
│   │   │   ├── pages/
│   │   │   │   └── Index.ets          // 主页面
│   │   │   ├── database/
│   │   │   │   ├── DatabaseHelper.ets // 数据库助手类
│   │   │   │   └── StudentDao.ets     // 数据访问对象
│   │   │   └── entryability/
│   │   └── resources/
│   │       ├── base/
│   │       │   ├── element/
│   │       │   └── media/
│   │       └── rawfile/
│   └── module.json5

🎯 关键技术点

1. 数据库初始化

import relationalStore from '@ohos.data.relationalStore';

// 数据库配置
const STORE_CONFIG: relationalStore.StoreConfig = {
  name: 'StudentDatabase.db',
  securityLevel: relationalStore.SecurityLevel.S1
};

// 创建数据库
relationalStore.getRdbStore(context, STORE_CONFIG);

2. 数据表创建

const CREATE_TABLE_STUDENT = `
  CREATE TABLE IF NOT EXISTS student (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER,
    grade TEXT,
    score REAL,
    created_time TEXT
  )
`;

3. 数据操作方法

// 插入数据
const valueBucket: relationalStore.ValuesBucket = {
  'name': 'John',
  'age': 18,
  'grade': 'A',
  'score': 95.5
};
store.insert('student', valueBucket);

4. 查询和事务处理

  • 条件查询:WHERE子句和参数绑定
  • 排序分页:ORDER BY和LIMIT子句
  • 事务管理:BEGIN、COMMIT、ROLLBACK
  • 索引优化:CREATE INDEX提升查询性能

💡 技术要点解析

数据库设计原则
遵循数据库设计的三范式,合理设计表结构和字段关系。

性能优化策略
通过索引、查询优化、批量操作等技术提升数据库性能。

数据安全保护
使用参数化查询防止SQL注入,设置合适的安全级别。


📝 完整Demo代码

🏠 主页面代码

// src/main/ets/pages/Index.ets
import relationalStore from '@ohos.data.relationalStore';
import { BusinessError } from '@ohos.base';

// 学生数据接口
interface StudentData {
  id?: number
  name: string
  age: number
  grade: string
  score: number
  created_time?: string
}

// 课程数据接口
interface CourseData {
  id?: number
  name: string
  teacher: string
  credits: number
  description: string
}

// 成绩数据接口
interface GradeData {
  id?: number
  student_id: number
  course_id: number
  score: number
  exam_date: string
}

// 统计数据接口
interface StatisticsData {
  totalStudents: number
  totalCourses: number
  averageScore: number
  topStudent: string
}

@Entry
@Component
struct SQLiteDemo {
  @State currentTab: number = 0
  @State students: StudentData[] = []
  @State courses: CourseData[] = []
  @State grades: GradeData[] = []
  @State statistics: StatisticsData = {
    totalStudents: 0,
    totalCourses: 0,
    averageScore: 0,
    topStudent: ''
  }
  @State isLoading: boolean = false
  @State showAddDialog: boolean = false
  @State editingStudent: StudentData | null = null

  private rdbStore: relationalStore.RdbStore | null = null
  private readonly DATABASE_NAME = 'StudentDatabase.db'

  // 数据库表创建SQL
  private readonly CREATE_TABLE_STUDENT = `
    CREATE TABLE IF NOT EXISTS student (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      age INTEGER NOT NULL,
      grade TEXT NOT NULL,
      score REAL NOT NULL,
      created_time TEXT DEFAULT CURRENT_TIMESTAMP
    )
  `

  private readonly CREATE_TABLE_COURSE = `
    CREATE TABLE IF NOT EXISTS course (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      teacher TEXT NOT NULL,
      credits INTEGER NOT NULL,
      description TEXT
    )
  `

  private readonly CREATE_TABLE_GRADE = `
    CREATE TABLE IF NOT EXISTS grade (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      student_id INTEGER NOT NULL,
      course_id INTEGER NOT NULL,
      score REAL NOT NULL,
      exam_date TEXT NOT NULL,
      FOREIGN KEY (student_id) REFERENCES student (id),
      FOREIGN KEY (course_id) REFERENCES course (id)
    )
  `

  aboutToAppear() {
    this.initDatabase()
  }

  aboutToDisappear() {
    if (this.rdbStore) {
      this.rdbStore = null
    }
  }

  build() {
    Column() {
      this.buildHeader()
      Tabs({ barPosition: BarPosition.End, index: this.currentTab }) {
        TabContent() {
          this.buildStudentPage()
        }
        .tabBar(this.buildTabBar(0, '学生管理', '👨‍🎓'))

        TabContent() {
          this.buildCoursePage()
        }
        .tabBar(this.buildTabBar(1, '课程管理', '📚'))

        TabContent() {
          this.buildGradePage()
        }
        .tabBar(this.buildTabBar(2, '成绩管理', '📊'))

        TabContent() {
          this.buildStatisticsPage()
        }
        .tabBar(this.buildTabBar(3, '数据统计', '📈'))
      }
      .layoutWeight(1)
      .barBackgroundColor('#FFFFFF')
      .barHeight(80)
      .animationDuration(300)
      .onChange((index: number) => {
        this.currentTab = index
        this.loadTabData(index)
      })

      if (this.showAddDialog) {
        this.buildAddStudentDialog()
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  @Builder
  buildHeader() {
    Row({ space: 15 }) {
      Text('🗄️')
        .fontSize(24)
      Text('SQLite数据库示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .layoutWeight(1)
      Text('🔄')
        .fontSize(20)
        .fontColor(Color.White)
        .onClick(() => {
          this.refreshData()
        })
    }
    .width('100%')
    .height(60)
    .padding({ left: 20, right: 20 })
    .backgroundColor('#667eea')
    .shadow({
      radius: 8,
      color: '#30000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildTabBar(index: number, title: string, icon: string) {
    Column({ space: 5 }) {
      Stack({ alignContent: Alignment.TopEnd }) {
        Text(icon)
          .fontSize(24)
          .fontColor(this.currentTab === index ? '#667eea' : '#999999')
        if (index === 0 && this.students.length > 0) {
          Text(this.students.length.toString())
            .fontSize(10)
            .fontColor(Color.White)
            .backgroundColor('#FF4444')
            .borderRadius(8)
            .padding({ left: 4, right: 4, top: 1, bottom: 1 })
            .margin({ top: -5, right: -5 })
        }
      }
      Text(title)
        .fontSize(12)
        .fontColor(this.currentTab === index ? '#667eea' : '#999999')
        .fontWeight(this.currentTab === index ? FontWeight.Medium : FontWeight.Normal)
    }
    .width(60)
    .height(60)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildStudentPage() {
    Column({ space: 20 }) {
      Row() {
        Text('👨‍🎓 学生管理')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
          .layoutWeight(1)
        Button('➕ 添加学生')
          .height(40)
          .fontSize(14)
          .backgroundColor('#27AE60')
          .borderRadius(20)
          .onClick(() => {
            this.showAddDialog = true
            this.editingStudent = null
          })
      }
      .width('100%')
      .padding({ left: 20, right: 20 })

      if (this.isLoading) {
        this.buildLoadingView()
      } else if (this.students.length === 0) {
        this.buildEmptyView('暂无学生数据', '点击上方按钮添加学生信息')
      } else {
        List({ space: 10 }) {
          ForEach(this.students, (student: StudentData, index: number) => {
            ListItem() {
              this.buildStudentCard(student, index)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
        .padding({ left: 20, right: 20 })
      }
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  buildCoursePage() {
    Column({ space: 20 }) {
      Row() {
        Text('📚 课程管理')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
          .layoutWeight(1)
        Button('➕ 添加课程')
          .height(40)
          .fontSize(14)
          .backgroundColor('#3498DB')
          .borderRadius(20)
          .onClick(() => {
            this.addSampleCourse()
          })
      }
      .width('100%')
      .padding({ left: 20, right: 20 })

      if (this.courses.length === 0) {
        this.buildEmptyView('暂无课程数据', '点击上方按钮添加课程信息')
      } else {
        List({ space: 10 }) {
          ForEach(this.courses, (course: CourseData, index: number) => {
            ListItem() {
              this.buildCourseCard(course, index)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
        .padding({ left: 20, right: 20 })
      }
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  buildGradePage() {
    Column({ space: 20 }) {
      Row() {
        Text('📊 成绩管理')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
          .layoutWeight(1)
        Button('📝 录入成绩')
          .height(40)
          .fontSize(14)
          .backgroundColor('#E74C3C')
          .borderRadius(20)
          .onClick(() => {
            this.addSampleGrade()
          })
      }
      .width('100%')
      .padding({ left: 20, right: 20 })

      if (this.grades.length === 0) {
        this.buildEmptyView('暂无成绩数据', '点击上方按钮录入成绩信息')
      } else {
        List({ space: 10 }) {
          ForEach(this.grades, (grade: GradeData, index: number) => {
            ListItem() {
              this.buildGradeCard(grade, index)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
        .padding({ left: 20, right: 20 })
      }
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  buildStatisticsPage() {
    Scroll() {
      Column({ space: 20 }) {
        Text('📈 数据统计')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
          .alignSelf(ItemAlign.Start)
          .padding({ left: 20 })

        this.buildStatisticsCards()
        this.buildChartSection()
        this.buildDatabaseActions()
      }
    }
    .width('100%')
    .height('100%')
    .padding({ bottom: 20 })
  }

  @Builder
  buildStudentCard(student: StudentData, index: number) {
    Row({ space: 15 }) {
      Text(`${index + 1}`)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .backgroundColor(this.getGradeColor(student.grade))
        .borderRadius(20)
        .width(40)
        .height(40)
        .textAlign(TextAlign.Center)
      Column({ space: 8 }) {
        Row({ space: 10 }) {
          Text(student.name)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#333333')
          Text(`${student.age}岁`)
            .fontSize(14)
            .fontColor('#666666')
            .backgroundColor('#F0F0F0')
            .padding({ left: 8, right: 8, top: 2, bottom: 2 })
            .borderRadius(10)
        }
        Row({ space: 15 }) {
          Text(`等级: ${student.grade}`)
            .fontSize(14)
            .fontColor('#666666')
          Text(`分数: ${student.score}`)
            .fontSize(14)
            .fontColor(student.score >= 90 ? '#27AE60' : student.score >= 60 ? '#F39C12' : '#E74C3C')
            .fontWeight(FontWeight.Medium)
        }
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      Column({ space: 5 }) {
        Text('✏️')
          .fontSize(16)
          .fontColor('#3498DB')
          .onClick(() => {
            this.editStudent(student)
          })
        Text('🗑️')
          .fontSize(16)
          .fontColor('#E74C3C')
          .onClick(() => {
            this.deleteStudent(student.id!)
          })
      }
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({
      radius: 5,
      color: '#20000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildCourseCard(course: CourseData, index: number) {
    Row({ space: 15 }) {
      Text('📚')
        .fontSize(32)
        .width(50)
        .textAlign(TextAlign.Center)
      Column({ space: 8 }) {
        Text(course.name)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        Row({ space: 15 }) {
          Text(`教师: ${course.teacher}`)
            .fontSize(14)
            .fontColor('#666666')
          Text(`学分: ${course.credits}`)
            .fontSize(14)
            .fontColor('#3498DB')
            .fontWeight(FontWeight.Medium)
        }
        if (course.description) {
          Text(course.description)
            .fontSize(12)
            .fontColor('#999999')
            .maxLines(2)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
        }
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({
      radius: 5,
      color: '#20000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildGradeCard(grade: GradeData, index: number) {
    Row({ space: 15 }) {
      Text(this.getScoreEmoji(grade.score))
        .fontSize(32)
        .width(50)
        .textAlign(TextAlign.Center)
      Column({ space: 8 }) {
        Row({ space: 10 }) {
          Text(`学生ID: ${grade.student_id}`)
            .fontSize(16)
            .fontColor('#333333')
          Text(`课程ID: ${grade.course_id}`)
            .fontSize(16)
            .fontColor('#333333')
        }
        Row({ space: 15 }) {
          Text(`成绩: ${grade.score}`)
            .fontSize(16)
            .fontColor(this.getScoreColor(grade.score))
            .fontWeight(FontWeight.Bold)
          Text(`考试日期: ${grade.exam_date}`)
            .fontSize(14)
            .fontColor('#666666')
        }
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({
      radius: 5,
      color: '#20000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildStatisticsCards() {
    Row() {
      this.buildStatCard('👨‍🎓', '学生总数', this.statistics.totalStudents.toString(), '#3498DB')
      this.buildStatCard('📚', '课程总数', this.statistics.totalCourses.toString(), '#27AE60')
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 20, right: 20 })

    Row() {
      this.buildStatCard('📊', '平均分', this.statistics.averageScore.toFixed(1), '#E74C3C')
      this.buildStatCard('🏆', '最高分学生', this.statistics.topStudent || '暂无', '#F39C12')
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 20, right: 20 })
  }

  @Builder
  buildStatCard(icon: string, title: string, value: string, color: string) {
    Column({ space: 10 }) {
      Text(icon)
        .fontSize(32)
        .fontColor(color)
      Text(value)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
      Text(title)
        .fontSize(14)
        .fontColor('#666666')
    }
    .width('45%')
    .height(120)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .justifyContent(FlexAlign.Center)
    .shadow({
      radius: 5,
      color: '#20000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildChartSection() {
    Column({ space: 15 }) {
      Text('📈 成绩分布')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
        .alignSelf(ItemAlign.Start)
        .padding({ left: 20 })

      Column({ space: 10 }) {
        this.buildScoreBar('优秀 (90-100)', this.getScoreCount(90, 100), '#27AE60')
        this.buildScoreBar('良好 (80-89)', this.getScoreCount(80, 89), '#3498DB')
        this.buildScoreBar('及格 (60-79)', this.getScoreCount(60, 79), '#F39C12')
        this.buildScoreBar('不及格 (0-59)', this.getScoreCount(0, 59), '#E74C3C')
      }
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(12)
      .padding(20)
      .shadow({
        radius: 5,
        color: '#20000000',
        offsetX: 0,
        offsetY: 2
      })
      .margin({ left: 20, right: 20 })
    }
  }

  @Builder
  buildScoreBar(label: string, count: number, color: string) {
    Row({ space: 15 }) {
      Text(label)
        .fontSize(14)
        .fontColor('#333333')
        .width(100)
      Row()
        .width(`${Math.max(count * 20, 5)}%`)
        .height(20)
        .backgroundColor(color)
        .borderRadius(10)
      Text(count.toString())
        .fontSize(14)
        .fontColor('#666666')
        .fontWeight(FontWeight.Medium)
    }
    .width('100%')
    .alignItems(VerticalAlign.Center)
  }

  @Builder
  buildDatabaseActions() {
    Column({ space: 15 }) {
      Text('🔧 数据库操作')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
        .alignSelf(ItemAlign.Start)
        .padding({ left: 20 })

      Column({ space: 10 }) {
        Button('📊 生成示例数据')
          .width('100%')
          .height(45)
          .backgroundColor('#3498DB')
          .borderRadius(22)
          .onClick(() => {
            this.generateSampleData()
          })
        Button('🗑️ 清空所有数据')
          .width('100%')
          .height(45)
          .backgroundColor('#E74C3C')
          .borderRadius(22)
          .onClick(() => {
            this.clearAllData()
          })
        Button('📈 刷新统计数据')
          .width('100%')
          .height(45)
          .backgroundColor('#27AE60')
          .borderRadius(22)
          .onClick(() => {
            this.updateStatistics()
          })
      }
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(12)
      .padding(20)
      .shadow({
        radius: 5,
        color: '#20000000',
        offsetX: 0,
        offsetY: 2
      })
      .margin({ left: 20, right: 20 })
    }
  }

  @Builder
  buildLoadingView() {
    Column({ space: 20 }) {
      LoadingProgress()
        .width(50)
        .height(50)
        .color('#667eea')
      Text('正在加载数据...')
        .fontSize(16)
        .fontColor('#666666')
    }
    .width('100%')
    .height(200)
    .justifyContent(FlexAlign.Center)
    .backgroundColor(Color.White)
    .borderRadius(15)
    .margin({ left: 20, right: 20 })
    .shadow({
      radius: 8,
      color: '#20000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildEmptyView(title: string, subtitle: string) {
    Column({ space: 20 }) {
      Text('📝')
        .fontSize(60)
      Text(title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
      Text(subtitle)
        .fontSize(14)
        .fontColor('#666666')
        .textAlign(TextAlign.Center)
    }
    .width('100%')
    .height(200)
    .justifyContent(FlexAlign.Center)
    .backgroundColor(Color.White)
    .borderRadius(15)
    .margin({ left: 20, right: 20 })
    .shadow({
      radius: 8,
      color: '#20000000',
      offsetX: 0,
      offsetY: 2
    })
  }

  @Builder
  buildAddStudentDialog() {
    Stack({ alignContent: Alignment.Center }) {
      // 背景遮罩
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor('#80000000')
        .onClick(() => {
          this.showAddDialog = false
        })

      // 对话框内容
      Column({ space: 20 }) {
        Text(this.editingStudent ? '编辑学生' : '添加学生')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')

        Column({ space: 15 }) {
          TextInput({ placeholder: '请输入姓名' })
            .width('100%')
            .height(45)
            .borderRadius(8)
            .backgroundColor('#F8F9FA')

          TextInput({ placeholder: '请输入年龄' })
            .width('100%')
            .height(45)
            .borderRadius(8)
            .backgroundColor('#F8F9FA')
            .type(InputType.Number)

          TextInput({ placeholder: '请输入等级 (A/B/C/D)' })
            .width('100%')
            .height(45)
            .borderRadius(8)
            .backgroundColor('#F8F9FA')

          TextInput({ placeholder: '请输入分数' })
            .width('100%')
            .height(45)
            .borderRadius(8)
            .backgroundColor('#F8F9FA')
            .type(InputType.Number)
        }

        Row({ space: 15 }) {
          Button('取消')
            .width('45%')
            .height(45)
            .backgroundColor('#95A5A6')
            .borderRadius(22)
            .onClick(() => {
              this.showAddDialog = false
            })
          Button('确定')
            .width('45%')
            .height(45)
            .backgroundColor('#27AE60')
            .borderRadius(22)
            .onClick(() => {
              this.saveStudent()
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .width('80%')
      .backgroundColor(Color.White)
      .borderRadius(15)
      .padding(25)
      .shadow({
        radius: 15,
        color: '#40000000',
        offsetX: 0,
        offsetY: 8
      })
    }
    .width('100%')
    .height('100%')
  }

  // 数据库初始化方法
  async initDatabase() {
    try {
      const context = getContext(this)
      const STORE_CONFIG: relationalStore.StoreConfig = {
        name: this.DATABASE_NAME,
        securityLevel: relationalStore.SecurityLevel.S1
      }

      this.rdbStore = await relationalStore.getRdbStore(context, STORE_CONFIG)

      // 创建表
      await this.rdbStore.executeSql(this.CREATE_TABLE_STUDENT)
      await this.rdbStore.executeSql(this.CREATE_TABLE_COURSE)
      await this.rdbStore.executeSql(this.CREATE_TABLE_GRADE)

      // 创建索引提升查询性能
      await this.rdbStore.executeSql('CREATE INDEX IF NOT EXISTS idx_student_name ON student(name)')
      await this.rdbStore.executeSql('CREATE INDEX IF NOT EXISTS idx_student_score ON student(score)')
      await this.rdbStore.executeSql('CREATE INDEX IF NOT EXISTS idx_grade_student ON grade(student_id)')

      console.log('Database initialized successfully')
      this.loadAllData()
    } catch (error) {
      console.error('Failed to initialize database:', error)
    }
  }

  // 加载所有数据
  async loadAllData() {
    await this.loadStudents()
    await this.loadCourses()
    await this.loadGrades()
    await this.updateStatistics()
  }

  // 根据标签页加载对应数据
  loadTabData(tabIndex: number) {
    switch (tabIndex) {
      case 0:
        this.loadStudents()
        break
      case 1:
        this.loadCourses()
        break
      case 2:
        this.loadGrades()
        break
      case 3:
        this.updateStatistics()
        break
    }
  }

  // 学生数据操作方法
  async loadStudents() {
    if (!this.rdbStore) return

    try {
      this.isLoading = true
      const predicates = new relationalStore.RdbPredicates('student')
      predicates.orderByDesc('created_time')

      const resultSet = await this.rdbStore.query(predicates)
      const students: StudentData[] = []

      if (resultSet.goToFirstRow()) {
        do {
          const student: StudentData = {
            id: resultSet.getLong(resultSet.getColumnIndex('id')),
            name: resultSet.getString(resultSet.getColumnIndex('name')),
            age: resultSet.getLong(resultSet.getColumnIndex('age')),
            grade: resultSet.getString(resultSet.getColumnIndex('grade')),
            score: resultSet.getDouble(resultSet.getColumnIndex('score')),
            created_time: resultSet.getString(resultSet.getColumnIndex('created_time'))
          }
          students.push(student)
        } while (resultSet.goToNextRow())
      }

      resultSet.close()
      this.students = students
    } catch (error) {
      console.error('Failed to load students:', error)
    } finally {
      this.isLoading = false
    }
  }

  async addStudent(student: StudentData) {
    if (!this.rdbStore) return

    try {
      const valueBucket: relationalStore.ValuesBucket = {
        'name': student.name,
        'age': student.age,
        'grade': student.grade,
        'score': student.score,
        'created_time': new Date().toISOString()
      }

      await this.rdbStore.insert('student', valueBucket)
      console.log('Student added successfully')
      this.loadStudents()
    } catch (error) {
      console.error('Failed to add student:', error)
    }
  }

  async updateStudent(student: StudentData) {
    if (!this.rdbStore || !student.id) return

    try {
      const valueBucket: relationalStore.ValuesBucket = {
        'name': student.name,
        'age': student.age,
        'grade': student.grade,
        'score': student.score
      }

      const predicates = new relationalStore.RdbPredicates('student')
      predicates.equalTo('id', student.id)

      await this.rdbStore.update(valueBucket, predicates)
      console.log('Student updated successfully')
      this.loadStudents()
    } catch (error) {
      console.error('Failed to update student:', error)
    }
  }

  async deleteStudent(studentId: number) {
    if (!this.rdbStore) return

    try {
      const predicates = new relationalStore.RdbPredicates('student')
      predicates.equalTo('id', studentId)

      await this.rdbStore.delete(predicates)
      console.log('Student deleted successfully')
      this.loadStudents()
    } catch (error) {
      console.error('Failed to delete student:', error)
    }
  }

  // 课程数据操作方法
  async loadCourses() {
    if (!this.rdbStore) return

    try {
      const predicates = new relationalStore.RdbPredicates('course')
      const resultSet = await this.rdbStore.query(predicates)
      const courses: CourseData[] = []

      if (resultSet.goToFirstRow()) {
        do {
          const course: CourseData = {
            id: resultSet.getLong(resultSet.getColumnIndex('id')),
            name: resultSet.getString(resultSet.getColumnIndex('name')),
            teacher: resultSet.getString(resultSet.getColumnIndex('teacher')),
            credits: resultSet.getLong(resultSet.getColumnIndex('credits')),
            description: resultSet.getString(resultSet.getColumnIndex('description'))
          }
          courses.push(course)
        } while (resultSet.goToNextRow())
      }

      resultSet.close()
      this.courses = courses
    } catch (error) {
      console.error('Failed to load courses:', error)
    }
  }

  async addSampleCourse() {
    const sampleCourses: CourseData[] = [
      { name: '高等数学', teacher: '张教授', credits: 4, description: '数学基础课程,培养逻辑思维能力' },
      { name: '程序设计', teacher: '李老师', credits: 3, description: '编程基础课程,学习算法和数据结构' },
      { name: '数据库原理', teacher: '王老师', credits: 3, description: '数据库设计和SQL语言学习' },
      { name: '操作系统', teacher: '赵教授', credits: 4, description: '计算机系统原理和操作系统设计' }
    ]

    for (const course of sampleCourses) {
      await this.addCourse(course)
    }
  }

  async addCourse(course: CourseData) {
    if (!this.rdbStore) return

    try {
      const valueBucket: relationalStore.ValuesBucket = {
        'name': course.name,
        'teacher': course.teacher,
        'credits': course.credits,
        'description': course.description || ''
      }

      await this.rdbStore.insert('course', valueBucket)
      console.log('Course added successfully')
      this.loadCourses()
    } catch (error) {
      console.error('Failed to add course:', error)
    }
  }

  // 成绩数据操作方法
  async loadGrades() {
    if (!this.rdbStore) return

    try {
      const sql = `
        SELECT g.*, s.name as student_name, c.name as course_name
        FROM grade g
        LEFT JOIN student s ON g.student_id = s.id
        LEFT JOIN course c ON g.course_id = c.id
        ORDER BY g.exam_date DESC
      `

      const resultSet = await this.rdbStore.querySql(sql)
      const grades: GradeData[] = []

      if (resultSet.goToFirstRow()) {
        do {
          const grade: GradeData = {
            id: resultSet.getLong(resultSet.getColumnIndex('id')),
            student_id: resultSet.getLong(resultSet.getColumnIndex('student_id')),
            course_id: resultSet.getLong(resultSet.getColumnIndex('course_id')),
            score: resultSet.getDouble(resultSet.getColumnIndex('score')),
            exam_date: resultSet.getString(resultSet.getColumnIndex('exam_date'))
          }
          grades.push(grade)
        } while (resultSet.goToNextRow())
      }

      resultSet.close()
      this.grades = grades
    } catch (error) {
      console.error('Failed to load grades:', error)
    }
  }

  async addSampleGrade() {
    if (this.students.length === 0 || this.courses.length === 0) {
      console.log('Please add students and courses first')
      return
    }

    const randomStudent = this.students[Math.floor(Math.random() * this.students.length)]
    const randomCourse = this.courses[Math.floor(Math.random() * this.courses.length)]
    const randomScore = Math.floor(Math.random() * 40) + 60 // 60-100分

    const grade: GradeData = {
      student_id: randomStudent.id!,
      course_id: randomCourse.id!,
      score: randomScore,
      exam_date: new Date().toISOString().split('T')[0]
    }

    await this.addGrade(grade)
  }

  async addGrade(grade: GradeData) {
    if (!this.rdbStore) return

    try {
      const valueBucket: relationalStore.ValuesBucket = {
        'student_id': grade.student_id,
        'course_id': grade.course_id,
        'score': grade.score,
        'exam_date': grade.exam_date
      }

      await this.rdbStore.insert('grade', valueBucket)
      console.log('Grade added successfully')
      this.loadGrades()
    } catch (error) {
      console.error('Failed to add grade:', error)
    }
  }

  // 统计数据更新
  async updateStatistics() {
    if (!this.rdbStore) return

    try {
      // 统计学生总数
      const studentCountSql = 'SELECT COUNT(*) as count FROM student'
      let resultSet = await this.rdbStore.querySql(studentCountSql)
      const totalStudents = resultSet.goToFirstRow() ? resultSet.getLong(0) : 0
      resultSet.close()

      // 统计课程总数
      const courseCountSql = 'SELECT COUNT(*) as count FROM course'
      resultSet = await this.rdbStore.querySql(courseCountSql)
      const totalCourses = resultSet.goToFirstRow() ? resultSet.getLong(0) : 0
      resultSet.close()

      // 计算平均分
      const avgScoreSql = 'SELECT AVG(score) as avg_score FROM student WHERE score > 0'
      resultSet = await this.rdbStore.querySql(avgScoreSql)
      const averageScore = resultSet.goToFirstRow() ? resultSet.getDouble(0) : 0
      resultSet.close()

      // 查找最高分学生
      const topStudentSql = 'SELECT name FROM student WHERE score = (SELECT MAX(score) FROM student) LIMIT 1'
      resultSet = await this.rdbStore.querySql(topStudentSql)
      const topStudent = resultSet.goToFirstRow() ? resultSet.getString(0) : ''
      resultSet.close()

      this.statistics = {
        totalStudents,
        totalCourses,
        averageScore,
        topStudent
      }
    } catch (error) {
      console.error('Failed to update statistics:', error)
    }
  }

  // 生成示例数据
  async generateSampleData() {
    const sampleStudents: StudentData[] = [
      { name: '张三', age: 20, grade: 'A', score: 95 },
      { name: '李四', age: 19, grade: 'B', score: 87 },
      { name: '王五', age: 21, grade: 'A', score: 92 },
      { name: '赵六', age: 20, grade: 'C', score: 78 },
      { name: '钱七', age: 22, grade: 'B', score: 85 },
      { name: '孙八', age: 19, grade: 'A', score: 96 },
      { name: '周九', age: 21, grade: 'C', score: 72 },
      { name: '吴十', age: 20, grade: 'B', score: 89 }
    ]

    for (const student of sampleStudents) {
      await this.addStudent(student)
    }

    await this.addSampleCourse()

    // 等待课程数据加载完成后再生成成绩
    setTimeout(async () => {
      for (let i = 0; i < 10; i++) {
        await this.addSampleGrade()
      }
    }, 1000)
  }

  // 清空所有数据
  async clearAllData() {
    if (!this.rdbStore) return

    try {
      await this.rdbStore.executeSql('DELETE FROM grade')
      await this.rdbStore.executeSql('DELETE FROM course')
      await this.rdbStore.executeSql('DELETE FROM student')

      console.log('All data cleared successfully')
      this.loadAllData()
    } catch (error) {
      console.error('Failed to clear data:', error)
    }
  }

  // 刷新数据
  refreshData() {
    this.loadTabData(this.currentTab)
  }

  // 编辑学生
  editStudent(student: StudentData) {
    this.editingStudent = student
    this.showAddDialog = true
  }

  // 保存学生(新增或编辑)
  saveStudent() {
    // 这里应该获取表单数据并保存
    // 为了演示,使用示例数据
    const newStudent: StudentData = {
      name: '新学生',
      age: 20,
      grade: 'A',
      score: 90
    }

    if (this.editingStudent) {
      newStudent.id = this.editingStudent.id
      this.updateStudent(newStudent)
    } else {
      this.addStudent(newStudent)
    }

    this.showAddDialog = false
  }

  // 工具方法
  getGradeColor(grade: string): string {
    switch (grade) {
      case 'A': return '#27AE60'
      case 'B': return '#3498DB'
      case 'C': return '#F39C12'
      case 'D': return '#E74C3C'
      default: return '#95A5A6'
    }
  }

  getScoreColor(score: number): string {
    if (score >= 90) return '#27AE60'
    if (score >= 80) return '#3498DB'
    if (score >= 60) return '#F39C12'
    return '#E74C3C'
  }

  getScoreEmoji(score: number): string {
    if (score >= 90) return '🏆'
    if (score >= 80) return '🥈'
    if (score >= 60) return '🥉'
    return '📉'
  }

  getScoreCount(minScore: number, maxScore: number): number {
    return this.students.filter(student =>
    student.score >= minScore && student.score <= maxScore
    ).length
  }
}

⚙️ 配置文件

// module.json5 配置
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "requestPermissions": [
      {
        "name": "ohos.permission.WRITE_USER_STORAGE",
        "reason": "$string:storage_permission_reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.READ_USER_STORAGE",
        "reason": "$string:storage_permission_reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      }
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ts",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background"
      }
    ]
  }
}

🚀 运行效果

📱 界面展示

运行后的界面将展示:

  • 🗄️ 现代化设计:渐变色标题栏和精美的卡片设计
  • 📑 四个功能页面:学生管理、课程管理、成绩管理、数据统计
  • 👨‍🎓 学生管理页:学生列表 + 添加编辑 + 删除功能
  • 📚 课程管理页:课程信息展示和管理
  • 📊 成绩管理页:成绩记录和统计分析
  • 📈 数据统计页:图表展示和数据库操作

✅ 功能验证

  1. 数据库创建:应用启动时自动创建数据库和表结构
  2. 数据增删改查:测试学生信息的完整CRUD操作
  3. 复杂查询:验证联表查询和统计分析功能
  4. 事务处理:测试批量操作的事务一致性
  5. 性能优化:观察索引对查询性能的提升效果
  6. 数据持久化:重启应用后数据依然存在

💡 开发小贴士

🎯 最佳实践

  • 💫 数据库设计:遵循三范式,合理设计表结构和字段关系
  • 🎨 性能优化:创建合适的索引,使用参数化查询
  • 事务管理:对于批量操作使用事务保证数据一致性
  • 🔧 错误处理:完善的异常捕获和错误提示机制

🚨 常见问题

  1. 权限问题:确保已申请存储相关权限
  2. 数据库路径:使用应用沙箱路径存储数据库文件
  3. SQL注入:使用参数化查询防止SQL注入攻击
  4. 内存泄漏:及时关闭ResultSet和数据库连接

📚 扩展学习

  • 数据库升级:学习数据库版本管理和结构升级
  • 数据加密:了解敏感数据的加密存储方案
  • 备份恢复:实现数据库的备份和恢复功能

🎉 总结与展望

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

  • ✨ HarmonyOS中SQLite数据库的完整使用流程
  • 🎯 数据库设计原则和性能优化技巧
  • 💡 复杂查询和数据统计的实现方法
  • 🎨 数据管理应用的界面设计和用户体验

SQLite数据库是移动应用开发中最重要的本地存储技术之一,掌握其各种用法对于创建功能完整的应用至关重要。从简单的数据存储到复杂的关系管理,每一个细节都体现着应用的专业性和可靠性。

这个示例展示了如何构建一个完整的数据库应用,包含了实际项目中常见的功能需求。通过合理的架构设计和优化策略,我们能够创建出既稳定又高效的数据存储应用。

希望这个示例能够帮助到正在学习HarmonyOS开发的你!下一期我们将探索更多高级功能和开发技巧,敬请期待!如果你有任何问题或建议,欢迎在评论区留言交流。


🔗 相关资源


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

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


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

Logo

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

更多推荐