HarmonyOS应用开发实战 | ArkTS SQLite数据库使用指南(精品篇)
💫 坚果派·红目香薰 倾情分享
🎯 用心打造每一个技术细节,为开发者创造更多价值
📱 让HarmonyOS开发变得更简单、更有趣
✨ 写在前面的话
嗨,亲爱的技术朋友们!👋
我是来自坚果派的红目香薰,一个热爱技术、专注HarmonyOS开发的程序媛。在这个数字化飞速发展的时代,HarmonyOS作为华为自主研发的操作系统,正在改变着我们的数字生活体验。
🌈 为什么要写这个系列?
- 💡 让复杂的技术变得简单易懂
- 🚀 帮助更多开发者快速上手HarmonyOS
- 💝 分享实战经验,避免踩坑
- 🌟 用代码创造美好,用技术传递温暖
每一个Demo都是我精心设计和反复测试的结果,希望能够为你的HarmonyOS开发之路点亮一盏明灯。✨
今天我们来深入学习HarmonyOS中最重要的本地存储技术之一——SQLite数据库。从基础的增删改查到复杂的数据关系管理,让我们一起打造高效可靠的数据存储应用!
📋 Demo功能说明
🎯 核心功能
本Demo展示了HarmonyOS中SQLite数据库的全面使用方法,包括:
- 🗄️ 数据库的创建、连接和版本管理
- 📝 数据表的设计和结构优化
- ➕ 数据的增加(INSERT)操作和批量插入
- 🔍 数据的查询(SELECT)和条件筛选
- ✏️ 数据的更新(UPDATE)和字段修改
- 🗑️ 数据的删除(DELETE)和清理操作
- 🔄 事务处理和数据一致性保证
- 📊 复杂查询和数据统计分析
- 🌟 实际应用场景的完整实现
✨ 特色亮点
- 🎨 界面精美:现代化的数据管理界面设计
- 🚀 性能优秀:高效的数据库操作和查询优化
- 📱 用户友好:直观的数据展示和操作反馈
- 💡 功能完整:涵盖数据库应用的各种场景
- 🌟 实用性强:可直接应用于实际项目开发
🎨 界面展示
界面采用现代化的数据管理应用设计:
- 学生管理:学生信息的增删改查
- 课程管理:课程数据的维护和查询
- 成绩管理:成绩记录的统计和分析
- 数据统计:图表展示和数据分析
- 设置页面:数据库配置和维护工具
📱 适用场景
- 📚 教育应用的学生和课程管理
- 📝 笔记应用的内容存储和检索
- 💰 财务应用的账单和交易记录
- 📊 数据应用的本地缓存和分析
- 🛍️ 电商应用的商品和订单管理
- 📱 通讯录应用的联系人信息存储
🔧 核心代码说明
📁 项目结构
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"
}
]
}
}
🚀 运行效果
📱 界面展示
运行后的界面将展示:
- 🗄️ 现代化设计:渐变色标题栏和精美的卡片设计
- 📑 四个功能页面:学生管理、课程管理、成绩管理、数据统计
- 👨🎓 学生管理页:学生列表 + 添加编辑 + 删除功能
- 📚 课程管理页:课程信息展示和管理
- 📊 成绩管理页:成绩记录和统计分析
- 📈 数据统计页:图表展示和数据库操作
✅ 功能验证
- 数据库创建:应用启动时自动创建数据库和表结构
- 数据增删改查:测试学生信息的完整CRUD操作
- 复杂查询:验证联表查询和统计分析功能
- 事务处理:测试批量操作的事务一致性
- 性能优化:观察索引对查询性能的提升效果
- 数据持久化:重启应用后数据依然存在
💡 开发小贴士
🎯 最佳实践
- 💫 数据库设计:遵循三范式,合理设计表结构和字段关系
- 🎨 性能优化:创建合适的索引,使用参数化查询
- ⚡ 事务管理:对于批量操作使用事务保证数据一致性
- 🔧 错误处理:完善的异常捕获和错误提示机制
🚨 常见问题
- 权限问题:确保已申请存储相关权限
- 数据库路径:使用应用沙箱路径存储数据库文件
- SQL注入:使用参数化查询防止SQL注入攻击
- 内存泄漏:及时关闭ResultSet和数据库连接
📚 扩展学习
- 数据库升级:学习数据库版本管理和结构升级
- 数据加密:了解敏感数据的加密存储方案
- 备份恢复:实现数据库的备份和恢复功能
🎉 总结与展望
通过这个Demo,我们学习了:
- ✨ HarmonyOS中SQLite数据库的完整使用流程
- 🎯 数据库设计原则和性能优化技巧
- 💡 复杂查询和数据统计的实现方法
- 🎨 数据管理应用的界面设计和用户体验
SQLite数据库是移动应用开发中最重要的本地存储技术之一,掌握其各种用法对于创建功能完整的应用至关重要。从简单的数据存储到复杂的关系管理,每一个细节都体现着应用的专业性和可靠性。
这个示例展示了如何构建一个完整的数据库应用,包含了实际项目中常见的功能需求。通过合理的架构设计和优化策略,我们能够创建出既稳定又高效的数据存储应用。
希望这个示例能够帮助到正在学习HarmonyOS开发的你!下一期我们将探索更多高级功能和开发技巧,敬请期待!如果你有任何问题或建议,欢迎在评论区留言交流。
🔗 相关资源
- 📚 HarmonyOS官方文档
- 🛠️ DevEco Studio下载
- 💬 坚果派技术社区
- 🗄️ 关系型数据库开发指南
- 📖 ArkTS语言指南
🌟 如果这篇文章对你有帮助,请点赞支持!🌟
让我们一起在HarmonyOS的世界里创造更多可能!
© 2024 坚果派·红目香薰 | 用心分享,用技术创造价值
更多推荐
所有评论(0)