鸿蒙RDB数据库封装与使用实践

最近项目又要搞数据存储,鸿蒙的RDB用起来还挺啰嗦,干脆自己封装了个工具类,省得每次都写一堆重复代码。这里随手记下,万一以后自己忘了还能翻出来看看。

一、SQL基础知识

1.1 什么是SQL

SQL(Structured Query Language)是用来操作关系型数据库的标准语言。其实最常用的就那几句,真遇到复杂的,百度/ChatGPT一搜一大把。

1.2 常用SQL语句

  1. 创建表
CREATE TABLE user_info (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键,自增
name TEXT, -- 文本类型
age INTEGER, -- 整数类型
score REAL, -- 浮点类型
is_active INTEGER DEFAULT 0 -- 默认值
);
  1. 插入数据
INSERT INTO user_info (name, age) VALUES ('张三', 18);
  1. 查询数据
-- 查询所有字段
SELECT * FROM user_info; -- 条件查询
SELECT name, age FROM user_info WHERE age > 18; -- 排序
SELECT * FROM user_info ORDER BY age DESC; -- 限制数量
SELECT * FROM user_info LIMIT 10;
  1. 更新数据
UPDATE user_info SET age = 20 WHERE name = '张三';
  1. 删除数据
DELETE FROM user_info WHERE age < 18;

二、开发背景

其实一开始我没打算封装,结果每次用都得写一堆初始化、建表、try-catch,烦得很。后来干脆写了个RDBUtils,省心多了。

三、实现步骤

3.1 创建RDB工具类

import relationalStore from '@ohos.data.relationalStore';
import common from '@ohos.app.ability.common'; // 数据库配置
const STORE_CONFIG: relationalStore.StoreConfig = {
name: 'user.db',
securityLevel: relationalStore.SecurityLevel.S1
}; // 数据库版本号
const DB_VERSION = 2; // 创建表的SQL语句
const SQL_CREATE_TABLE = `
CREATE TABLE IF NOT EXISTS user_info (
id INTEGER PRIMARY KEY AUTOINCREMENT,
avatarUri TEXT,
nickName TEXT,
unionID TEXT,
openID TEXT,
authorizationCode TEXT,
createTime INTEGER,
memo TEXT,
isSynced INTEGER DEFAULT 0
)
`; export class RDBUtils {
private static instance: RDBUtils | null = null;
private rdbStore: relationalStore.RdbStore | null = null;
private context: common.Context; // 单例模式
public static getInstance(context: common.Context): RDBUtils {
if (!RDBUtils.instance) {
RDBUtils.instance = new RDBUtils(context);
}
return RDBUtils.instance;
} // 初始化数据库
async initRDB(): Promise<void> {
if (this.rdbStore) {
return;
} try {
this.rdbStore = await relationalStore.getRdbStore(this.context, STORE_CONFIG);
await this.rdbStore.executeSql(SQL_CREATE_TABLE);
await this.checkAndUpdateVersion();
} catch (err) {
console.error(`初始化数据库失败: ${err}`);
throw err;
}
} // 数据库升级
private async checkAndUpdateVersion(): Promise<void> {
// 检查版本并执行升级
const currentVersion = await this.getCurrentVersion();
if (currentVersion < DB_VERSION) {
await this.upgradeDatabase(currentVersion);
}
}
}

3.2 实现CRUD操作

export class RDBUtils {
// ... 前面的代码 ... // 插入数据
async insertUser(user: UserInfo): Promise<number> {
if (!this.rdbStore) {
throw new Error('数据库未初始化');
} const valueBucket: relationalStore.ValuesBucket = {
'avatarUri': user.avatarUri || '',
'nickName': user.nickName || '',
'unionID': user.unionID || '',
'openID': user.openID || '',
'authorizationCode': user.authorizationCode || '',
'createTime': user.createTime || Date.now(),
'memo': user.memo || '',
'isSynced': user.isSynced ? 1 : 0
}; try {
return await this.rdbStore.insert('user_info', valueBucket);
} catch (err) {
console.error(`插入数据失败: ${err}`);
throw err;
}
} // 更新数据
async updateUser(user: UserInfo): Promise<number> {
if (!this.rdbStore || !user.id) {
throw new Error('参数错误');
} const valueBucket: relationalStore.ValuesBucket = {
'avatarUri': user.avatarUri || '',
'nickName': user.nickName || '',
// ... 其他字段
}; const predicates = new relationalStore.RdbPredicates('user_info');
predicates.equalTo('id', user.id); try {
return await this.rdbStore.update(valueBucket, predicates);
} catch (err) {
console.error(`更新数据失败: ${err}`);
throw err;
}
} // 查询数据
async queryUserById(id: number): Promise<UserInfo | null> {
if (!this.rdbStore) {
throw new Error('数据库未初始化');
} const predicates = new relationalStore.RdbPredicates('user_info');
predicates.equalTo('id', id); try {
const resultSet = await this.rdbStore.query(predicates, ['*']);
if (resultSet.goToNextRow()) {
const user = this.parseUserFromResultSet(resultSet);
resultSet.close();
return user;
}
resultSet.close();
return null;
} catch (err) {
console.error(`查询数据失败: ${err}`);
throw err;
}
}
}

四、踩坑记录

  • 单例不做,内存飙升,手机直接卡死,血的教训。
  • 查询结果集忘记close,内存泄漏,调了半天才发现。
  • 批量插入不加事务,慢得想砸电脑。
  • 插入数据时没处理空值,数据库直接报错。
  • 更新数据时没检查ID,更新失败,debug半天。

五、使用示例

比如我有个用户表,插入、查、改、删都靠这套,写起来就几行代码,舒服。

// 初始化数据库
const rdbUtils = RDBUtils.getInstance(context);
await rdbUtils.initRDB(); // 插入数据
const user: UserInfo = {
avatarUri: 'https://example.com/avatar.jpg',
nickName: '张三',
unionID: '123456',
openID: '789012',
authorizationCode: 'abc123'
};
const id = await rdbUtils.insertUser(user); // 查询数据
const user = await rdbUtils.queryUserById(id);
console.info(`查询到用户: ${user?.nickName}`); // 更新数据
if (user) {
user.nickName = '李四';
await rdbUtils.updateUser(user);
} // 删除数据
await rdbUtils.deleteUser(id);

六、注意事项

  • 千万别忘了异常处理,出错了不提示,用户一脸懵。
  • 结果集不用就close,不然你会怀疑人生。
  • 数据类型别写错,调试起来很费劲。
  • 合理用索引,批量操作记得加事务。
  • 敏感数据要加密,别让用户数据裸奔。

七、总结

反正这玩意我自己用着还挺顺手,大家有更好的写法欢迎留言交流,别让我一个人踩坑。

八、参考资源

欢迎体验

这个RDB工具类已经集成到鸿蒙开发者工具箱里了,欢迎下载体验!

鸿蒙开发者工具箱


作者:在人间耕耘

邮箱:1743914721@qq.com

版权声明:本文为博主原创文章,转载请附上原文出处链接及本声明。

开发者工具箱-鸿蒙RDB数据库封装与使用实践的更多相关文章

  1. mysql数据库封装和 分页查询

    1 之前我们学到了php连接mysql数据库的增删改查,中间要多次调用数据库, 而且以后用到的表比较多,上传中如果需要改数据的话会非常麻烦,但是如果 我们把数据库封装,到时就可以很轻松的把改掉一些数据 ...

  2. Asp.Net Core 2.0 项目实战(4)ADO.NET操作数据库封装、 EF Core操作及实例

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

  3. MySQL数据库封装和分页查询

    1.数据库封装 <?php //我用的数据库名是housedb class DBDA {public $host="localhost";public $uid=" ...

  4. 【Cocos2d-x Lua】数据库封装类型的操作

    Lua数据库封装类型的操作 使用演示样例 lua代码: require("DB") -- 保存一个字符串(数据库中存储的数据都是以字符串的形式保存的) DB:getInstance ...

  5. 数据库周刊28│开发者最喜爱的数据库是什么?阿里云脱口秀聊程序员转型;MySQL update误操作;PG流复制踩坑;PG异机归档;MySQL架构选型;Oracle技能表;Oracle文件损坏处理……

    热门资讯 1.Stackoverflow 2020年度报告出炉!开发者最喜爱的数据库是什么?[摘要]2020年2月,近6.5万名开发者参与了 Stackoverflow 的 2020 年度调查,这份报 ...

  6. XPages访问关系型数据库技术与最佳实践

    XPage 对于 Domino 开发人员的一大好处就是能够很方便和高效的访问关系型数据库.本文通过实例代码展现了在 XPage 中访问关系型数据库的具体步骤 , 同时讲解了一些在 XPage 中高效访 ...

  7. Cobar分布式数据库的应用与实践

    最新文章:看我如何快速学习.Net(高可用数据采集平台).高并发数据采集的架构应用(Redis的应用) 问题点: 随着项目的增长,数据和数据表也成倍的增长,普通的单点数据库已经无法满足日常的增长的需要 ...

  8. 单KEY业务,数据库水平切分架构实践 | 架构师之路

    https://mp.weixin.qq.com/s/8aI9jS0SXJl5NdcM3TPYuQ 单KEY业务,数据库水平切分架构实践 | 架构师之路 原创: 58沈剑 架构师之路 2017-06- ...

  9. 单机数据库优化的一些实践(mysql)

    数据库优化有很多可以讲,按照支撑的数据量来分可以分为两个阶段:单机数据库和分库分表,前者一般可以支撑500W或者10G以内的数据,超过这个值则需要考虑分库分表.另外,一般大企业面试往往会从单机数据库问 ...

  10. mysql数据库封装

    <?php /** * name: sql操作封装,可扩展 . * User: 张云山 * Date: 2016/9/4 * Time: 22:02 */ //php文件编码设置header(' ...

随机推荐

  1. 手把手教你如何给 Docker 开启 IPv6 网络支持

    Docker 默认是不开启 IPv6 支持的,但是我们某些业务往往又需要 IPv6 的支持,特别是 IPv6 普及大势所趋,本文主要介绍的是如何开启 Docker 桥接网络 IPv6 支持,这篇文章具 ...

  2. Linux如何从命令行卡死的进程中退出?

    Linux如何从命令行卡死的进程中退出? 不知道大家在使用Linux的时候,会不会遇到一些命令,有可能卡顿,有可能执行时间过长(比如使用 find 查找某个文件),这个时候我不想继续执行这个命令了,说 ...

  3. ro在xe10.3上的安装

    在学习研究RO. RO9.2.101.1295在xe10.3上安装遇到新问题.记录处理的办法: 没有采用执行exe安装的方法.而是采用复制源代码后编译安装. 1.把生成的bpl.dcp安装到默认目录, ...

  4. nodejs参数的处理与用户的交互

    解析脚本参数 作为脚本或者命令行工具,一般都需要支持不同的用户参数.默认参数被保存在process.argv的数组中,如下: [ nodeBinary, script, arg0, arg1, ... ...

  5. Unity Mask原理及自定义遮罩

    主要内容 StencilBuffer是什么? 自定义Shader来实现遮罩 Unity Mask的原理 1.什么是StencilBuffer GPU在渲染前会为每个像素点分配一个1字节(8位)大小的内 ...

  6. Python科学计算系列9—逻辑代数

    1.基本定理的验证 代码如下: from sympy import * A, B, C = symbols('A B C') # 重叠律 # A·A=A A+A=A print(to_cnf(A | ...

  7. 匿名内部类、lambda匿名函数表达式

    a.匿名内部类的定义格式: 接口名称 对象名 = new 接口名称(){ //覆盖重写所有抽象方法 }: 一. /** * lambda匿名函数的使用 * Lambda省去面向对象的条条框框,格式由3 ...

  8. Linux防火墙操作指令

    firewall-cmd --list-ports # 查看开放的端口号 firewall-cmd --zone=public --add-port=8888/tcp --permanent # 开放 ...

  9. grequests,并发执行接口请求的方法(简易版)

    有时候需要处理很多请求,显然,一个一个去处理是要花费很多时间的 我们就需要用到并发的方式,python并发请求的方法很多,从简单到复杂. 本案例,介绍一个超级简单,使用grequests库,实现并发请 ...

  10. 图解-六种常见开源协议的比较(GPL,Mozilla,LGPL,BSD,Apache,MIT)

    附: 五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)