鸿蒙开发笔记2
鸿蒙开发经验之谈~
一、鸿蒙SQL查询语法
在鸿蒙(HarmonyOS)的关系型数据库开发中,SQL查询语法遵循SQLite标准,以下是关键语法说明及注意事项:
1. 基础SELECT语法
SELECT 列名 FROM 表名 [WHERE 条件]
- 示例:
SELECT USER_ID FROM USERS WHERE ACCOUNT = ?USER_ID:目标列USERS:表名ACCOUNT = ?:条件(?为占位符)
2. 核心组件
(1) 占位符(?)
- 用于参数化查询,避免SQL注入
- 执行时需绑定实际值:
const queryArgs = ["user123"]; // 替换?的实际参数
(2) WHERE子句运算符
| 运算符 | 示例 | 说明 |
|---|---|---|
= |
ACCOUNT = ? |
等于 |
LIKE |
NAME LIKE '%张%' |
模糊匹配 |
IN |
ID IN (?,?,?) |
多值匹配 |
BETWEEN |
AGE BETWEEN 18 AND 30 |
范围查询 |
3. 鸿蒙执行流程
- 获取RdbStore实例(数据库连接)
- 构建查询参数:
const predicates = new relationalStore.RdbPredicates("USERS"); // 表名 predicates.equalTo("ACCOUNT", "user123"); // 条件设置 - 执行查询:
const resultSet = await rdbStore.query(predicates, ["USER_ID"]); // 指定返回列
4. 安全规范
- 禁止拼接SQL:
❌ 错误示例:SELECT ... WHERE ACCOUNT = '${userInput}'
✅ 正确做法:始终使用占位符?绑定参数 - 列名/表名验证:动态名称需白名单过滤
5. 高级查询
-- 多表JOIN
SELECT u.USER_ID, o.ORDER_NO
FROM USERS u
JOIN ORDERS o ON u.ID = o.USER_ID
WHERE u.ACCOUNT = ?
-- 聚合函数
SELECT COUNT(*) FROM USERS WHERE AGE > 18
提示:完整文档见 HarmonyOS RDB开发指南(官方Gitee仓库)
二、鸿蒙关系性数据库自增计数器
在鸿蒙(HarmonyOS)的关系型数据库中清空数据表的所有数据后,通常需要手动重置主键自增计数器。以下是详细分析和操作建议,基于鸿蒙数据库的设计机制和最佳实践:
1. 为什么需要重置主键自增计数器?
- 计数器不自动重置:鸿蒙的关系型数据库基于 SQLite 实现。当使用
DELETE操作清空表数据时(例如rdbStore.delete(predicates)),数据库仅移除数据行,但主键的自增计数器(如AUTOINCREMENT)会保留最后一次插入的 ID 值。下次插入数据时,主键会从旧的最大值继续递增,可能导致主键冲突或不连续。 - 潜在风险:
- 如果后续插入数据时主键值重复,会触发唯一性约束错误(错误码 14800011)。
- 在数据同步或备份场景中,主键不连续可能影响数据一致性。
2. 如何重置主增计数器?
在鸿蒙开发中,需通过执行自定义 SQL 语句手动重置计数器。以下是具体方法(以 ArkTS 为例):
步骤 1:清空表数据
使用 RdbPredicates 删除所有数据:
import relationalStore from '@kit.RelationalStore';
// 假设 rdbStore 已初始化
let predicates = new relationalStore.RdbPredicates('your_table_name');
await rdbStore.delete(predicates); // 清空表数据
步骤 2:重置自增计数器
执行 SQL 语句重置计数器:
// 重置主键计数器(针对 SQLite 实现)
await rdbStore.executeSql("DELETE FROM sqlite_sequence WHERE name = 'your_table_name';");
- 说明:
sqlite_sequence是 SQLite 的系统表,存储每个表的自增计数器值。- 执行此语句后,主键将从
1重新开始计数。
3. 注意事项
- 表是否定义自增主键:仅当表的主键使用了
AUTOINCREMENT属性时(例如通过@Column({ autoIncrement: true })注解),才需此操作。非自增主键无需重置。 - 原子性操作:建议将清空数据和重置计数器放在事务中,确保操作原子性:
await rdbStore.beginTransaction(); try { await rdbStore.delete(predicates); await rdbStore.executeSql("DELETE FROM sqlite_sequence WHERE name = 'your_table_name';"); await rdbStore.commit(); } catch (err) { await rdbStore.rollback(); console.error(`Operation failed: ${err.code}, ${err.message}`); } - 替代方案:如果表结构允许,可删除并重建表(通过
rdbStore.executeSql('DROP TABLE ...')和重新初始化),但此方法更重,适用于表结构需变更的场景。
4. 使用第三方库的说明
如果使用 ZDbUtil 等第三方库(参考搜索结果中的库),其默认的 delete 或 batchDelete 操作不会自动重置计数器。需手动调用原生 executeSql 方法完成重置,或检查库是否提供扩展接口(如自定义 SQL 执行功能)。
总结
| 操作 | 是否必需 | 原因 | 推荐方法 |
|---|---|---|---|
| 清空表数据 | 是 | 移除所有数据行 | rdbStore.delete(predicates) |
| 重置自增计数器 | 是 | 避免主键冲突和连续性错误 | rdbStore.executeSql("DELETE FROM sqlite_sequence WHERE name = 'table_name';") |
通过手动重置计数器,可确保数据表在清空后保持正确的初始状态。如果涉及复杂业务逻辑(如数据同步),建议在鸿蒙的 relationalStore 模块文档中进一步查阅事务管理和错误处理机制。
三、装饰器@
以下是鸿蒙(HarmonyOS)开发中常用装饰器的核心作用分类说明,基于功能特性和应用场景进行归纳:
1 状态管理类
-
@State
- 作用:定义组件内部私有状态,状态变化自动触发UI更新
- 特点:必须初始化,适用于组件内部数据驱动
-
@Prop
- 作用:实现父→子组件单向数据流
- 特点:可接收父组件初始值,子组件修改不影响父源
-
@Link
- 作用:建立父子组件双向数据绑定
- 特点:需父组件通过
$传递引用,禁止初始化
-
@ObjectLink
- 作用:监听复杂对象属性变化
- 特点:需配合
@Observed装饰类使用,深度监测对象内部变更
-
@Watch
- 作用:监听状态变量变化并触发回调
- 特点:常用于状态变更后执行异步操作或联动逻辑
2 跨组件/层级通信
-
@Provide / @Consume
- 作用:实现跨组件层级数据共享
- 机制:提供方(@Provide)自动广播,消费方(@Consume)自动更新
-
@StorageLink / @StorageProp
- 作用:绑定应用全局状态存储(AppStorage)
- 区别:
@StorageLink:双向同步(修改同步到AppStorage)@StorageProp:单向同步(仅接收AppStorage更新)
-
@LocalStorageLink / @LocalStorageProp
- 作用:管理页面级状态存储
- 场景:同页面多组件间数据共享,需显式初始化
3 UI构建与复用
-
@Builder
- 作用:声明可复用的UI构建函数
- 优势:抽象重复UI结构,支持逻辑控制
-
@BuilderParam
- 作用:动态引用@Builder函数
- 应用:为组件注入自定义UI片段,实现插槽功能
四、样式与约束
-
@Styles
- 作用:定义组件样式集合 1
- 示例:封装字体/颜色等通用样式,通过
.style调用
-
@Require
- 作用:强制约束组件参数必传
- 应用场景:确保
@Prop或@BuilderParam参数不为空
补充说明
- 装饰器组合策略:
- 状态类装饰器禁止嵌套(如同时用
@State+@Prop) @Watch常配合@State/@Link实现状态监听链@ObjectLink需与@Observed联用实现对象深度响应
- 状态类装饰器禁止嵌套(如同时用
💡 最佳实践建议:
- 简单状态用
@State,跨组件通信优选@Provide/@Consume- 全局状态管理使用
@StorageLink,页面级共享用@LocalStorageLink- UI复用场景组合使用
@Builder+@BuilderParam实现组件动态化
四、组件之间距离设置
在鸿蒙(HarmonyOS)应用开发中,配置组件之间的距离主要通过以下属性实现,需根据具体布局和组件类型选择:
1 通用间距属性
-
margin外边距
用于设置组件外部与其他组件的间距:// 设置四个方向统一外边距(单位:vp) Component().margin(20) // 分别设置各方向外边距(API 12+) Component().margin({ top: 10, right: 20, bottom: 15, left: 5 })- 适用场景:所有支持通用属性的组件(如
Column、Row、Button等) - 注意事项:设置在父容器内子组件之间时,需避免过度使用导致布局溢出
- 适用场景:所有支持通用属性的组件(如
-
padding内边距
用于设置组件内部内容与边界的间距:// 设置四个方向统一内边距 Component().padding(15)- 适用场景:需调整内容与组件边界的距离时(如文本框内文字与边框的距离)
2 特殊布局专用属性
-
gutter栅格间距(AutoRow组件)
在自定义栅格布局中设置列间水平/垂直间距:AutoRow({ gutter: 20 }) // 水平间距20px AutoRow({ gutter: [20, 30] }) // 水平20px,垂直30px- 原理:通过负边距抵消列的内边距,实现均匀间隔
- 场景:栅格布局(如卡片式列表)
-
divider分割线(List组件)
通过分割线样式间接控制列表项间距:List() { // ... }.divider({ strokeWidth: 1, // 分割线粗细 startMargin: 10, // 分割线起始边距 endMargin: 10 // 分割线结束边距 })- 效果:调整
strokeWidth和startMargin/endMargin可控制视觉间距
- 效果:调整
-
nextMargin后边距(Swiper组件)
控制轮播项与后续项的露出距离:Swiper() { // ... }.nextMargin(30) // 后一项露出30vp- 动态调整:结合
onAnimationStart回调实现运行时修改1
- 动态调整:结合
3 关键限制与替代方案
-
不支持的组件
Span组件不支持margin/padding(如Text中的子组件)
替代方案:
// 用空格字符增加间距 Text('前' + ' '.repeat(5) + '后') // 改用Row+Text组合 Row() { Text('左').margin({ right: 10 }) Text('右') } -
布局重叠排查
若出现间距失效(如组件遮挡):- 检查是否误用
Stack布局(默认居中堆叠) - 避免在
Column/Row中给子组件设置冲突的百分比尺寸
- 检查是否误用
总结建议:
- 优先使用
margin处理组件间通用距离- 列表/栅格等结构化布局使用
gutter或dividerSpan等特殊组件需采用空格或组合布局替代
五、background与backgroundColor区别
目的实现效果:页面背景为灰色,时间选择器背景为白色,四角圆润
代码1
Stack() {
TimePicker({
format: TimePickerFormat.HOUR_MINUTE,
selected:alarmData?.alarmTime ? this.timeStringToDate(alarmData.alarmTime) : new Date(0, 0, 0, 0, 0, 0)
})
.useMilitaryTime(true)
.onChange((result: TimePickerResult) => {
this.selectedTime = `${result.hour.toString().padStart(2,'0')}:${result.minute.toString().padStart(2,'0')}`
this.is_ChangedTime=true
})
.width('100%')
.height(200)
}
.width('85%')
.height(220)
.borderRadius(30)
.padding(10)
.background(Color.White)
.shadow({
radius: 8,
color: '#00000020',
offsetX: 0,
offsetY: 4
})
结果:
把background改为backgroundcolor后的结果
六、List组件动画问题
所有列表项共享同一个状态变量,导致点击一个列表项时所有列表项都会动画。
根本原因:
共享状态变量:代码中使用 @State alarmItemScaleValue: number = 1 和 @State alarmItemBgColor: string = ‘#ffffff’ 作为所有列表项的动画状态
全局控制:handleAlarmItemClickAnimation() 方法修改的是全局状态,影响所有列表项
缺乏独立状态管理:每个列表项没有自己的独立动画状态
修改原则:
状态管理原则:每个列表项应该有自己独立的状态,而不是共享全局状态
数据驱动UI:动画状态应该作为数据模型的一部分,而不是UI状态
ArkTS响应式机制:修改对象属性后,需要通过重新赋值数组来触发UI更新
性能优化:使用展开运算符 […this.alarmList] 创建新数组引用,触发响应式更新
七、数据表字段重名
SQLite的ALTER TABLE不支持直接重命名字段,需要通过创建新表、迁移数据的方式实现完整迁移。
最简单的方法:先清除表数据再重命名~
更多推荐



所有评论(0)