发布时间:2026/8/16 3:51:13
把心事存进鸿蒙:ArkTS 为日记本设计长文本表与时间戳字段 实例电子日记本Diary技术长文本存储、时间分组查询、关键词搜索一、业务需求分析日记本的数据形态与记账本有何不同前三个实例我们处理了「任务清单」短文本状态、「通讯录」结构化字段和「记账本」数值时间。到了实例 4「电子日记本」数据形态发生了一次质变主体内容从「字段」变成了「长文本」。日记应用的核心业务需求可以归纳为五点写日记标题 正文可能上千字的长文本 心情 天气 地点一次保存按时间轴浏览日记天生是「按时间组织的个人编年史」页面要按日期/月份分组展示时间线是主视觉关键词搜索用户想「找那天写日记提到爬山的记录」需要标题/正文双字段模糊搜索编辑与删除日记写错了要能改、能删轻量统计总篇数、本月篇数支撑页面头部信息。对比记账本日记本的数据特点维度记账本实例 3日记本实例 4主数据金额 REAL 分类 TEXT正文长文本 TEXT时间角色统计维度SUM/GROUP BY组织维度时间轴分组检索方式分类精确匹配关键词 LIKE 模糊更新频率几乎不更新删了重记经常编辑改日记这些差异决定了表结构设计的不同侧重。下面进入字段设计。二、字段设计表每一列都为「时间轴」服务字段名类型约束说明idINTEGERPRIMARY KEY AUTOINCREMENT自增主键titleTEXTNOT NULL标题contentTEXTNOT NULL正文长文本可数千字moodTEXTDEFAULT ‘’心情 emoji…weatherTEXTDEFAULT ‘’天气晴/多云/雨…locationTEXTDEFAULT ‘’地点created_timeINTEGERNOT NULL创建时间戳毫秒updated_timeINTEGERNOT NULL最近修改时间戳毫秒设计要点逐条拆解1. content 用 TEXT 存长文本不需要分表。这是初学者最容易纠结的问题「正文那么长要不要单独建一张 content 表」答案是不需要。SQLite 单条 TEXT 字段可以存数百 MB一篇几千字的日记约 10KB毫无压力。RDB 的设计原则是「一行一个业务实体」——一篇日记就是一行正文是它的一个属性。强行拆表反而增加 JOIN 复杂度得不偿失。2. mood 直接存 emoji 字符。心情用 emoji//而非数字编码。为什么这里不用记账本 type 的 0/1 数字编码因为心情没有「参与计算」的需求——不做 SUM、不做 GROUP BY 统计只有展示。当枚举值只用于展示时直接存可读文本UI 零转换是最务实的方案。判断用数字还是文本的标准这个字段会不会参与聚合运算会如记账本 type就编码成数字不会如心情就存文本。3. location 取代「标签」体系。早期的文章版设计里有tags逗号分隔标签字段落地版将其替换为location地点。原因个人日记场景下「标签」概念偏重而「地点」新家/公司/书房更贴近日记的自然属性也与文章版 4-2 的时间轴节点设计一致。如果你需要标签体系逗号分隔 LIKE 查询的模式可以随时加回来——SQLite 对字符串的处理非常灵活。4. created_time 与 updated_time 双时间戳。这是内容型应用的标准配置created_time创建时刻永不变更updated_time每次编辑刷新时间轴排序用它——「最近修改的日记排前面」比「最早写的排前面」更符合用户心智刚编辑完的日记大概率还想继续看。5. 为什么只给 created_time 建索引落地版的建表 SQL 为created_time建了idx_diary_time索引排序、按月份LIKE 2025-06%过滤都走它。其实更严谨的做法是索引updated_time排序字段但个人日记几百条数据两者性能差异不可感知选择 created_time 是因为它的值域稳定插入后不变索引不会因频繁更新而失效。这个细节体现了「索引服务于查询」的朴素原则。三、建表 SQL把设计变成现实CREATETABLEIFNOTEXISTSdiary(idINTEGERPRIMARYKEYAUTOINCREMENT,titleTEXTNOTNULL,contentTEXTNOTNULL,moodTEXTDEFAULT,weatherTEXTDEFAULT,locationTEXTDEFAULT,created_timeINTEGERNOTNULL,updated_timeINTEGERNOTNULL);CREATEINDEXIFNOTEXISTSidx_diary_timeONdiary(created_time);注意三点IF NOT EXISTS幂等重复执行不报错这是 getStore 里建表语句的统一要求DEFAULT mood 有默认值插入时可以省略代码更简洁NOT NULL DEFAULT ‘’weather、location 可空但默认空串避免 NULL 泄漏到 UI 层页面|| 兜底是双保险。四、DiaryDao 封装把 SQL 关进类里数据层核心是DiaryDao职责边界与前面实例一致只管数据库不管 UI。先看实体接口exportinterfaceDiary{id:number;title:string;content:string;// 长文本正文mood:string;// 心情 emoji…weather:string;// 天气晴/多云/雨…location:string;// 地点createdTime:number;// 日记时间戳毫秒updatedTime:number;}字段命名依然是「DB 蛇形 ↔ 代码驼峰」的双风格映射集中在rowToDiary方法privatestaticrowToDiary(result:relationalStore.ResultSet):Diary{return{id:result.getLong(result.getColumnIndex(id)),title:result.getString(result.getColumnIndex(title)),content:result.getString(result.getColumnIndex(content)),mood:result.getString(result.getColumnIndex(mood))||,weather:result.getString(result.getColumnIndex(weather))||,location:result.getString(result.getColumnIndex(location))||,createdTime:result.getLong(result.getColumnIndex(created_time)),updatedTime:result.getLong(result.getColumnIndex(updated_time)),};}与文章版代码的关键差异这是本实例读者最容易踩的坑文章版用row: relationalStore.ValuesBucketrow.id as number强转落地版改用ResultSetgetColumnIndexgetLong/getString——前者拿到的getRow()是弱类型的键值容器强转不安全后者类型安全、NULL 可兜底。文章版静态方法里写this.store落地版一律DiaryDao.store——ArkTS 禁止静态上下文使用thisarkts-no-this-in-static。文章版 context 用common.UIAbilityContext落地版统一common.Context基类复用性更强。getStore单例复用模式不变staticasyncgetStore(context:common.Context):PromiserelationalStore.RdbStore{if(DiaryDao.store){returnDiaryDao.store;}constconfig:relationalStore.StoreConfig{name:diary.db,securityLevel:relationalStore.SecurityLevel.S1,};DiaryDao.storeawaitrelationalStore.getRdbStore(context,config);// 建表 建索引见上节 SQLhilog.info(DOMAIN,TAG,日记表初始化成功);returnDiaryDao.store;}五、核心 CRUD写、查、改、删插入写日记staticasyncinsert(context:common.Context,d:Diary):Promisenumber{conststoreawaitDiaryDao.getStore(context);constvalues:relationalStore.ValuesBucket{title:d.title,content:d.content,mood:d.mood,weather:d.weather,location:d.location,created_time:d.createdTime,updated_time:d.updatedTime,};returnawaitstore.insert(DiaryDao.TABLE,values);}注意content作为字符串直接塞进 ValuesBucket——长文本无需特殊处理SQLite 自动分配存储这是 TEXT 类型的天然优势。全部日记按修改时间倒序staticasyncqueryAll(context:common.Context):PromiseDiary[]{conststoreawaitDiaryDao.getStore(context);constpredicatesnewrelationalStore.RdbPredicates(DiaryDao.TABLE);predicates.orderByDesc(created_time);constresultawaitstore.query(predicates);returnDiaryDao.collect(result);}排序字段用created_time与索引一致页面时间轴按创建时间分组展示语义上更符合「日记编年史」。更新编辑日记刷新 updated_timestaticasyncupdate(context:common.Context,d:Diary):Promisenumber{conststoreawaitDiaryDao.getStore(context);constvalues:relationalStore.ValuesBucket{title:d.title,content:d.content,mood:d.mood,weather:d.weather,location:d.location,updated_time:d.updatedTime,};constpredicatesnewrelationalStore.RdbPredicates(DiaryDao.TABLE);predicates.equalTo(id,d.id);returnawaitstore.update(values,predicates);}注意 update 不更新created_time——创建时间不可变只刷updated_time这是双时间戳的协作方式。删除与计数staticasyncdelete(context:common.Context,id:number):Promisenumber{conststoreawaitDiaryDao.getStore(context);constpredicatesnewrelationalStore.RdbPredicates(DiaryDao.TABLE);predicates.equalTo(id,id);returnawaitstore.delete(predicates);}staticasynccount(context:common.Context):Promisenumber{conststoreawaitDiaryDao.getStore(context);constresultawaitstore.querySql(SELECT COUNT(*) AS c FROM${DiaryDao.TABLE});lettotal0;if(result.goToNextRow()){totalresult.getLong(result.getColumnIndex(c));}result.close();returntotal;}六、时间分组查询按月份捞日记时间轴页面的核心需求选中某个月份只看那个月的日记。落地版提供queryByMonthstaticasyncqueryByMonth(context:common.Context,month:string):PromiseDiary[]{conststoreawaitDiaryDao.getStore(context);constpredicatesnewrelationalStore.RdbPredicates(DiaryDao.TABLE);predicates.like(created_time,${month}%).orderByDesc(created_time);constresultawaitstore.query(predicates);returnDiaryDao.collect(result);}这个方法的巧妙之处like(created_time, 2025-06%)用 LIKE 前缀匹配模拟「时间范围查询」。因为 created_time 存的是毫秒时间戳的字符串形式——不对等等created_time 是 INTEGER 类型LIKE 匹配的是数字的字符串表示。2025-06-01 00:00:00的时间戳是1748707200000用LIKE 2025-06%匹配不到这是一个需要澄清的坑文章版的queryByMonth按updated_time LIKE的设计在 INTEGER 时间戳上是不成立的那是按文本日期存储时的写法。落地版页面DiaryPage采用更可靠的方式直接queryAll拉全量在内存里按fmtDate分组渲染时间轴节点。个人日记数据量小几十上百篇全量加载 前端分组是务实的选择若数据量大正确做法是between(startOfMonth, endOfMonth)毫秒区间查询——这与记账本实例的区间查询一脉相承。关键词搜索标题/正文双字段 LIKEstaticasyncsearch(context:common.Context,keyword:string):PromiseDiary[]{conststoreawaitDiaryDao.getStore(context);constpredicatesnewrelationalStore.RdbPredicates(DiaryDao.TABLE);predicates.like(title,%${keyword}%).or().like(content,%${keyword}%).orderByDesc(created_time);constresultawaitstore.query(predicates);returnDiaryDao.collect(result);}LIKE %关键词%是包含匹配——标题或正文任意位置出现关键词即命中。.or()把两个条件连接为 OR 语义。注意LIKE 前导%会导致索引失效无法走 B 树全表扫描但日记本几百条数据扫描无压力。如果将来数据量到十万级应改用 FTS5 全文索引——那是另一个维度的优化本书暂不展开。七、技术要点对照表技术点实现方式生产价值长文本TEXT 字段直接存正文无需分表读写简单双时间戳created_time updated_time创建/编辑分离时间轴按编辑排序关键词搜索title/content OR LIKE双字段命中率高时间分组全量加载 前端按日期分组小数据量下的务实方案emoji 存储mood 直接存字符UI 零转换幂等建表CREATE IF NOT EXISTS重复启动不报错八、文章小结日记本数据层与记账本的数值聚合完全不同重心在长文本存储策略 双时间戳的时间轴组织 LIKE 关键词检索。TEXT 类型让长正文毫无负担双时间戳让「最近编辑优先」成为可能LIKE 让搜索零配置。下一篇4-2将展示这些数据如何被时间轴 UI 呈现——垂直时间轴 日期节点 心情天气卡片把数据变成故事。动手练习尝试给search增加第三个条件按天气过滤equalTo(weather, 晴)并观察 OR 与 AND 组合时 RdbPredicates 的链式写法。

相关新闻

2026/8/16 3:51:13

AI论文写作工具哪个最好?2026亲测

"开题报告改5版仍被打回","文献综述堆30篇却毫无逻辑","格式排版耗3天还不符合学校要求","AI生成内容被AIGC检测标红"——2026年高校AI学术规范全面收紧的背景下,毕业生在选择AI论文写作…

2026/8/16 4:46:17

PyTorch+DeepSpeed大模型分布式训练实战指南

1. 项目背景与核心挑战在2024年的AI技术浪潮中,大模型训练已经成为行业标配。但当我第一次尝试在单台8卡A100服务器上训练10B参数量的模型时,显存不足的报错让我意识到:分布式训练不是选修课,而是生存技能。本文将分享基于PyTorch…

2026/8/16 4:46:17

深入解析set_input_delay:数字芯片时序约束的核心概念与工程实践

1. 项目概述:为什么我们需要理解set_input_delay在数字芯片设计的时序约束世界里,set_input_delay绝对是一个高频出现但又让不少新手甚至有一定经验的工程师感到困惑的命令。我第一次接触它时,也花了不少时间才把“输入延迟”这个概念从字面意…

2026/8/16 4:46:17

大模型项目翻车后,我才明白小团队该补的不是Agent能力

聊《程序员职业规划怎么选方向?先回答几个现实问题》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。 摘要 摘要:接入过三个Agent项目后,我逐渐看清一个现实:真正卡住…

2026/8/16 4:46:17

OpenClaw开源AI智能体框架:从本地部署到自动化任务实战

1. 从“玩具”到“员工”:为什么我们需要一个永不下班的AI助手?最近,我身边不少朋友和同事都在讨论一个词:AI智能体。从Dify、Kimi的本地部署,到各种开源框架的涌现,大家似乎都在寻找一个答案:如…

2026/8/16 4:46:17

AI自反性:构建能自我进化的智能体系统

1. 项目概述:当AI开始“自我审视”最近在AI圈子里,一个叫“Autoreflection”(自反性)的概念讨论度越来越高。它听起来有点哲学,但内核其实非常技术化,直指当前大语言模型和智能体发展的一个核心瓶颈。简单来…

2026/8/16 4:41:16

Win11下VSCode配置Python虚拟环境:从venv原理到高效开发实战

1. 项目概述:为什么在Win11上用VSCode配置Python虚拟环境是开发者的必修课 如果你在Windows 11上写Python,还在用系统全局的Python环境,那无异于在厨房里把所有调料都倒进一个罐子——炒菜时,你永远不知道会尝到什么奇怪的味道。…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/15 9:46:39

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/15 4:56:16

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/15 9:46:30

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…