TypeScript中将SQLite查询结果反序列化为类型化对象的教程


typescript中将sqlite查询结果反序列化为类型化对象的教程

本教程将指导您如何在TypeScript应用中,特别是使用`sqlite3`库时,将从SQLite数据库查询到的原始数据行高效且类型安全地反序列化为预定义的TypeScript类实例。文章重点讲解了`sqlite3.all()`方法的异步特性、Promise的正确使用方式,以及如何迭代并映射数据库返回的行数据以构建类型化的对象数组,确保数据处理的健壮性和可维护性。

引言:数据库结果与类型安全

在现代TypeScript应用开发中,与数据库交互是常见任务。从数据库中检索数据后,我们通常希望将其转换为应用程序中定义的、具有明确结构的TypeScript对象或接口实例,以利用TypeScript的类型检查优势,提高代码的可读性、可维护性和健壮性。

然而,像sqlite3这样的数据库驱动程序,其查询方法(例如all())通常返回的是原始的、松散类型的数据行数组。直接使用这些原始数据往往会导致类型不明确,甚至运行时错误。此外,数据库操作本质上是异步的,这要求我们采用适当的异步编程模式来处理结果。本教程将以sqlite3为例,详细讲解如何克服这些挑战,实现从SQLite查询结果到TypeScript类型化对象的无缝反序列化。

理解 sqlite3.all() 方法的异步特性

sqlite3库中的all()方法用于执行SQL语句并获取所有匹配的行。然而,它并不是一个同步方法,它不会立即返回查询结果。根据node-sqlite3的API文档,all()方法提供的是一个回调式API:它接受一个回调函数作为参数,当查询完成时,该回调函数会被调用,并传入可能发生的错误和查询结果行。

值得注意的是,all()方法本身的返回值是Statement对象,这允许进行链式调用,但它并不是我们期望的数据结果。因此,直接在all()调用之后尝试访问结果是无效的,因为查询可能尚未完成。

为了在TypeScript/J*aScript环境中优雅地处理这种异步行为,我们应该将其封装在一个Promise中,或者使用async/await语法糖。

MarketingBlocks AI MarketingBlocks AI

AI营销助理,快速创建所有的营销物料。

MarketingBlocks AI 27 查看详情 MarketingBlocks AI

构建类型安全的查询函数

假设我们有一个名为Obj的TypeScript接口或类,它代表了数据库中ObjTable表的结构:

interface Obj {
  id: number;
  name: string;
  amount: number;
}

我们的目标是编写一个函数,能够从ObjTable中读取所有行,并将它们转换为Obj类型的数组。

1. 使用 Promise 封装异步操作

由于sqlite3.all()是异步的,我们需要使用Promise来封装它,以便能够以同步的方式处理其结果(例如使用.then()或await)。

import * as sqlite3 from 'sqlite3';

// 假设 db 是已初始化的 sqlite3 数据库实例
const db = new sqlite3.Database(':memory:'); // 示例:使用内存数据库

// 创建表(为完整示例提供)
export const CreateObjTable = (): Promise<void> => {
  return new Promise((resolve, reject) => {
    const query = db.prepare(`
      CREATE TABLE IF NOT EXISTS ObjTable
      (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        amount INTEGER
      )
    `);
    query.run((err) => {
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
};

// 获取所有 Obj 对象的函数
export const GetAllObjs = (): Promise<Obj[]> => {
  const query = db.prepare("SELECT * FROM ObjTable");

  return new Promise((resolve, reject) => {
    let objs: Obj[] = []; // 初始化一个空数组来存储类型化的对象

    // 调用 query.all(),并传入回调函数
    query.all((err, rows) => {
      if (err) {
        // 如果发生错误,拒绝 Promise
        return reject(err);
      }

      // 确保 rows 是一个数组,并正确迭代
      // rows 的类型通常是 any[],我们需要将其断言为 Obj[] 或在内部手动映射
      for (const row of rows as any[]) { // 使用 for...of 迭代数组元素
        objs.push({
          id: row.id,
          name: row.name,
          amount: row.amount,
        } as Obj); // 将原始行数据映射到 Obj 类型
      }

      // 查询成功且数据映射完成,解决 Promise 并返回类型化数组
      resolve(objs);
    });
  });
};

2. 核心逻辑解析

  • return new Promise((resolve, reject) => { ... });: 这是将回调式API转换为Promise式API的标准模式。resolve函数用于在操作成功时返回结果,reject函数用于在操作失败时返回错误。
  • query.all((err, rows) => { ... });: 这是sqlite3.all()方法的回调函数。
    • err: 如果查询过程中发生错误,此参数将包含错误对象。我们应该检查它并在存在错误时调用reject(err)。
    • rows: 这是一个数组,其中包含查询返回的所有数据行。每一行都是一个普通J*aScript对象,其属性名对应于SQL查询中的列名。
  • for (const row of rows as any[]) { ... }:
    • for...of vs for...in: 原始问题中使用了for...in,它会迭代对象的(对于数组来说是索引),而不是值。例如,for(const row in rows)会使row变量依次为"0", "1", "2"等字符串。正确的做法是使用for...of来直接迭代数组的元素
    • rows as any[]: rows参数的实际类型通常是any[],因为它是一个通用的数据结构。我们可以将其断言为any[],然后在循环内部将每个row对象映射到我们预期的Obj类型。
  • objs.push({ id: row.id, name: row.name, amount: row.amount } as Obj);:
    • 在循环内部,row变量现在代表了数据库中的一行数据(一个普通的J*aScript对象)。我们可以直接通过属性名(例如row.id、row.name)访问其列值。
    • 我们创建一个新的Obj字面量对象,将row的属性值赋给它,并通过as Obj进行类型断言,确保我们正在构建一个符合Obj接口的对象。
  • resolve(objs);: 当所有行都被成功处理并映射到Obj数组后,我们调用resolve(objs)来完成Promise,并将最终的类型化对象数组返回给调用者。

示例代码

为了提供完整的上下文,以下是包含表创建、数据插入和数据查询的完整示例:

import * as sqlite3 from 'sqlite3';

// 定义数据模型
interface Obj {
  id: number;
  name: string;
  amount: number;
}

// 假设 db 是已初始化的 sqlite3 数据库实例
// 实际应用中,你可能从外部配置或单例模式获取 db 实例
const db = new sqlite3.Database(':memory:', (err) => {
  if (err) {
    console.error('Error opening database', err.message);
  } else {
    console.log('Connected to the SQLite database.');
  }
});

// 创建 ObjTable 表
export const CreateObjTable = (): Promise<void> => {
  return new Promise((resolve, reject) => {
    db.run(`
      CREATE TABLE IF NOT EXISTS ObjTable
      (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        amount INTEGER
      )
    `, (err) => {
      if (err) {
        reject(err);
      } else {
        console.log('ObjTable created or already exists.');
        resolve();
      }
    });
  });
};

// 插入示例数据
export const InsertObj = (name: string, amount: number): Promise<void> => {
  return new Promise((resolve, reject) => {
    db.run(`INSERT INTO ObjTable (name, amount) VALUES (?, ?)`, [name, amount], function(err) {
      if (err) {
        reject(err);
      } else {
        console.log(`A row has been inserted with rowid ${this.lastID}`);
        resolve();
      }
    });
  });
};

// 获取所有 Obj 对象的函数
export const GetAllObjs = (): Promise<Obj[]> => {
  return new Promise((resolve, reject) => {
    const objs: Obj[] = []; // 初始化一个空数组来存储类型化的对象

    db.all("SELECT id, name, amount FROM ObjTable", (err, rows: any[]) => {
      if (err) {
        return reject(err); // 如果发生错误,拒绝 Promise
      }

      // 迭代查询结果,将原始行数据映射到 Obj 类型
      for (const row of rows) {
        objs.push({
          id: row.id,
          name: row.name,
          amount: row.amount,
        });
      }

      resolve(objs); // 查询成功且数据映射完成,解决 Promise
    });
  });
};

// 示例使用
async function main() {
  try {
    await CreateObjTable();
    await InsertObj('Item A', 100);
    await InsertObj('Item B', 250);
    await InsertObj('Item C', 75);

    const allObjs = await GetAllObjs();
    console.log('Retrieved Objects:', allObjs);

    // 验证类型安全
    allObjs.forEach(obj => {
      console.log(`ID: ${obj.id}, Name: ${obj.name}, Amount: ${obj.amount}`);
      // obj.nonExistentProperty = 'error'; // 这会在编译时报错,体现了类型安全
    });

  } catch (error) {
    console.error('An error occurred:', error);
  } finally {
    db.close((err) => {
      if (err) {
        console.error('Error closing database', err.message);
      } else {
        console.log('Database connection closed.');
      }
    });
  }
}

main();

注意事项与最佳实践

  1. 异步处理优先: 始终将数据库操作视为异步任务。在现代TypeScript/J*aScript中,推荐使用async/await语法来编写更具可读性的异步代码,它在底层依然是基于Promise的。
  2. 错误处理: 在Promise的reject回调中捕获并处理数据库操作可能出现的错误。在async/await模式下,可以使用try...catch块。
  3. 类型断言与数据校验: 尽管as Obj可以帮助编译器理解数据类型,但它并不能在运行时提供真正的类型校验。如果数据源不可信(例如来自外部API),在映射之前进行运行时数据校验(如使用Zod、Joi等库)是更健壮的做法。
  4. SQL 注入防护: 在插入或更新数据时,务必使用参数化查询(如示例中的db.run(sql, [value1, value2])),而不是直接拼接字符串,以防止SQL注入攻击。
  5. ORM/Query Builder: 对于更复杂的数据库交互和大型项目,考虑使用ORM(Object-Relational Mapping)库(如TypeORM, Prisma)或查询构建器(如Knex.js)。它们可以进一步抽象数据库操作,提供更高级的类型安全和开发效率。
  6. 数据库连接管理: 确保数据库连接的正确打开和关闭。在生产环境中,通常会使用连接池来管理数据库连接,提高性能和资源利用率。

总结

将SQLite查询结果反序列化为TypeScript类型化对象是构建健壮、可维护应用程序的关键一步。通过深入理解sqlite3.all()等方法的异步特性,并结合Promise的封装、for...of的正确迭代以及明确的类型映射,我们可以有效地将原始数据库数据转换为我们应用程序所需的强类型结构。遵循本教程中的指导和最佳实践,将有助于您在TypeScript项目中实现高效且类型安全的数据库交互。

以上就是TypeScript中将SQLite查询结果反序列化为类型化对象的教程的详细内容,更多请关注其它相关文章!


# 将其  # 微博营销推广公司模式  # 外贸网站建设骗局案例  # 磐安建设工程招标网站  # 江北普通网站建设  # wh网站建设  # 浙江宁波网站优化价格  # 充电器推广营销策略论文  # 宝山区定制网站建设  # 靠谱网站建设源码  # 网站专业建设价钱  # 这是  # 行数  # 数据结构  # 转换为  # 是一个  # javascript  # 的是  # 迭代  # 查询结果  # 回调  # 异步  # 应用开发  # sql注入  # ai  # 回调函数  # app  # typescript  # node  # js  # java 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 腾讯QQ邮箱官方入口 QQ邮箱网页版登录平台  如何在CSS中使用absolute实现登录弹窗居中_transform translate结合  支付宝登录刷脸不是本人如何解决  iPhone 13 mini如何清理Safari缓存_iPhone 13 mini浏览器缓存清理方法  PHP安全加载非公开目录图片与动态内容类型处理指南  从J*a应用程序中导出MySQL表数据的技术指南  《兴业银行》注册登录方法  ToDesk远程摄像头功能使用方法_ToDesk远程视频画面查看设置教程  QQ网页版入口导航 QQ网页版在线访问通道  漫蛙manwa2网页版书签同步链接_漫蛙manwa多设备登录入口  怎样设置开机后自动运行某个程序_Windows启动文件夹与任务计划【自动化】  Google Cloud Functions 时区处理指南:理解与最佳实践  Microsoft Edge网页字体太淡看不清怎么办_Microsoft Edge字体渲染优化技巧  C++ switch case字符串_C++如何实现字符串switch匹配  顺丰快递单号查询寄件人 顺丰寄件人查询入口  《长生:天机降世》火塔小怪大全  优化2xN网格最大路径和的动态规划算法实践  Excel如何快速找到并断开外部数据源链接_Excel外部数据源断开方法  iPhone14无法连接蓝牙设备如何解决  PHP中获取HTTP响应状态消息:方法与限制  曝《丝之歌》DLC有望开发!开发商还有神秘新企划  J*aScript调试技巧_性能分析与内存快照  Mac如何开启画中画模式_Mac Safari浏览器视频画中画功能  php如何实现多域名共享session_php存储session到redis与跨域读取配置  TikTok笔记文字无法编辑如何解决 TikTok笔记文字编辑优化方法  PHP中实现JSON数据数组分页的教程  AO3中文入口稳定分享_AO3官网HTTPS看文详解  windows10怎么设置电源按钮_windows10按下电源键功能修改  t3出行如何使用微信支付  composer licenses 命令:如何检查项目依赖的许可证?  C#解析并修改XML后保存 如何确保格式与编码的正确性  德邦快递收费标准详解  《随手记》备份数据方法  知乎APP怎么查看自己被邀请的问题_知乎APP邀请回答记录查看与参与方法  使用VS Code作为你的个人知识管理系统  包子漫画官网链接官方地址 包子漫画在线观看官网首页入口  金牛福袋获取攻略  猫眼电影app如何筛选支持退改签的影院_猫眼电影退改签影院筛选方法  抖音火山版注销账号抖音会注销吗 抖音火山版与抖音账号注销关系  如何配置VS Code作为您Git操作的默认编辑器  TikTok收藏夹无法删除视频如何解决 TikTok收藏管理优化方法  性能与资源监视器快捷打开  edge浏览器怎么修改语言为中文_Edge界面语言切换教程  Windows Audio服务启动失败怎么办_电脑没声音的终极服务修复法【修复】  如何查询国外邮政编码_国外邮政编码查询的多种有效途径  word文档中的分隔符有哪些不同类型和用途_Word分隔符类型与用途方法  Highcharts雷达图径向轴数值标签实现教程  汽水音乐车机版官网5.0 汽水音乐车机版5.0版本下载入口  TikTok网页版实时观看入口 TikTok网页版短视频在线浏览  b站怎么查看视频的码率_b站视频码率查看方法 

 2025-11-14

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.