开发者工具箱-鸿蒙RDB数据库封装与使用实践
鸿蒙RDB数据库封装与使用实践
最近项目又要搞数据存储,鸿蒙的RDB用起来还挺啰嗦,干脆自己封装了个工具类,省得每次都写一堆重复代码。这里随手记下,万一以后自己忘了还能翻出来看看。
一、SQL基础知识
1.1 什么是SQL
SQL(Structured Query Language)是用来操作关系型数据库的标准语言。其实最常用的就那几句,真遇到复杂的,百度/ChatGPT一搜一大把。
1.2 常用SQL语句
- 创建表
CREATE TABLE user_info (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键,自增
name TEXT, -- 文本类型
age INTEGER, -- 整数类型
score REAL, -- 浮点类型
is_active INTEGER DEFAULT 0 -- 默认值
);
- 插入数据
INSERT INTO user_info (name, age) VALUES ('张三', 18);
- 查询数据
-- 查询所有字段
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;
- 更新数据
UPDATE user_info SET age = 20 WHERE name = '张三';
- 删除数据
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数据库封装与使用实践的更多相关文章
- mysql数据库封装和 分页查询
1 之前我们学到了php连接mysql数据库的增删改查,中间要多次调用数据库, 而且以后用到的表比较多,上传中如果需要改数据的话会非常麻烦,但是如果 我们把数据库封装,到时就可以很轻松的把改掉一些数据 ...
- 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 ...
- MySQL数据库封装和分页查询
1.数据库封装 <?php //我用的数据库名是housedb class DBDA {public $host="localhost";public $uid=" ...
- 【Cocos2d-x Lua】数据库封装类型的操作
Lua数据库封装类型的操作 使用演示样例 lua代码: require("DB") -- 保存一个字符串(数据库中存储的数据都是以字符串的形式保存的) DB:getInstance ...
- 数据库周刊28│开发者最喜爱的数据库是什么?阿里云脱口秀聊程序员转型;MySQL update误操作;PG流复制踩坑;PG异机归档;MySQL架构选型;Oracle技能表;Oracle文件损坏处理……
热门资讯 1.Stackoverflow 2020年度报告出炉!开发者最喜爱的数据库是什么?[摘要]2020年2月,近6.5万名开发者参与了 Stackoverflow 的 2020 年度调查,这份报 ...
- XPages访问关系型数据库技术与最佳实践
XPage 对于 Domino 开发人员的一大好处就是能够很方便和高效的访问关系型数据库.本文通过实例代码展现了在 XPage 中访问关系型数据库的具体步骤 , 同时讲解了一些在 XPage 中高效访 ...
- Cobar分布式数据库的应用与实践
最新文章:看我如何快速学习.Net(高可用数据采集平台).高并发数据采集的架构应用(Redis的应用) 问题点: 随着项目的增长,数据和数据表也成倍的增长,普通的单点数据库已经无法满足日常的增长的需要 ...
- 单KEY业务,数据库水平切分架构实践 | 架构师之路
https://mp.weixin.qq.com/s/8aI9jS0SXJl5NdcM3TPYuQ 单KEY业务,数据库水平切分架构实践 | 架构师之路 原创: 58沈剑 架构师之路 2017-06- ...
- 单机数据库优化的一些实践(mysql)
数据库优化有很多可以讲,按照支撑的数据量来分可以分为两个阶段:单机数据库和分库分表,前者一般可以支撑500W或者10G以内的数据,超过这个值则需要考虑分库分表.另外,一般大企业面试往往会从单机数据库问 ...
- mysql数据库封装
<?php /** * name: sql操作封装,可扩展 . * User: 张云山 * Date: 2016/9/4 * Time: 22:02 */ //php文件编码设置header(' ...
随机推荐
- 手把手教你如何给 Docker 开启 IPv6 网络支持
Docker 默认是不开启 IPv6 支持的,但是我们某些业务往往又需要 IPv6 的支持,特别是 IPv6 普及大势所趋,本文主要介绍的是如何开启 Docker 桥接网络 IPv6 支持,这篇文章具 ...
- Linux如何从命令行卡死的进程中退出?
Linux如何从命令行卡死的进程中退出? 不知道大家在使用Linux的时候,会不会遇到一些命令,有可能卡顿,有可能执行时间过长(比如使用 find 查找某个文件),这个时候我不想继续执行这个命令了,说 ...
- ro在xe10.3上的安装
在学习研究RO. RO9.2.101.1295在xe10.3上安装遇到新问题.记录处理的办法: 没有采用执行exe安装的方法.而是采用复制源代码后编译安装. 1.把生成的bpl.dcp安装到默认目录, ...
- nodejs参数的处理与用户的交互
解析脚本参数 作为脚本或者命令行工具,一般都需要支持不同的用户参数.默认参数被保存在process.argv的数组中,如下: [ nodeBinary, script, arg0, arg1, ... ...
- Unity Mask原理及自定义遮罩
主要内容 StencilBuffer是什么? 自定义Shader来实现遮罩 Unity Mask的原理 1.什么是StencilBuffer GPU在渲染前会为每个像素点分配一个1字节(8位)大小的内 ...
- Python科学计算系列9—逻辑代数
1.基本定理的验证 代码如下: from sympy import * A, B, C = symbols('A B C') # 重叠律 # A·A=A A+A=A print(to_cnf(A | ...
- 匿名内部类、lambda匿名函数表达式
a.匿名内部类的定义格式: 接口名称 对象名 = new 接口名称(){ //覆盖重写所有抽象方法 }: 一. /** * lambda匿名函数的使用 * Lambda省去面向对象的条条框框,格式由3 ...
- Linux防火墙操作指令
firewall-cmd --list-ports # 查看开放的端口号 firewall-cmd --zone=public --add-port=8888/tcp --permanent # 开放 ...
- grequests,并发执行接口请求的方法(简易版)
有时候需要处理很多请求,显然,一个一个去处理是要花费很多时间的 我们就需要用到并发的方式,python并发请求的方法很多,从简单到复杂. 本案例,介绍一个超级简单,使用grequests库,实现并发请 ...
- 图解-六种常见开源协议的比较(GPL,Mozilla,LGPL,BSD,Apache,MIT)
附: 五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)