一、鸿蒙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. 鸿蒙执行流程

  1. 获取RdbStore实例(数据库连接)
  2. 构建查询参数
    const predicates = new relationalStore.RdbPredicates("USERS"); // 表名
    predicates.equalTo("ACCOUNT", "user123"); // 条件设置
    
  3. 执行查询
    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 等第三方库(参考搜索结果中的库),其默认的 deletebatchDelete 操作不会自动重置计数器。需手动调用原生 executeSql 方法完成重置,或检查库是否提供扩展接口(如自定义 SQL 执行功能)。

总结

操作 是否必需 原因 推荐方法
清空表数据 移除所有数据行 rdbStore.delete(predicates)
重置自增计数器 避免主键冲突和连续性错误 rdbStore.executeSql("DELETE FROM sqlite_sequence WHERE name = 'table_name';")

通过手动重置计数器,可确保数据表在清空后保持正确的初始状态。如果涉及复杂业务逻辑(如数据同步),建议在鸿蒙的 relationalStore 模块文档中进一步查阅事务管理和错误处理机制。

三、装饰器@

以下是鸿蒙(HarmonyOS)开发中常用装饰器的核心作用分类说明,基于功能特性和应用场景进行归纳:

1 状态管理类

  1. @State

    • 作用:定义组件内部私有状态,状态变化自动触发UI更新
    • 特点:必须初始化,适用于组件内部数据驱动
  2. @Prop

    • 作用:实现父→子组件单向数据流
    • 特点:可接收父组件初始值,子组件修改不影响父源
  3. @Link

    • 作用:建立父子组件双向数据绑定
    • 特点:需父组件通过$传递引用,禁止初始化
  4. @ObjectLink

    • 作用:监听复杂对象属性变化
    • 特点:需配合@Observed装饰类使用,深度监测对象内部变更
  5. @Watch

    • 作用监听状态变量变化并触发回调
    • 特点:常用于状态变更后执行异步操作或联动逻辑

2 跨组件/层级通信

  1. @Provide / @Consume

    • 作用:实现跨组件层级数据共享
    • 机制:提供方(@Provide)自动广播,消费方(@Consume)自动更新
  2. @StorageLink / @StorageProp

    • 作用:绑定应用全局状态存储(AppStorage)
    • 区别
      • @StorageLink:双向同步(修改同步到AppStorage)
      • @StorageProp:单向同步(仅接收AppStorage更新)
  3. @LocalStorageLink / @LocalStorageProp

    • 作用:管理页面级状态存储
    • 场景:同页面多组件间数据共享,需显式初始化

3 UI构建与复用

  1. @Builder

    • 作用:声明可复用的UI构建函数
    • 优势:抽象重复UI结构,支持逻辑控制
  2. @BuilderParam

    • 作用动态引用@Builder函数
    • 应用:为组件注入自定义UI片段,实现插槽功能

四、样式与约束

  1. @Styles

    • 作用:定义组件样式集合 1
    • 示例:封装字体/颜色等通用样式,通过.style调用
  2. @Require

    • 作用:强制约束组件参数必传
    • 应用场景:确保@Prop@BuilderParam参数不为空

补充说明

  • 装饰器组合策略
    • 状态类装饰器禁止嵌套(如同时用@State+@Prop
    • @Watch常配合@State/@Link实现状态监听链
    • @ObjectLink需与@Observed联用实现对象深度响应

💡 最佳实践建议

  • 简单状态用@State,跨组件通信优选@Provide/@Consume
  • 全局状态管理使用@StorageLink,页面级共享用@LocalStorageLink
  • UI复用场景组合使用@Builder+@BuilderParam实现组件动态化

四、组件之间距离设置

在鸿蒙(HarmonyOS)应用开发中,配置组件之间的距离主要通过以下属性实现,需根据具体布局和组件类型选择:

1 通用间距属性

  1. margin 外边距
    用于设置组件外部与其他组件的间距:

    // 设置四个方向统一外边距(单位:vp)
    Component().margin(20)
    
    // 分别设置各方向外边距(API 12+)
    Component().margin({ top: 10, right: 20, bottom: 15, left: 5 })
    
    • 适用场景:所有支持通用属性的组件(如ColumnRowButton等)
    • 注意事项:设置在父容器内子组件之间时,需避免过度使用导致布局溢出
  2. padding 内边距
    用于设置组件内部内容与边界的间距:

    // 设置四个方向统一内边距
    Component().padding(15)
    
    • 适用场景:需调整内容与组件边界的距离时(如文本框内文字与边框的距离)

2 特殊布局专用属性

  1. gutter 栅格间距(AutoRow组件)
    在自定义栅格布局中设置列间水平/垂直间距:

    AutoRow({ gutter: 20 })          // 水平间距20px
    AutoRow({ gutter: [20, 30] })    // 水平20px,垂直30px
    
    • 原理:通过负边距抵消列的内边距,实现均匀间隔
    • 场景:栅格布局(如卡片式列表)
  2. divider 分割线(List组件)
    通过分割线样式间接控制列表项间距:

    List() {
      // ...
    }.divider({
      strokeWidth: 1,   // 分割线粗细
      startMargin: 10,  // 分割线起始边距
      endMargin: 10     // 分割线结束边距
    })
    
    • 效果:调整strokeWidthstartMargin/endMargin可控制视觉间距
  3. nextMargin 后边距(Swiper组件)
    控制轮播项与后续项的露出距离:

    Swiper() {
      // ...
    }.nextMargin(30)  // 后一项露出30vp
    
    • 动态调整:结合onAnimationStart回调实现运行时修改1

3 关键限制与替代方案

  1. 不支持的组件

    • Span组件不支持 margin/padding(如Text中的子组件)
      替代方案
    // 用空格字符增加间距
    Text('前' + ' '.repeat(5) + '后')
    
    // 改用Row+Text组合
    Row() {
      Text('左').margin({ right: 10 })
      Text('右')
    }
    
  2. 布局重叠排查
    若出现间距失效(如组件遮挡):

    • 检查是否误用Stack布局(默认居中堆叠)
    • 避免在Column/Row中给子组件设置冲突的百分比尺寸

总结建议

  • 优先使用 margin 处理组件间通用距离
  • 列表/栅格等结构化布局使用 gutterdivider
  • Span等特殊组件需采用空格或组合布局替代

五、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不支持直接重命名字段,需要通过创建新表、迁移数据的方式实现完整迁移。

最简单的方法:先清除表数据再重命名~

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐