重庆企业服务建站网站开发,网站一般建什么,怎么做qq代刷网站,个人网页怎么做个人首页#xff1a; VON 鸿蒙系列专栏#xff1a; 鸿蒙开发小型案例总结 综合案例 #xff1a;鸿蒙综合案例开发 鸿蒙6.0#xff1a;从0开始的开源鸿蒙6.0.0 鸿蒙5.0#xff1a;鸿蒙5.0零基础入门到项目实战 Electron适配开源鸿蒙专栏#xff1a;Electron for Open…个人首页 VON鸿蒙系列专栏 鸿蒙开发小型案例总结综合案例 鸿蒙综合案例开发鸿蒙6.0从0开始的开源鸿蒙6.0.0鸿蒙5.0鸿蒙5.0零基础入门到项目实战Electron适配开源鸿蒙专栏Electron for OpenHarmonyFlutter 适配开源鸿蒙专栏Flutter for OpenHarmony本文所属专栏鸿蒙综合案例开发本文atomgit地址小V健身小V健身助手开发手记五数据库架构设计与实现详解一、为什么选择关系型数据库二、数据模型设计从业务对象到数据库表1. 业务对象RecordPO2. 数据库表结构三、列映射机制ColumnInfo 与类型安全四、通用数据记录容器DataRecord五、数据库工具类DBUtil单例 封装1. 初始化数据库2. 建表3. CRUD 操作封装插入Insert查询Query六、业务模型层RecordModel七、辅助模块运动项管理与首选项八、潜在问题与改进建议九 功能展示十、总结数据库架构设计与实现详解目前项目已经移植到坚果派可以通过坚果派直接访问项目传送门在小V健身助手的开发过程中数据持久化是支撑整个应用功能运转的核心模块。无论是用户每日打卡记录、运动项目管理还是历史数据统计分析都离不开一套稳定、高效、可维护的本地数据库系统。本文将深入剖析我们在小V健身助手中采用的数据库设计方案从表结构定义、ORM映射、CRUD操作封装到数据库初始化流程和工具类抽象全面解读其技术实现细节。一、为什么选择关系型数据库小V健身助手运行在HarmonyOS平台基于ArkTS语言开发。HarmonyOS提供了多种本地存储方案包括Preferences轻量级键值对存储适合配置项Relational DatabaseRDBSQLite兼容的关系型数据库适合结构化数据Distributed Data ObjectDDM用于跨设备同步。考虑到我们的核心数据——运动记录如跳绳次数、跑步时长等具有明确的字段结构、需要支持复杂查询如“按日期范围查询”、且可能随时间增长形成大量记录我们最终选择了Relational DatabaseRDB作为主存储引擎。✅优势支持事务、索引、约束可高效执行条件查询、分组、排序ArkTS 提供了完善的relationalStoreAPI 封装。二、数据模型设计从业务对象到数据库表1. 业务对象RecordPO首先我们定义了代表一条运动记录的业务对象RecordPOPersistent ObjectexportdefaultclassRecordPO{id?:number;// 主键自增keepId:number0;// 关联的运动项IDamount:number0;// 计划完成量如跳绳1000次createTime?:number;// 记录创建时间时间戳successAmount:number0;// 实际完成量}该类完全对应一条数据库记录字段命名采用驼峰式符合TS规范而数据库列名则使用下划线风格符合SQL惯例。2. 数据库表结构根据RecordPO我们设计了如下建表语句constCREATE_TABLE_SQL:string( id INTEGER PRIMARY KEY AUTOINCREMENT, keep_id INTEGER NOT NULL, amount INTEGER NOT NULL, create_time INTEGER NOT NULL, success_amount INTEGER NOT NULL );表名为recode注此处应为record属笔误但不影响功能所有字段均为NOT NULL确保数据完整性id为主键并自动递增时间以毫秒时间戳INTEGER存储便于跨平台处理。注意虽然amount在RecordPO中是number类型但在数据库中我们统一用INTEGER存储。若未来需支持小数如公里数可改为REAL。三、列映射机制ColumnInfo 与类型安全为了在业务对象与数据库列之间建立可靠映射我们引入了ColumnInfo接口和ColumnType枚举exportinterfaceColumnInfo{name:string;// RecordPO 中的属性名如 keepIdcolumnName:string;// 数据库列名如 keep_idtype:ColumnType;// 列的数据类型}exportenumColumnType{LONG,DOUBLE,STRING,BLOB}并定义了具体的列映射数组constCOLUMNS:ColumnInfo[][{name:id,columnName:id,type:ColumnType.LONG},{name:keepId,columnName:keep_id,type:ColumnType.LONG},{name:amount,columnName:amount,type:ColumnType.DOUBLE},// 注意此处设为DOUBLE但建表用INTEGER存在不一致{name:createTime,columnName:create_time,type:ColumnType.LONG},{name:successAmount,columnName:success_amount,type:ColumnType.LONG}];⚠️潜在问题amount在COLUMNS中被标记为DOUBLE但建表 SQL 使用INTEGER。这可能导致读取时类型转换异常。建议统一为LONG或根据实际需求调整。此设计实现了解耦业务层无需关心数据库列名只需通过ColumnInfo配置即可完成自动映射。四、通用数据记录容器DataRecord由于 ArkTS 的ValuesBucket要求键为字符串、值为基本类型我们封装了一个通用的DataRecord类exportclassDataRecord{privatedata:Mapstring,number|string|boolean|Uint8Array|null|undefinednewMap();setValue(key:string,value:any):void{this.data.set(key,value);}getValue(key:string):any{returnthis.data.get(key);}hasValue(key:string):boolean{constvaluethis.data.get(key);returntypeofvalue!undefinedvalue!null;}}它作为中间层将RecordPO转换为可被数据库操作的格式避免直接暴露底层ValuesBucket。五、数据库工具类DBUtil单例 封装DBUtil是整个数据库操作的核心枢纽采用单例模式确保全局唯一实例classDBUtil{privaterdbStore!:relationalStore.RdbStore;privatestaticinstance:DBUtil|nullnull;privateconstructor(){}staticgetInstance():DBUtil{if(!DBUtil.instance){DBUtil.instancenewDBUtil();}returnDBUtil.instance;}}1. 初始化数据库通过initDB方法在应用启动时绑定上下文并打开数据库initDB(context:common.UIAbilityContext):Promisevoid{constconfig:relationalStore.StoreConfig{name:Small_V_Health.db,securityLevel:1,// 安全等级};returnrelationalStore.getRdbStore(context,config).then(rdbStore{this.rdbStorerdbStore;});}2. 建表提供通用的createTable方法createTable(createSQL:string):Promisevoid{returnthis.rdbStore.executeSql(CREATE TABLE IF NOT EXISTS recode${createSQL});} 建议表名应作为参数传入或从常量读取避免硬编码。3. CRUD 操作封装插入Insertinsert(tableName:string,obj:DataRecord,columns:ColumnInfo[]):Promisenumber{constvaluethis.buildValueBucket(obj,columns);returnnewPromise((resolve,reject){this.rdbStore.insert(tableName,value,(err,id){err?reject(err):resolve(id);});});}其中buildValueBucket负责将DataRecord转为ValuesBucketbuildValueBucket(obj:DataRecord,columns:ColumnInfo[]):relationalStore.ValuesBucket{constvalue:relationalStore.ValuesBucket{};columns.forEach(info{if(obj.hasValue(info.name)){value[info.columnName]obj.getValue(info.name);}});returnvalue;}查询Query查询是最复杂的部分需将ResultSet转回DataRecord数组queryForList(predicates:RdbPredicates,columns:ColumnInfo[]):PromiseDataRecord[]{returnnewPromise((resolve,reject){this.rdbStore.query(predicates,columns.map(cc.columnName),(err,result){if(err)reject(err);else{try{constrecordsthis.parseResultSet(result,columns);resolve(records);}finally{result.close();// 必须关闭结果集防止内存泄漏}}});});}parseResultSet方法逐行解析parseResultSet(result:ResultSet,columns:ColumnInfo[]):DataRecord[]{constarr:DataRecord[][];if(result.rowCount0)returnarr;result.goToFirstRow();while(!result.isAtLastRow){constrecordthis.extractRow(result,columns);arr.push(record);result.goToNextRow();}// 处理最后一行API设计缺陷isAtLastRow 不包含最后一行if(result.rowCount0){constrecordthis.extractRow(result,columns);arr.push(record);}returnarr;}privateextractRow(result:ResultSet,columns:ColumnInfo[]):DataRecord{constrecordnewDataRecord();columns.forEach(info{constidxresult.getColumnIndex(info.columnName);letval:any;switch(info.type){caseColumnType.LONG:valresult.getLong(idx);break;caseColumnType.DOUBLE:valresult.getDouble(idx);break;caseColumnType.STRING:valresult.getString(idx);break;caseColumnType.BLOB:valresult.getBlob(idx);break;default:valnull;}record.setValue(info.name,val);});returnrecord;}关键点HarmonyOS 的ResultSet遍历逻辑较为特殊需手动处理“最后一行”这是官方 API 的一个常见陷阱。六、业务模型层RecordModel在DBUtil之上我们构建了RecordModel提供面向业务的接口classRecordModel{// 表常量privatereadonlyTABLE_NAMErecode;privatereadonlyID_COLUMNid;privatereadonlyDATE_COLUMNcreate_time;insert(record:RecordPO):Promisenumber{constdataRecordnewDataRecord();dataRecord.setValue(id,record.id);dataRecord.setValue(keepId,record.keepId);// ... 其他字段returnDBUtil.insert(this.TABLE_NAME,dataRecord,COLUMNS);}asyncqueryByDate(date:number):PromiseRecordPO[]{constpredicatesnewRdbPredicates(this.TABLE_NAME);conststartOfDaydate;constendOfDaydate24*60*60*1000-1;predicates.between(this.DATE_COLUMN,startOfDay,endOfDay);constdataRecordsawaitDBUtil.queryForList(predicates,COLUMNS);returndataRecords.map(dr{constponewRecordPO();po.iddr.getValue(id)asnumber;po.keepIddr.getValue(keepId)asnumber;// ... 赋值其他字段returnpo;});}// delete / update 略...}这种分层设计使得业务层只与RecordPO和RecordModel交互数据访问层DBUtil完全屏蔽了 SQL 和底层 API 细节可测试性强可 mockRecordModel返回模拟数据。七、辅助模块运动项管理与首选项除了核心记录表我们还维护了一个静态的运动项列表constkeeps:RecordItem[][newRecordItem(0,跳绳,$r(app.media.home_ic_swimming),/小时,600),// ...];并通过ItemModel提供查询classItemModel{getById(id:number){returnkeeps[id];}list(){returnkeeps;}}优化建议未来可将keeps存入数据库支持用户自定义运动项目。同时使用PreferenceUtil管理用户设置如首次启动状态、目标提醒等其基于ohos.data.preferences实现采用单例异步加载模式确保线程安全。八、潜在问题与改进建议尽管当前架构已满足 MVP 需求但仍存在可优化空间问题建议表名recode拼写错误改为record并添加数据库版本迁移逻辑amount类型不一致INTEGER vs DOUBLE统一为REAL或明确业务含义RecordPO字段未校验非空添加构造函数或 Builder 模式查询遍历逻辑冗余封装通用ResultSettoT[]工具缺少索引对create_time和keep_id添加索引提升查询性能无事务支持在批量插入/更新时启用事务九 功能展示十、总结小V健身助手的数据库模块通过分层架构 类型安全映射 单例工具封装实现了高内聚、低耦合的设计目标。从RecordPO到DataRecord再到ValuesBucket和ResultSet每一步转换都经过精心抽象既保证了代码可读性又提升了可维护性。这套方案不仅适用于健身记录场景也可作为 HarmonyOS 应用本地数据库开发的参考模板。未来我们将引入数据库版本管理、加密存储、以及云端同步能力进一步提升数据安全与用户体验。代码即文档架构即承诺。在小V健身助手的演进之路上稳健的数据基石是我们交付可靠体验的底气所在。附关键常量与类型定义汇总// 表名与列名constTABLE_NAMErecode;constID_COLUMNid;constDATE_COLUMNcreate_time;// 列信息constCOLUMNS:ColumnInfo[][{name:id,columnName:id,type:ColumnType.LONG},{name:keepId,columnName:keep_id,type:ColumnType.LONG},{name:amount,columnName:amount,type:ColumnType.DOUBLE},{name:createTime,columnName:create_time,type:ColumnType.LONG},{name:successAmount,columnName:success_amount,type:ColumnType.LONG}];// 建表语句constCREATE_TABLE_SQL( ... );通过以上设计小V健身助手得以在用户每一次点击“完成”按钮时默默而可靠地将汗水转化为数据为健康生活留下数字足迹。