HarmonyOS鸿蒙Next中在开发应用时,如何设计一个优雅的数据服务层来封装数据库操作?
HarmonyOS鸿蒙Next中在开发应用时,如何设计一个优雅的数据服务层来封装数据库操作? 经常会遇见以下问题,怎样解决比较好:
- 数据层和业务层耦合严重
- CRUD操作代码重复
- 复杂查询难以实现
- 数据类型转换繁琐
- 批量操作性能差
3 回复
技术要点
- 数据服务层架构设计
- 单例模式应用
- SQL查询优化
- 批量操作事务处理
- 数据类型转换封装
完整实现代码
/**
* 数据服务层
* 提供业务数据的CRUD操作
*/
import { relationalStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import {
HumanRecord,
Person,
FilterCondition,
SortConfig,
StatisticsData,
PersonDetail,
RecordType
} from '../model/DataModels';
import { DatabaseManager } from '../database/DatabaseManager';
export class DataService {
private static instance: DataService;
private dbManager: DatabaseManager;
private constructor() {
this.dbManager = DatabaseManager.getInstance();
}
/**
* 获取单例实例
*/
public static getInstance(): DataService {
if (!DataService.instance) {
DataService.instance = new DataService();
}
return DataService.instance;
}
/**
* 初始化数据服务
*/
public async init(context: common.UIAbilityContext): Promise<void> {
await this.dbManager.initDatabase(context);
}
// ==================== 人物管理 ====================
/**
* 添加人物
*/
public async addPerson(person: Person): Promise<string> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const id = person.id || this.generateId();
const now = Date.now();
const insertSql = `
INSERT INTO lelv_persons
(id, name, relationship_type, relationship_tags, phone, avatar, contact_id, create_time, update_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
await store.executeSql(insertSql, [
id,
person.name,
person.relationshipType,
JSON.stringify(person.relationshipTags),
person.phone || null,
person.avatar || null,
person.contactId || null,
person.createTime || now,
now
]);
return id;
}
/**
* 更新人物信息
*/
public async updatePerson(id: string, person: Person): Promise<void> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const updateSql = `
UPDATE lelv_persons
SET name = ?, relationship_type = ?, relationship_tags = ?, phone = ?, avatar = ?, update_time = ?
WHERE id = ?
`;
await store.executeSql(updateSql, [
person.name,
person.relationshipType,
JSON.stringify(person.relationshipTags),
person.phone || null,
person.avatar || null,
Date.now(),
id
]);
}
/**
* 删除人物
*/
public async deletePerson(id: string): Promise<void> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
// 先删除相关的人情记录
await store.executeSql('DELETE FROM lelv_human_records WHERE person_id = ?', [id]);
// 再删除人物
await store.executeSql('DELETE FROM lelv_persons WHERE id = ?', [id]);
}
/**
* 获取所有人物
*/
public async getAllPersons(): Promise<Person[]> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const resultSet = await store.querySql('SELECT * FROM lelv_persons ORDER BY create_time DESC');
return this.parsePersonsFromResultSet(resultSet);
}
/**
* 根据ID获取人物
*/
public async getPersonById(id: string): Promise<Person | null> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const resultSet = await store.querySql('SELECT * FROM lelv_persons WHERE id = ?', [id]);
const persons = this.parsePersonsFromResultSet(resultSet);
return persons.length > 0 ? persons[0] : null;
}
// ==================== 人情记录管理 ====================
/**
* 添加人情记录
*/
public async addRecord(record: HumanRecord): Promise<string> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const id = record.id || this.generateId();
const now = Date.now();
const insertSql = `
INSERT INTO lelv_human_records
(id, type, event_type, custom_event_type, amount, event_time, person_id, location, remark, photos, custom_fields, create_time, update_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
await store.executeSql(insertSql, [
id,
record.type,
record.eventType,
record.customEventType || null,
record.amount,
record.eventTime,
record.personId,
record.location || null,
record.remark || null,
JSON.stringify(record.photos || []),
this.stringifyCustomFields(record.customFields),
record.createTime || now,
now
]);
return id;
}
/**
* 获取所有人情记录(支持筛选和排序)
*/
public async getAllRecords(filter?: FilterCondition, sort?: SortConfig): Promise<HumanRecord[]> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
let sql = `
SELECT hr.*, p.name as person_name, p.relationship_type, p.avatar
FROM lelv_human_records hr
LEFT JOIN lelv_persons p ON hr.person_id = p.id
WHERE 1=1
`;
const params: (string | number)[] = [];
// 应用筛选条件
if (filter) {
if (filter.timeRange) {
sql += ' AND hr.event_time BETWEEN ? AND ?';
params.push(filter.timeRange.start, filter.timeRange.end);
}
if (filter.eventTypes && filter.eventTypes.length > 0) {
const placeholders = filter.eventTypes.map(() => '?').join(',');
sql += ` AND hr.event_type IN (${placeholders})`;
params.push(...filter.eventTypes);
}
if (filter.relationshipTypes && filter.relationshipTypes.length > 0) {
const placeholders = filter.relationshipTypes.map(() => '?').join(',');
sql += ` AND p.relationship_type IN (${placeholders})`;
params.push(...filter.relationshipTypes);
}
if (filter.amountRange) {
sql += ' AND hr.amount BETWEEN ? AND ?';
params.push(filter.amountRange.min, filter.amountRange.max);
}
if (filter.keywords) {
sql += ' AND (hr.remark LIKE ? OR p.name LIKE ?)';
const keyword = `%${filter.keywords}%`;
params.push(keyword, keyword);
}
}
// 应用排序
if (sort) {
let orderField = 'hr.create_time';
switch (sort.field) {
case 'eventTime':
orderField = 'hr.event_time';
break;
case 'amount':
orderField = 'hr.amount';
break;
case 'name':
orderField = 'p.name';
break;
}
sql += ` ORDER BY ${orderField} ${sort.order.toUpperCase()}`;
} else {
sql += ' ORDER BY hr.create_time DESC';
}
const resultSet = await store.querySql(sql, params);
return this.parseRecordsFromResultSet(resultSet);
}
// ==================== 统计功能 ====================
/**
* 获取统计数据
*/
public async getStatistics(): Promise<StatisticsData> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
// 获取总收入
const receivedResult = await store.querySql(`
SELECT COALESCE(SUM(amount), 0) as total
FROM lelv_human_records
WHERE type = 'received'
`);
// 获取总支出
const sentResult = await store.querySql(`
SELECT COALESCE(SUM(amount), 0) as total
FROM lelv_human_records
WHERE type = 'sent'
`);
// 获取记录总数
const countResult = await store.querySql(`
SELECT COUNT(*) as count FROM lelv_human_records
`);
// 获取人物总数
const personCountResult = await store.querySql(`
SELECT COUNT(DISTINCT person_id) as count FROM lelv_human_records
`);
let totalReceived = 0;
if (receivedResult.goToFirstRow()) {
totalReceived = receivedResult.getDouble(0);
}
let totalSent = 0;
if (sentResult.goToFirstRow()) {
totalSent = sentResult.getDouble(0);
}
let recordCount = 0;
if (countResult.goToFirstRow()) {
recordCount = countResult.getLong(0);
}
let personCount = 0;
if (personCountResult.goToFirstRow()) {
personCount = personCountResult.getLong(0);
}
return {
totalReceived,
totalSent,
netAmount: totalReceived - totalSent,
recordCount,
personCount,
monthlyTrend: await this.getMonthlyTrend()
};
}
/**
* 获取个人往来详情
*/
public async getPersonDetail(personId: string): Promise<PersonDetail | null> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const person = await this.getPersonById(personId);
if (!person) return null;
// 获取该人物的所有记录
const resultSet = await store.querySql(`
SELECT * FROM lelv_human_records
WHERE person_id = ?
ORDER BY event_time DESC
`, [personId]);
const records = this.parseRecordsFromResultSet(resultSet);
// 计算统计数据
const totalReceived = records
.filter(r => r.type === RecordType.RECEIVED)
.reduce((sum, r) => sum + r.amount, 0);
const totalSent = records
.filter(r => r.type === RecordType.SENT)
.reduce((sum, r) => sum + r.amount, 0);
return {
person,
totalReceived,
totalSent,
netAmount: totalReceived - totalSent,
recordCount: records.length,
lastRecordTime: records.length > 0 ? records[0].eventTime : 0,
records
};
}
// ==================== 批量操作 ====================
/**
* 批量添加记录
*/
public async batchAddRecords(records: HumanRecord[]): Promise<string[]> {
const store = this.dbManager.getStore();
if (!store) throw new Error('数据库未初始化');
const ids: string[] = [];
const now = Date.now();
try {
// 开启事务
await store.beginTransaction();
for (const record of records) {
const id = this.generateId();
const insertSql = `
INSERT INTO lelv_human_records
(id, type, event_type, custom_event_type, amount, event_time, person_id, location, remark, photos, custom_fields, create_time, update_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
await store.executeSql(insertSql, [
id,
record.type,
record.eventType,
record.customEventType || null,
record.amount,
record.eventTime,
record.personId,
record.location || null,
record.remark || null,
JSON.stringify(record.photos),
this.stringifyCustomFields(record.customFields),
now,
now
]);
ids.push(id);
}
// 提交事务
await store.commit();
console.info(`批量添加记录成功: ${ids.length}条`);
return ids;
} catch (error) {
// 回滚事务
await store.rollback(void 0);
console.error('批量添加记录失败:', JSON.stringify(error));
throw new Error('批量添加记录失败');
}
}
// ==================== 工具方法 ====================
/**
* 生成唯一ID
*/
private generateId(): string {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
/**
* 解析人物数据
*/
private parsePersonsFromResultSet(resultSet: relationalStore.ResultSet): Person[] {
const persons: Person[] = [];
while (resultSet.goToNextRow()) {
persons.push({
id: resultSet.getString(0),
name: resultSet.getString(1),
relationshipType: resultSet.getString(2),
relationshipTags: JSON.parse(resultSet.getString(3) || '[]'),
phone: resultSet.getString(4),
avatar: resultSet.getString(5),
contactId: resultSet.getString(6),
createTime: resultSet.getLong(7),
updateTime: resultSet.getLong(8)
});
}
resultSet.close();
return persons;
}
/**
* 解析记录数据
*/
private parseRecordsFromResultSet(resultSet: relationalStore.ResultSet): HumanRecord[] {
const records: HumanRecord[] = [];
while (resultSet.goToNextRow()) {
const record: HumanRecord = {
id: resultSet.getString(0),
type: resultSet.getString(1) as RecordType,
eventType: resultSet.getString(2),
customEventType: resultSet.getString(3),
amount: resultSet.getDouble(4),
eventTime: resultSet.getLong(5),
personId: resultSet.getString(6),
location: resultSet.getString(7),
remark: resultSet.getString(8),
photos: JSON.parse(resultSet.getString(9) || '[]'),
customFields: this.parseCustomFields(resultSet.getString(10) || '{}'),
createTime: resultSet.getLong(11),
updateTime: resultSet.getLong(12)
};
// 尝试获取JOIN的关联字段
try {
record.personName = resultSet.getString(13);
record.relationshipType = resultSet.getString(14);
record.avatar = resultSet.getString(15);
} catch (error) {
// 如果没有JOIN字段,忽略
}
records.push(record);
}
resultSet.close();
return records;
}
/**
* 序列化自定义字段
*/
private stringifyCustomFields(customFields: Map<string, string> | Record<string, string> | undefined): string {
if (!customFields) return '[]';
if (customFields instanceof Map) {
return JSON.stringify(Array.from(customFields.entries()));
}
if (typeof customFields === 'object') {
return JSON.stringify(Object.entries(customFields));
}
return '[]';
}
/**
* 解析自定义字段
*/
private parseCustomFields(jsonStr: string): Map<string, string> {
try {
const entries: [string, string][] = JSON.parse(jsonStr);
const map = new Map<string, string>();
if (Array.isArray(entries)) {
entries.forEach(entry => map.set(entry[0], entry[1]));
}
return map;
} catch (error) {
return new Map<string, string>();
}
}
/**
* 获取月度趋势数据
*/
private async getMonthlyTrend(): Promise<any[]> {
const store = this.dbManager.getStore();
if (!store) return [];
const resultSet = await store.querySql(`
SELECT
strftime('%Y-%m', datetime(event_time/1000, 'unixepoch')) as month,
SUM(CASE WHEN type = 'received' THEN amount ELSE 0 END) as received,
SUM(CASE WHEN type = 'sent' THEN amount ELSE 0 END) as sent
FROM lelv_human_records
WHERE event_time >= ?
GROUP BY month
ORDER BY month DESC
LIMIT 12
`, [Date.now() - 365 * 24 * 60 * 60 * 1000]);
const trends: any[] = [];
while (resultSet.goToNextRow()) {
trends.push({
month: resultSet.getString(0),
received: resultSet.getDouble(1),
sent: resultSet.getDouble(2),
net: resultSet.getDouble(1) - resultSet.getDouble(2)
});
}
resultSet.close();
return trends;
}
}
核心设计思想
1. 分层架构
UI层 (Pages)
↓
业务服务层 (Services)
↓
数据服务层 (DataService) ← 本文重点
↓
数据库层 (DatabaseManager)
2. 单例模式
确保全局只有一个DataService实例,统一管理数据访问。
3. 统一的错误处理
if (!store) throw new Error('数据库未初始化');
4. 数据类型转换封装
- JSON序列化/反序列化
- Map与数组的相互转换
- ResultSet到业务对象的映射
最佳实践
1. 复杂查询使用LEFT JOIN
SELECT hr.*, p.name as person_name
FROM lelv_human_records hr
LEFT JOIN lelv_persons p ON hr.person_id = p.id
2. 参数化查询防止SQL注入
await store.querySql('SELECT * FROM persons WHERE id = ?', [id]);
3. 批量操作使用事务
await store.beginTransaction();
// ... 批量操作
await store.commit();
4. 记得关闭ResultSet
resultSet.close();
性能优化技巧
- 使用索引: 在常用查询字段上创建索引
- 减少JOIN: 简单查询避免不必要的JOIN
- 分页加载: 大数据量使用LIMIT和OFFSET
- 缓存策略: 频繁访问的数据使用内存缓存
避坑指南
更多关于HarmonyOS鸿蒙Next中在开发应用时,如何设计一个优雅的数据服务层来封装数据库操作?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,可通过Data Ability Kit封装数据库操作。使用关系型数据库(RDB)或对象关系映射(ORM)框架管理数据,结合DataShare Extension Ability实现跨应用数据共享。设计时采用分层架构:数据访问层负责CRUD操作,业务逻辑层处理数据转换,服务层提供统一API。利用HarmonyOS分布式特性,通过分布式数据对象实现设备间数据同步。确保数据操作异步化,避免阻塞UI线程。
在HarmonyOS Next中设计优雅的数据服务层,建议采用以下架构方案:
- 分层设计
- 使用Repository模式隔离数据源与业务逻辑
- 定义清晰的Entity数据实体和Domain领域模型
- 通过Mapper实现Entity与Domain对象转换
- 统一数据访问接口
- 封装BaseRepository提供通用CRUD操作
- 使用泛型约束数据类型,避免重复代码
- 示例:
abstract class BaseRepository<T> {
abstract create(item: T): Promise<void>;
abstract update(item: T): Promise<void>;
abstract delete(id: string): Promise<void>;
abstract findById(id: string): Promise<T | null>;
}
- 复杂查询处理
- 使用Specification模式封装查询条件
- 实现QueryBuilder构建动态查询
- 支持分页、排序等通用查询需求
- 数据类型转换
- 在Mapper层集中处理类型转换
- 使用工厂模式创建领域对象
- 利用TypeScript类型约束确保类型安全
- 批量操作优化
- 实现BatchExecutor处理批量写入
- 使用事务保证数据一致性
- 通过连接池管理数据库连接
- 异步处理
- 统一使用Promise/async-await处理异步操作
- 实现重试机制应对临时故障
这种架构能有效解耦数据层与业务层,提高代码复用性,同时保持良好性能。

