问题描述
在 HarmonyOS 应用开发中,如何使用 RelationalStore 实现数据库的增删改查(CRUD)操作?很多开发者在使用关系型数据库时遇到以下问题:
- 不清楚如何正确初始化数据库
- 不知道如何设计 Dao 层
- 查询结果集如何转换为对象
- 如何处理异步操作
解决方案
1. 技术原理
RelationalStore 是 HarmonyOS 提供的关系型数据库解决方案,基于 SQLite 实现。核心流程:
DatabaseHelper(单例) → 初始化RdbStore → 创建表结构 → Dao层操作
2. 完整实现代码
步骤 1: 创建数据库管理类
import relationalStore from '@ohos.data.relationalStore';
export class DatabaseHelper {
private static instance: DatabaseHelper;
private rdbStore: relationalStore.RdbStore | null = null;
private readonly DB_NAME: string = 'app_database.db';
private readonly DB_VERSION: number = 1;
private constructor() {}
/**
* 获取单例
*/
static getInstance(): DatabaseHelper {
if (!DatabaseHelper.instance) {
DatabaseHelper.instance = new DatabaseHelper();
}
return DatabaseHelper.instance;
}
/**
* 初始化数据库
*/
async init(context: Context): Promise<void> {
const config: relationalStore.StoreConfig = {
name: this.DB_NAME,
securityLevel: relationalStore.SecurityLevel.S1
};
this.rdbStore = await relationalStore.getRdbStore(context, config);
await this.createTables();
}
/**
* 创建表结构
*/
private async createTables(): Promise<void> {
const createTableSql = `
CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
nickname TEXT,
email TEXT,
create_time INTEGER,
update_time INTEGER
)
`;
await this.rdbStore!.executeSql(createTableSql);
}
/**
* 获取数据库实例
*/
getStore(): relationalStore.RdbStore {
if (!this.rdbStore) {
throw new Error('Database not initialized');
}
return this.rdbStore;
}
}
步骤 2: 创建数据模型
export class User {
id: number = 0;
username: string = '';
nickname: string = '';
email: string = '';
createTime: number = 0;
updateTime: number = 0;
/**
* 从数据库记录创建对象
*/
static fromDb(record: Record<string, Object>): User {
const user = new User();
user.id = record['id'] as number;
user.username = record['username'] as string;
user.nickname = record['nickname'] as string;
user.email = record['email'] as string;
user.createTime = record['create_time'] as number;
user.updateTime = record['update_time'] as number;
return user;
}
}
步骤 3: 实现 Dao 层
import relationalStore from '@ohos.data.relationalStore';
import { User } from '../models/User';
import { DatabaseHelper } from './DatabaseHelper';
const TABLE_NAME = 'user';
export class UserDao {
private dbHelper: DatabaseHelper;
constructor() {
this.dbHelper = DatabaseHelper.getInstance();
}
/**
* 插入用户
*/
async insert(user: User): Promise<number> {
const store = this.dbHelper.getStore();
const valueBucket: relationalStore.ValuesBucket = {
username: user.username,
nickname: user.nickname,
email: user.email,
create_time: Date.now(),
update_time: Date.now()
};
return await store.insert(TABLE_NAME, valueBucket);
}
/**
* 更新用户
*/
async update(user: User): Promise<number> {
const store = this.dbHelper.getStore();
const valueBucket: relationalStore.ValuesBucket = {
nickname: user.nickname,
email: user.email,
update_time: Date.now()
};
const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
predicates.equalTo('id', user.id);
return await store.update(valueBucket, predicates);
}
/**
* 根据ID查询
*/
async findById(id: number): Promise<User | null> {
const store = this.dbHelper.getStore();
const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
predicates.equalTo('id', id);
const resultSet = await store.query(predicates);
let user: User | null = null;
if (resultSet.goToFirstRow()) {
const record = this.resultSetToRecord(resultSet);
user = User.fromDb(record);
}
resultSet.close();
return user;
}
/**
* 查询所有用户
*/
async findAll(): Promise<User[]> {
const store = this.dbHelper.getStore();
const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
const resultSet = await store.query(predicates);
const users: User[] = [];
while (resultSet.goToNextRow()) {
const record = this.resultSetToRecord(resultSet);
users.push(User.fromDb(record));
}
resultSet.close();
return users;
}
/**
* 删除用户
*/
async delete(id: number): Promise<number> {
const store = this.dbHelper.getStore();
const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
predicates.equalTo('id', id);
return await store.delete(predicates);
}
/**
* 将ResultSet转换为记录对象
*/
private resultSetToRecord(resultSet: relationalStore.ResultSet): Record<string, Object> {
const record: Record<string, Object> = {};
const columnNames = resultSet.columnNames;
for (const name of columnNames) {
const index = resultSet.getColumnIndex(name);
const type = resultSet.getColumnType(index);
switch (type) {
case relationalStore.ColumnType.TYPE_INTEGER:
record[name] = resultSet.getLong(index);
break;
case relationalStore.ColumnType.TYPE_STRING:
record[name] = resultSet.getString(index);
break;
case relationalStore.ColumnType.TYPE_FLOAT:
record[name] = resultSet.getDouble(index);
break;
default:
record[name] = resultSet.getString(index);
}
}
return record;
}
}
步骤 4: 在 EntryAbility 中初始化
import { DatabaseHelper } from '../database/DatabaseHelper';
export default class EntryAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
// 初始化数据库
await DatabaseHelper.getInstance().init(this.context);
}
}
步骤 5: 使用示例
import { UserDao } from '../database/UserDao';
import { User } from '../models/User';
// 创建用户
const userDao = new UserDao();
const user = new User();
user.username = 'zhangsan';
user.nickname = '张三';
user.email = 'zhangsan@example.com';
const userId = await userDao.insert(user);
console.log('插入用户ID:', userId);
// 查询用户
const foundUser = await userDao.findById(userId);
console.log('查询到用户:', foundUser?.nickname);
// 更新用户
if (foundUser) {
foundUser.nickname = '张三三';
await userDao.update(foundUser);
}
// 查询所有用户
const allUsers = await userDao.findAll();
console.log('用户总数:', allUsers.length);
// 删除用户
await userDao.delete(userId);
3. 运行效果
[日志] 插入用户ID: 1
[日志] 查询到用户: 张三
[日志] 用户总数: 1
[日志] 删除成功
关键要点
1. 单例模式
DatabaseHelper 使用单例模式,确保全局只有一个数据库实例,避免资源浪费。
2. 异步操作
所有数据库操作都是异步的,使用 async/await 确保数据一致性。
3. ResultSet 处理
- 必须调用
goToFirstRow()或goToNextRow()移动游标 - 使用完毕后必须调用
close()释放资源 - 根据列类型使用对应的 getter 方法
4. ValuesBucket
插入和更新操作使用 ValuesBucket 传递数据,字段名必须与表结构一致。
5. RdbPredicates
查询和删除操作使用 RdbPredicates 构建条件,支持链式调用:
predicates
.equalTo('status', 1)
.and()
.greaterThan('age', 18)
.orderByAsc('create_time');
常见问题
Q1: 如何处理数据库初始化失败?
async init(context: Context): Promise<void> {
try {
const config: relationalStore.StoreConfig = {
name: this.DB_NAME,
securityLevel: relationalStore.SecurityLevel.S1
};
this.rdbStore = await relationalStore.getRdbStore(context, config);
await this.createTables();
} catch (err) {
console.error('数据库初始化失败:', JSON.stringify(err));
throw err;
}
}
Q2: 如何实现分页查询?
async findByPage(page: number, pageSize: number): Promise<User[]> {
const store = this.dbHelper.getStore();
const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
predicates.limitAs(pageSize).offsetAs((page - 1) * pageSize);
const resultSet = await store.query(predicates);
const users: User[] = [];
while (resultSet.goToNextRow()) {
const record = this.resultSetToRecord(resultSet);
users.push(User.fromDb(record));
}
resultSet.close();
return users;
}
Q3: 如何执行复杂 SQL 查询?
async customQuery(sql: string, args: Array<string | number>): Promise<any[]> {
const store = this.dbHelper.getStore();
const resultSet = await store.querySql(sql, args);
const results: any[] = [];
while (resultSet.goToNextRow()) {
results.push(this.resultSetToRecord(resultSet));
}
resultSet.close();
return results;
}
总结
本文展示了 HarmonyOS 中 RelationalStore 的完整 CRUD 实现方案:
- ✅ 单例模式管理数据库实例
- ✅ 规范的 Dao 层设计
- ✅ 类型安全的数据转换
- ✅ 完善的错误处理
- ✅ 支持复杂查询