在移动应用开发中,数据持久化是构建复杂应用的基础能力。鸿蒙系统提供了强大的RelationalStore关系型数据库模块,使开发者能够轻松实现数据的增删改查操作。本文将通过一个完整的文章管理系统案例,深入解析鸿蒙数据库的使用方法、设计模式与最佳实践。
鸿蒙RelationalStore数据库基础概念
RelationalStore是鸿蒙系统提供的轻量级关系型数据库解决方案,专门为移动设备优化,具有以下核心特性:
- 轻量级架构:基于SQLite引擎,占用资源少,适合移动设备
- 类型安全:支持TypeScript类型标注,编译期检查数据类型
- 异步操作:所有数据库操作均为异步,避免阻塞UI线程
- 事务支持:支持完整的ACID事务,确保数据一致性
- 安全级别:提供S1/S2/S3多级安全配置,保护敏感数据
数据库核心组件
RelationalStore由以下关键组件构成:
- RdbStore:数据库主操作类,提供增删改查等核心功能
- RdbPredicates:查询条件构建器,支持链式调用构造复杂查询
- ValuesBucket:键值对容器,用于存储和传递数据
- ResultSet:查询结果集,提供遍历和数据提取功能
- StoreConfig:数据库配置类,定义数据库名称和安全级别
文章管理系统数据模型设计
Article数据类设计
系统核心数据模型为Article
类,采用@Sendable
装饰器使其可在不同线程间传递:
@Sendable
export class Article {
id?: number;
userImage: string = ''; // 用户头像
phone: string = ''; // 用户手机号
name: string = ''; // 用户名
source: string = ''; // 标签
title: string = ''; // 标题
contentStr: string = ''; // 内容
likes: number = 0; // 点赞数量
comments: number = 0; // 评论数量
shares: number = 0; // 收藏数量
constructor(
id: number,
userImage: string,
phone: string,
name: string,
source: string,
title: string,
contentStr: string,
likes: number,
comments: number,
shares: number
) {
this.id = id;
this.userImage = userImage;
this.phone = phone;
this.name = name;
this.source = source;
this.title = title;
this.contentStr = contentStr;
this.likes = likes;
this.comments = comments;
this.shares = shares;
}
}
该模型包含了文章的基本属性,其中:
id
为主键,使用AUTOINCREMENT
自增- 包含用户相关信息(头像、手机号、用户名)
- 设计了社交属性(点赞、评论、收藏数量)
- 所有属性均设置了默认值,确保数据完整性
数据库表结构设计
系统创建了三张核心表,以article
表为例,其SQL建表语句如下:
CREATE TABLE IF NOT EXISTS article (
id INTEGER PRIMARY KEY AUTOINCREMENT,
userImage TEXT NOT NULL,
phone TEXT NOT NULL,
name TEXT NOT NULL,
source TEXT NOT NULL,
title TEXT NOT NULL,
contentStr TEXT NOT NULL,
likes INTEGER NOT NULL DEFAULT 0,
comments INTEGER NOT NULL DEFAULT 0,
shares INTEGER NOT NULL DEFAULT 0
)
表结构设计特点:
- 使用
INTEGER PRIMARY KEY AUTOINCREMENT
设置自增主键 - 所有字段均设置
NOT NULL
约束,确保数据完整性 - 数值型字段设置默认值
DEFAULT 0
- 文本字段使用
TEXT
类型,适配不同长度文本
数据库操作核心类实现
单例模式数据库管理类
系统采用单例模式设计ArticleData
类,确保全局唯一数据库实例:
class ArticleData {
private static instance: ArticleData | undefined = undefined;
public rdbStore: relationalStore.RdbStore | undefined = undefined;
// 数据库实例获取方法
public static getInstance(): ArticleData {
if (!ArticleData.instance) {
ArticleData.instance = new ArticleData();
}
return ArticleData.instance;
}
// 初始化数据库
public async initRdbStore(context: common.Context): Promise<void> {
if (!context) {
console.error(TAG, 'Context is invalid or not provided');
return;
}
try {
this.rdbStore = await relationalStore.getRdbStore(
context,
{ name: 'article.db', securityLevel: relationalStore.SecurityLevel.S2 }
);
console.info(TAG, '数据库创建成功');
await this.createTable();
} catch (err) {
console.error(TAG, `getRdbStore failed, err: ${err}`);
}
}
}
单例模式优势:
- 避免多次创建数据库连接,提升性能
- 确保全局数据一致性
- 简化资源管理,自动处理连接生命周期
核心数据库操作方法
增删改查基础操作
// 添加文章数据
async addArticle(context: common.Context, article: Article): Promise<void> {
const insertValue: relationalStore.ValuesBucket = {
userImage: article.userImage,
phone: article.phone,
name: article.name,
source: article.source,
title: article.title,
contentStr: article.contentStr
// 数值型字段使用默认值0
};
await this.rdbStore?.insert(TABLE_NAME_ARTICLE, insertValue);
}
// 删除文章数据
async deleteArticle(context: common.Context, id: number): Promise<void> {
const predicate: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicate.equalTo('id', id);
await this.rdbStore?.delete(predicate);
}
// 更新文章数据
async updateArticle(context: common.Context, article: Article): Promise<void> {
const updateValue: relationalStore.ValuesBucket = {
userImage: article.userImage,
phone: article.phone,
name: article.name,
source: article.source,
title: article.title,
contentStr: article.contentStr,
likes: article.likes,
comments: article.comments,
shares: article.shares
};
const predicate: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicate.equalTo('id', article.id);
await this.rdbStore?.update(updateValue, predicate);
}
复杂查询操作
// 基础查询
public async queryArticle(context: common.Context): Promise<Array<Article>> {
let predicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicates.orderByAsc("id"); // 按ID升序排列
const resultSet: relationalStore.ResultSet = await this.rdbStore?.query(predicates) as relationalStore.ResultSet;
return this.getArticleListFromResultSet(resultSet);
}
// 条件查询 - 根据手机号查询
public async queryPhoneArticle(context: common.Context, phone?: string): Promise<Array<Article>> {
let predicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicates.orderByAsc("id");
if (phone) {
predicates.equalTo("phone", phone); // 添加手机号相等条件
}
const resultSet: relationalStore.ResultSet = await this.rdbStore?.query(predicates) as relationalStore.ResultSet;
return this.getArticleListFromResultSet(resultSet);
}
// 模糊查询 - 标题搜索
public async queryLIkeArticle(context: common.Context, content: string): Promise<Array<Article>> {
const result = await this.rdbStore?.querySql(
`select * from ${TABLE_NAME_ARTICLE} where title like '%${content}%'`
);
return this.getArticleListFromResultSet(result as relationalStore.ResultSet);
}
查询实现特点:
- 使用
RdbPredicates
构建类型安全的查询条件 - 支持排序、等值、模糊等多种查询方式
- 复杂查询使用原生SQL语句,提升灵活性
- 所有查询均返回
Promise
,支持异步操作
结果集处理
// 从ResultSet中提取文章数据
private getArticleListFromResultSet(resultSet: relationalStore.ResultSet): Array<Article> {
const articleList: Array<Article> = [];
while (resultSet.goToNextRow()) {
const article = new Article(
resultSet.getLong(resultSet.getColumnIndex('id')),
resultSet.getString(resultSet.getColumnIndex('userImage')),
resultSet.getString(resultSet.getColumnIndex('phone')),
resultSet.getString(resultSet.getColumnIndex('name')),
resultSet.getString(resultSet.getColumnIndex('source')),
resultSet.getString(resultSet.getColumnIndex('title')),
resultSet.getString(resultSet.getColumnIndex('contentStr')),
resultSet.getLong(resultSet.getColumnIndex('likes')),
resultSet.getLong(resultSet.getColumnIndex('comments')),
resultSet.getLong(resultSet.getColumnIndex('shares'))
);
articleList.push(article);
}
resultSet.close(); // 重要:关闭结果集释放资源
return articleList;
}
结果集处理要点:
- 使用
goToNextRow()
遍历结果集 - 通过
getColumnIndex
获取列索引,提升查询效率 - 严格匹配数据类型(
getLong
/getString
) - 最后调用
close()
释放资源,避免内存泄漏
前端界面与数据库集成
界面状态管理
前端界面通过@State
装饰器管理文章列表状态:
@Entry
@Component
struct Index {
// 本地文章列表,与数据库同步
@State articleList: Article[] = [];
// 输入框内容状态
@State titleInput: string = '';
@State contentInput: string = '';
// 数据库操作实例
private articleData: ArticleData = new ArticleData();
aboutToAppear(): void {
// 页面加载时查询数据库
this.articleData.queryArticle(getContext(this) as common.Context).then((articles) => {
this.articleList = articles;
});
}
}
状态管理特点:
@State
实现数据与UI的双向绑定- 页面加载时异步查询数据库并更新状态
- 输入框内容单独管理,避免与数据库状态耦合
数据库操作与UI交互
build() {
Column() {
// 标题输入框
TextInput({ placeholder: '输入文章标题' })
.onChange((value: string) => this.titleInput = value);
// 内容输入框
TextInput({ placeholder: '输入文章内容' })
.onChange((value: string) => this.contentInput = value);
// 添加文章按钮
Button('添加文章')
.onClick(async () => {
if (this.titleInput === '' || this.contentInput === '') return;
const context = getContext(this) as common.Context;
const newArticle = new Article(
1, // 实际应用中应省略ID,由数据库自增
'default_image_url',
'1234567890',
'默认用户',
'默认来源',
this.titleInput,
this.contentInput,
0, 0, 0
);
// 异步添加文章
await this.articleData.addArticle(context, newArticle);
// 更新文章列表
this.articleList = await this.articleData.queryArticle(context);
// 清空输入框
this.titleInput = '';
this.contentInput = '';
});
// 文章列表
List() {
ForEach(this.articleList, (article: Article) => {
ListItem() {
Column() {
Text(`标题: ${article.title}`).fontSize(20).fontWeight(FontWeight.Bold);
Text(`内容: ${article.contentStr}`).fontSize(16);
Text(`点赞: ${article.likes} | 评论: ${article.comments} | 收藏: ${article.shares}`)
.fontSize(14).fontColor('#666');
}
.width('100%').padding(10)
.backgroundColor('#f5f5f5').borderRadius(8);
}
}, (article: Article) => article.id?.toString());
}
.width('100%');
}
.padding(10);
}
UI与数据库集成要点:
- 按钮点击事件中实现数据库写入操作
- 使用
async/await
处理异步操作,保持代码可读性 - 数据更新后立即查询数据库并更新UI
- 列表使用
ForEach
循环渲染文章数据,自动响应状态变化
附:代码
import { common } from '@kit.AbilityKit';
import { relationalStore } from '@kit.ArkData';
@Sendable
export class Article {
id?:number
userImage:string = '' //用户头像
phone:string = '' // 用户手机号
name:string= '' //用户名
source: string= ''//标签
title: string= '' //标题
contentStr:string= '' //内容
likes: number= 0 //点赞数量
comments: number=0 //评论数量
shares: number=0//收藏数量
constructor(id: number, userImage: string, phone: string, name: string, source: string, title: string,
contentStr: string, likes: number, comments: number, shares: number) {
this.id = id
this.userImage = userImage
this.phone = phone
this.name = name
this.source = source
this.title = title
this.contentStr = contentStr
this.likes = likes
this.comments = comments
this.shares = shares
}
}
export const TAG = 'ArticleRdb'
export const CommentTAG = 'CommentRdb'
export const UserTAG = 'UserRdb'
export const TABLE_NAME_ARTICLE = 'article';
export const TABLE_NAME_USER = 'user';
export const TABLE_NAME_COMMENT = 'comments'
export const SQL_CREATE_TABLE_ARTICLE =
`CREATE TABLE IF NOT EXISTS article (
id INTEGER PRIMARY KEY AUTOINCREMENT,
userImage TEXT NOT NULL,
phone TEXT NOT NULL,
name TEXT NOT NULL,
source TEXT NOT NULL,
title TEXT NOT NULL,
contentStr TEXT NOT NULL,
likes INTEGER NOT NULL DEFAULT 0,
comments INTEGER NOT NULL DEFAULT 0,
shares INTEGER NOT NULL DEFAULT 0
)`;
const STORE_CONFIG_ARTICLE: relationalStore.StoreConfig = { name: 'article.db', securityLevel: relationalStore.SecurityLevel.S2 };
// 数据库配置
class ArticleData {
private static instance: ArticleData | undefined = undefined;
public rdbStore: relationalStore.RdbStore | undefined = undefined;
// 数据库实例
public static getInstance(): ArticleData {
if (!ArticleData.instance) {
ArticleData.instance = new ArticleData();
}
return ArticleData.instance;
}
// 初始化数据库
public async initRdbStore(context: common.Context): Promise<void> {
if (!context) {
console.error(TAG, 'Context is invalid or not provided');
return;
}
console.info(TAG, 'Context is valid:', context);
try {
this.rdbStore = await relationalStore.getRdbStore(context, STORE_CONFIG_ARTICLE);
console.info(TAG, '数据库创建成功');
await this.createTable();
} catch (err) {
console.error(TAG, `getRdbStore failed, err: ${err}`);
}
}
// 创建表
private async createTable(): Promise<void> {
try {
if (this.rdbStore) {
await this.rdbStore.executeSql(SQL_CREATE_TABLE_ARTICLE);
console.info(TAG, 'create table succeed');
}
} catch (err) {
console.error(TAG, `create table failed, err: ${err}`);
}
}
// 查询文章数据
public async queryArticle(context: common.Context): Promise<Array<Article>> {
console.info('queryArticle begin');
if (!context) {
console.error('context is null or undefined');
return [];
}
let predicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicates.orderByAsc("id");
try {
if (!this.rdbStore) {
console.error(TAG, 'RdbStore is not initialized');
await this.initRdbStore(context);
}
const resultSet: relationalStore.ResultSet = await this.rdbStore?.query(predicates) as relationalStore.ResultSet
console.info(TAG, `查询成功,表中有${resultSet.rowCount}篇文章`);
return this.getArticleListFromResultSet(resultSet);
} catch (err) {
console.error(TAG, err);
return [];
}
}
// 根据手机号查询文章数据
public async queryPhoneArticle(context: common.Context, phone?: string): Promise<Array<Article>> {
console.info('queryArticle begin');
if (!context) {
console.error('context is null or undefined');
return [];
}
let predicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicates.orderByAsc("id");
if (phone) {
predicates.equalTo("phone", phone);
}
try {
if (!this.rdbStore) {
console.error(TAG, 'RdbStore is not initialized');
await this.initRdbStore(context);
}
const resultSet: relationalStore.ResultSet = await this.rdbStore?.query(predicates) as relationalStore.ResultSet
console.info(TAG, `查询成功,表中有${resultSet.rowCount}篇文章`);
return this.getArticleListFromResultSet(resultSet);
} catch (err) {
console.error(TAG, `Query failed, error: ${err}`);
return [];
}
}
// 模糊查询文章数据
public async queryLIkeArticle(context: common.Context, content: string): Promise<Array<Article>> {
console.info('queryArticle begin');
if (!context) {
console.error('context is null or undefined');
return [];
}
let predicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicates.orderByAsc("id");
try {
if (!this.rdbStore) {
console.error(TAG, 'RdbStore is not initialized');
await this.initRdbStore(context);
}
const result = await this.rdbStore?.querySql(`select * from ${TABLE_NAME_ARTICLE} where title like '%${content}%'`);
const res = this.getArticleListFromResultSet(result as relationalStore.ResultSet);
return res;
} catch (err) {
console.error(TAG, `Query failed, error: ${err}`);
return [];
}
}
// 添加文章数据
async addArticle(context: common.Context, article: Article): Promise<void> {
const insertValue: relationalStore.ValuesBucket = {
userImage: article.userImage,
phone: article.phone,
name: article.name,
source: article.source,
title: article.title,
contentStr: article.contentStr,
likes: article.likes,
comments: article.comments,
shares: article.shares
};
try {
if (!this.rdbStore) {
console.error(TAG, 'RdbStore is not initialized');
await this.initRdbStore(context);
}
await this.rdbStore?.insert(TABLE_NAME_ARTICLE, insertValue);
console.info(TAG, `在${TABLE_NAME_ARTICLE}表中添加数据成功`);
} catch (err) {
console.error(TAG, `在${TABLE_NAME_ARTICLE}表中添加数据失败, err: ${err}`);
}
}
// 删除文章数据
async deleteArticle(context: common.Context, id: number): Promise<void> {
try {
if (!this.rdbStore) {
console.error(TAG, 'RdbStore is not initialized');
await this.initRdbStore(context);
}
const predicate: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicate.equalTo('id', id);
await this.rdbStore?.delete(predicate);
console.info(TAG, `在${TABLE_NAME_ARTICLE}删除数据成功`);
} catch (err) {
console.error(TAG, `在${TABLE_NAME_ARTICLE}删除数据失败`);
}
}
// 更新文章数据
async updateArticle(context: common.Context, article: Article): Promise<void> {
const updateValue: relationalStore.ValuesBucket = {
userImage: article.userImage,
phone: article.phone,
name: article.name,
source: article.source,
title: article.title,
contentStr: article.contentStr,
likes: article.likes,
comments: article.comments,
shares: article.shares
};
const predicate: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(TABLE_NAME_ARTICLE);
predicate.equalTo('id', article.id);
try {
if (!this.rdbStore) {
console.error(TAG, 'RdbStore is not initialized');
await this.initRdbStore(context);
}
await this.rdbStore?.update(updateValue, predicate);
console.info(TAG, `在${TABLE_NAME_ARTICLE}更新数据成功`);
} catch (err) {
console.error(TAG, `在${TABLE_NAME_ARTICLE}更新数据失败`);
}
}
// 从 ResultSet 中提取文章数据并转换为 Article 类数组
private getArticleListFromResultSet(resultSet: relationalStore.ResultSet): Array<Article> {
const articleList: Array<Article> = [];
while (resultSet.goToNextRow()) {
const article = new Article(
resultSet.getLong(resultSet.getColumnIndex('id')),
resultSet.getString(resultSet.getColumnIndex('userImage')),
resultSet.getString(resultSet.getColumnIndex('phone')),
resultSet.getString(resultSet.getColumnIndex('name')),
resultSet.getString(resultSet.getColumnIndex('source')),
resultSet.getString(resultSet.getColumnIndex('title')),
resultSet.getString(resultSet.getColumnIndex('contentStr')),
resultSet.getLong(resultSet.getColumnIndex('likes')),
resultSet.getLong(resultSet.getColumnIndex('comments')),
resultSet.getLong(resultSet.getColumnIndex('shares'))
);
articleList.push(article);
}
resultSet.close(); // 关闭 ResultSet
return articleList;
}
}
export { ArticleData };
@Entry
@Component
struct Index {
// 本地文章列表
@State articleList: Article[] = []
// 标题和内容输入框的临时存储变量
@State titleInput: string = ''
@State contentInput: string = ''
// 初始化数据库实例
private articleData: ArticleData = new ArticleData()
aboutToAppear(): void {
this.articleData.queryArticle(getContext(this) as common.Context).then((articles) => {
this.articleList = articles;
});
}
build() {
Column() {
// 文章标题输入框
TextInput({ placeholder: '输入文章标题' })
.onChange((value: string) => {
this.titleInput = value
})
// 文章内容输入框
TextInput({ placeholder: '输入文章内容', })
.onChange((value: string) => {
this.contentInput = value
})
// 添加文章按钮
Button('添加文章')
.onClick(async () => {
if (this.titleInput === '' || this.contentInput === '') {
console.warn(TAG, '标题或内容为空,不添加文章')
return
}
const context = getContext(this) as common.Context
// 创建一个Article对象
const newArticle: Article = new Article(
1,
'default_image_url',
'1234567890',
'默认用户',
'默认来源',
this.titleInput,
this.contentInput,
0,
0,
0
)
// 调用数据库方法添加文章
await this.articleData.addArticle(context, newArticle)
// 更新文章列表
this.articleList = await this.articleData.queryArticle(context)
// 清空输入框
this.titleInput = ''
this.contentInput = ''
})
// 显示文章列表
List({ space: 5 }) {
ForEach(this.articleList, (article: Article) => {
ListItem() {
Column() {
Text(`标题: ${article.title}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`内容: ${article.contentStr}`)
.fontSize(16)
Text(`点赞: ${article.likes} | 评论: ${article.comments} | 收藏: ${article.shares}`)
.fontSize(14)
.fontColor('#666')
}
.width('100%')
.padding(10)
.borderRadius(8)
.backgroundColor('#f5f5f5')
}
}, (article: Article) => article.id?.toString())
}
.width('100%')
}
.padding(10)
}
}