HarmonyOS鸿蒙Next中通过继承机制模仿后端优雅的实现CRUD
HarmonyOS鸿蒙Next中通过继承机制模仿后端优雅的实现CRUD 通过继承机制模仿后端优雅的实现CRUD
实现思路:
在后端开发中,有很多开源的CRUD框架,在鸿蒙Arkts中,我们通过继承的方式,进行模版化开发,通过固定的模版再加上AI,能够快速提升代码的生成效率;
具体如下,定义一个数据表的基础接口 IBasePO:

通过抽象AbstractBasePO实现接口:

具体的PO去实现这个抽象类,后续通过这个规范,用AI根据表结构设计批量生成PO

接下来开始实现DAO层(后端开发做久了,什么都想分层处理😭)
定义基础的接口:

然后通过抽象类来实现这些接口:

为了解决Arkts没有重载和反射这些后端特性,我们做了以上的处理,也是为了方便后续通过AI进行优化的代码批量生成。
接下来开始写业务Dao层:

通过继承抽象类,所有的基础增删改查都不用重复去写代码了,后续抽象类可以通过实现更多的通用查询,来减少业务DAO层的重复代码。(代码强迫症,如果有更好的实现方式或特性,记得留言告诉我)
完整的代码示例:
/**
* @author: 萌狼
* @date: 2025-11-12
* @version: 2.0.0
* @description: 文档库数据访问层
*
* 职责:
* - 纯数据库操作(CRUD)
* - 不包含业务逻辑
* - 不管理事务(由 Service 层管理)
* - 不记录变更日志(由 Service 层管理)
*/
export class LibraryDao extends AbstractBaseDao<LibraryPO> {
/**
* 获取表名
* @returns 数据库表名
*/
protected getTableName(): string {
return 'U_LIBRARY';
}
}
/**
* @author: 萌狼
* @date: 2025-11-02
* @version: 1.0.0
* @description:
*
* ArkTS 设计说明:
* 由于 ArkTS 不支持函数重载,且静态属性无法通过泛型获取,
* 因此采用抽象方法 getTableName() 让子类提供表名。
* 这样既避免了每个方法都传递实例对象,又保持了代码的优雅性。
*/
export abstract class AbstractBaseDao<T extends AbstractBasePO> implements IBaseDao<T> {
/**
* 插入数据
* @param po
* @returns 返回行ID
*/
insert(po: T): number {
let result = 0;
const valueBucket: relationalStore.ValuesBucket = po.toInsertRecord();
try {
result = RdbStoreUtil.getRdbStore()?.insertSync(this.getTableName(), valueBucket) ?? 0;
} catch (err) {
console.error(`[AbstractBaseDao] insert failed: ${err}`);
}
return result;
}
/**
* 更新数据
* @param id 记录ID
* @param po 持久化对象
* @author 萌狼
*/
update(po: T): number {
let result = 0;
let predicates = new relationalStore.RdbPredicates(this.getTableName());
predicates.equalTo('ID', po.id);
if (po.isUserFilter()) {
predicates.equalTo('USER_ID', po.userId);
}
try {
result = RdbStoreUtil.getRdbStore()?.updateSync(po.toUpdateRecord(), predicates) ?? 0;
} catch (err) {
//Logger.error(EventLogCode.EL80020012, err.code, err.message);
}
return result;
}
/**
* 根据ID查询记录
*
* @param id 记录ID
* @returns T | null 查询结果,不存在返回 null
*/
findById(id: string): T | null {
try {
const predicates = new relationalStore.RdbPredicates(this.getTableName());
predicates.equalTo('ID', id);
const resultSet = RdbStoreUtil.getRdbStore()?.querySync(predicates);
if (resultSet && resultSet.goToFirstRow()) {
const mapResult = this.mapResultSetToPO(resultSet);
const po: T = JSON.parse(JSON.stringify(mapResult));
resultSet.close();
return po;
}
if (resultSet) {
resultSet.close();
}
return null;
} catch (error) {
console.error(`[AbstractBaseDao] findById failed: ${error}`);
return null;
}
}
/**
* 查询所有数据
* @returns T[] 查询结果列表
* @author 萌狼
*/
findAll(): T[] {
const resultList: Array<T> = [];
try {
const predicates = new relationalStore.RdbPredicates(this.getTableName());
predicates.equalTo('IS_DELETED', 0);
predicates.equalTo('USER_ID', AppContextUtil.getUserId());
const resultSet = RdbStoreUtil.getRdbStore()?.querySync(predicates);
if (resultSet) {
while (resultSet.goToNextRow()) {
const mapResult = this.mapResultSetToPO(resultSet);
const po: T = JSON.parse(JSON.stringify(mapResult));
resultList.push(po);
}
resultSet.close();
}
} catch (error) {
console.error(`[AbstractBaseDao] findAll failed: ${error}`);
}
return resultList;
}
/**
* 获取表名
* 子类必须实现此方法,返回对应的数据库表名
* @returns 数据库表名
*/
protected abstract getTableName(): string;
/**
* 获取自定义字段映射(可选)
*
* 子类可以覆盖此方法,提供自定义的数据库字段名到PO字段名的映射
* 如果返回 null,则使用默认的 dbColumnToCamelCase 自动转换
*
* @returns Record<string, string> | null
* - key: 数据库字段名(如 'RESOURCE_ID')
* - value: PO字段名(如 'resourceId')
*
* @example
* protected getFieldMapping(): Record<string, string> | null {
* return {
* 'RESOURCE_ID': 'resourceId',
* 'USER_ID': 'userId'
* };
* }
*/
protected getFieldMapping(): Record<string, string> | null {
return null; // 默认使用自动转换
}
/**
* 数据库字段名转驼峰命名
*
* 转换规则:
* - RESOURCE_ID → resourceId
* - USER_ID → userId
* - ID → id
* - CREATED_TIME → createdTime
*
* @param columnName 数据库字段名(大写+下划线)
* @returns 驼峰命名的字段名
*/
protected dbColumnToCamelCase(columnName: string): string {
return columnName
.toLowerCase() // RESOURCE_ID → resource_id
.split('_') // ['resource', 'id']
.map((word, index) => {
if (index === 0) {
return word; // 第一个单词保持小写
}
return word.charAt(0).toUpperCase() + word.slice(1); // 首字母大写
})
.join(''); // resourceId
}
/**
* 将数据库记录转换为PO对象
*
* 优先使用自定义映射 getFieldMapping(),
* 如果没有自定义映射,则使用自动转换 dbColumnToCamelCase()
*
* @param resultSet 查询结果集
* @returns Record<string, ValueType> PO字段映射
*/
protected mapResultSetToPO(resultSet: relationalStore.ResultSet): Record<string, relationalStore.ValueType> {
const columnNames = resultSet.columnNames;
const mapResult: Record<string, relationalStore.ValueType> = {};
const customMapping = this.getFieldMapping();
for (let i = 0; i < columnNames.length; i++) {
const dbColumn = columnNames[i];
const poField = customMapping && customMapping[dbColumn] ?
customMapping[dbColumn] :
this.dbColumnToCamelCase(dbColumn);
mapResult[poField] = resultSet.getValue(resultSet.getColumnIndex(dbColumn));
}
return mapResult;
}
}
/**
* @author: 萌狼
* @date: 2025-11-01
* @version: 1.0.0
* @description:
*/
export interface IBaseDao<T extends AbstractBasePO> {
/**
* 插入数据
* @param po
* @returns 返回行ID
*/
insert(po: T): number;
/**
* 更新数据
* @param id 记录ID
* @param po 持久化对象
* @author 萌狼
*/
update(po: T): number;
/**
* 根据ID查询记录
*
* @param id 记录ID
* @returns T | null 查询结果,不存在返回 null
*/
findById(id: string): T | null;
}
总结:
代码还有很多可以改进的地方,后续会持续去优化,如果反馈的人数多,会考虑做成一个开源框架,让大家更多的从关注业务数据库层转为关注业务服务层,如果Arkts能够支持反射就好了,很多代码都可以通过反射去处理,包括JSON格式可以通过注解来实现序列化和反序列化,如果原生已经支持的话记得踢一脚博主哈。
为什么会想去实现这一套,是因为通过模版化的开发,我们后面就可以很简单的用AI来帮我们做这种模版化开发,包括批量生成PO、DAO层的代码,特性化的可以自己去实现。
更多关于HarmonyOS鸿蒙Next中通过继承机制模仿后端优雅的实现CRUD的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
不错👍👍👍,给个小小的建议。可以封装一下然后发布到 ohpm 三方库里去,这样更方便使用。
你这是自问自答了吗
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17
活动举办方要求自问自答的格式🤣,
在HarmonyOS Next中,可通过继承基类实现CRUD。定义抽象基类如BaseDao<T>,封装通用数据库操作方法。子类继承并指定泛型类型,实现具体数据模型的操作。使用ArkTS编写,利用系统提供的数据库API进行数据持久化。这种方式减少重复代码,提升开发效率。
在HarmonyOS Next中,通过继承机制实现CRUD操作是一种符合ArkTS面向对象特性的设计模式,可以提升代码复用性和结构清晰度。以下是一种典型的实现思路:
1. 定义基础数据模型
// BaseModel.ets
export class BaseModel {
id?: string; // 通用ID字段
createdAt?: number; // 创建时间戳
updatedAt?: number; // 更新时间戳
}
2. 创建通用Repository基类
// BaseRepository.ets
export abstract class BaseRepository<T extends BaseModel> {
protected abstract tableName: string;
// 通用CRUD方法
async create(item: T): Promise<boolean> {
// 统一设置时间戳
const now = Date.now();
item.createdAt = now;
item.updatedAt = now;
// 实际数据库操作
return await this.saveToDB(item);
}
async update(id: string, updates: Partial<T>): Promise<boolean> {
updates.updatedAt = Date.now();
return await this.updateInDB(id, updates);
}
async delete(id: string): Promise<boolean> {
return await this.deleteFromDB(id);
}
async findById(id: string): Promise<T | null> {
return await this.queryFromDB(id);
}
// 抽象方法由子类实现具体存储逻辑
protected abstract saveToDB(item: T): Promise<boolean>;
protected abstract updateInDB(id: string, updates: Partial<T>): Promise<boolean>;
protected abstract deleteFromDB(id: string): Promise<boolean>;
protected abstract queryFromDB(id: string): Promise<T | null>;
}
3. 具体业务Repository实现
// UserRepository.ets
import { BaseRepository } from './BaseRepository';
import { User } from '../model/User';
export class UserRepository extends BaseRepository<User> {
protected tableName: string = 'users';
// 实现具体的数据库操作
protected async saveToDB(user: User): Promise<boolean> {
// 使用HarmonyOS DataAbility或RDB具体实现
const predicates = new dataRdb.RdbPredicates(this.tableName);
// ... 具体插入逻辑
return true;
}
protected async updateInDB(id: string, updates: Partial<User>): Promise<boolean> {
// ... 更新逻辑实现
return true;
}
// 可以添加业务特有方法
async findByEmail(email: string): Promise<User | null> {
// 特定查询逻辑
return null;
}
}
4. 业务模型定义
// User.ets
import { BaseModel } from './BaseModel';
export class User extends BaseModel {
username: string = '';
email: string = '';
age?: number;
constructor(username: string, email: string) {
super();
this.username = username;
this.email = email;
}
}
5. 使用示例
// 业务层使用
const userRepo = new UserRepository();
const newUser = new User('john_doe', 'john@example.com');
// 创建用户
await userRepo.create(newUser);
// 查询用户
const user = await userRepo.findById('123');
// 更新用户
await userRepo.update('123', { age: 30 });
// 特有查询
const userByEmail = await userRepo.findByEmail('john@example.com');
优势:
- 代码复用:通用CRUD逻辑在基类中一次实现
- 类型安全:ArkTS泛型确保类型一致性
- 易于扩展:新增实体只需继承基类
- 维护简单:数据库操作变更只需修改基类或具体实现
注意事项:
- 根据实际存储方案(RDB、对象存储等)调整具体实现
- 考虑异步操作的状态管理和错误处理
- 复杂查询场景可能需要扩展基类接口
这种模式特别适合HarmonyOS Next中需要本地数据持久化的应用场景,通过良好的分层设计保持代码的可维护性和可测试性。

