前端进击全栈 sql 篇(上)

前言

在当今的软件开发领域,前端开发人员需要不断拓展自己的技能,从纯粹的前端知识逐步进阶到后端技术和数据库领域。特别是对于想要系统性学习和记录Node.js后端知识的开发者来说,理解SQL数据库是其中重要的一部分。接下来我会分几个章节系列系统性地学习分享相关 NodeJs、数据库相关的内容。

作为前端开发者,我们已经熟悉了客户端的技术栈,但随着互联网应用的复杂性不断增加,了解后端技术和数据库已成为必然趋势。Node.js作为一个基于JavaScript的后端技术,为我们提供了构建服务器端应用程序的便利环境,使我们可以更深入地参与整个软件开发过程。

同时,学习SQL数据库也是不可或缺的一部分。掌握SQL能够帮助我们有效地存储、查询和操作数据,为我们的应用程序提供稳定可靠的数据支持。这种全面的技能拓展将使我们能够更好地理解整个应用的架构,并为我们的职业发展打下坚实的基础。

github 相关项目

项目地址 First Sql 放在开头,你可以直接 clone github.com/guanwanxiao... 开始测试用例。

测试驱动开发 TDD

当学习SQL时,使用测试驱动开发(TDD)方法是一种高效的方式。通过结合使用Better-Sqlite3、Jest和Node.js,我们可以以TDD的方式学习SQL。Better-Sqlite3是一个轻量级的SQLite客户端,适用于Node.js环境。Jest是一个流行的JavaScript测试框架,我们可以使用它来编写和运行测试用例。

在本教程中,我们将介绍如何使用Better-Sqlite3、Jest和Node.js进行TDD学习SQL。我们将从简单的查询开始,逐步扩展到插入、更新和删除数据等更复杂的操作。通过编写测试用例,我们将逐步构建一个完整的SQL数据库应用程序,并学习如何处理事务、处理错误以及进行性能优化。 让我们开始使用Better-Sqlite3、Jest和Node.js进行TDD学习SQL的旅程吧!

Better-Sqlite3

当涉及到SQL学习和开发数据库应用程序时,我们可以选择不同的数据库客户端来满足不同的需求。Better-Sqlite3和MySQL是两种常见的数据库客户端,它们在一些方面有着相似之处,但也存在一些关键的差异。

首先,让我们来看一下Better-Sqlite3。它是一个轻量级的SQLite客户端,专为Node.js环境设计。与MySQL相比,SQLite是一种嵌入式数据库,它将整个数据库存储在单个文件中,不需要独立的服务器进程。这使得SQLite在处理小型到中型数据库应用时非常高效,而且使用起来非常简单。

Better-Sqlite3提供了简洁易用的API,可以通过直接与SQLite数据库文件交互来执行各种SQL查询和操作。它的性能非常出色,并且支持参数绑定、预编译语句和批量插入等功能,可以提高查询效率和减少资源消耗。

之所以选择 Better-Sqlite3,是与 MySQL相比,MySQL的配置和管理可能更加复杂。MySQL需要独立安装和配置服务器进程,并且需要考虑网络连接、权限控制和数据库维护等方面的问题。相比于此,Better-Sqlite3 就简单许多了,可以帮我们省去很多配置上的烦恼。

最后无论您选择哪个数据库客户端,对于学习和实践SQL影响并不大,选择你自己喜欢的数据库。

Better-Sqlite3 使用

1. 连接和创建数据库

JS 复制代码
const Database = require('better-sqlite3');
/**
 * 打开数据库文件,如果数据库文件不存在则创建
 * 同步行为
 */ 
const db = new Database('foobar.db', { verbose: console.log });

/**
 * 或者你可以通过传递:memory:作为第一个参数来创建一个内存数据库
 * 内存版的数据库不需要创建或连接本地数据库文件,所以不需要对应的文件名
 */
const db = new Database(':memory:')
console.log(db.open) // 查看数据库是否打开
db.close() // 关闭数据库,清理资源并确保测试的独立性和一致性。

2. 创建表插入查询数据

db.exec() 方法可以执行任何合法的 SQL 代码。它接受一个 SQL 字符串作为参数,并在 SQLite 数据库中执行该 SQL 语句。
db.prepare() 方法用于预编译 SQL 查询语句,并返回一个查询对象(statement object)。 预编译是指将 SQL 查询语句转换成一个查询模板,并生成一个执行计划。这样可以在后续的执行阶段中多次使用相同的查询模板,从而提高查询效率。

JS 复制代码
const Database = require('better-sqlite3');
const db = new Database(':memory:')
// 创建 students 表
db.exec(`
    CREATE TABLE students(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name VARCHAR(30),
        grade INT
    )
`)
// 表示单个 SQL 语句的对象。
const addStmt = db.prepare(`INSERT INTO students (name,grade) VALUES ('jack', 99)`)
const queryStmt = db.prepare(`SELECT * FROM students`)
addStmt.run() // 执行插入
queryStmt.all() // 查询所有结果

两者的区别 db.exec() 相当于直接执行 SQL 字符串,所以你可以同时执行多条,而 db.prepare() SQL 语句会被预编译成一个 SQL 语句模板,而不是被直接执行通常是单条。

所以 db.prepare() 会有以下优点:

  1. 预编译:prepare 方法将 SQL 语句进行预编译,将其转换为可执行的二进制代码。这样可以提高 SQL 查询的执行效率,并且可以重复使用已编译的查询计划,减少重复编译的开销。
  2. 参数绑定及复用:prepare 方法还允许将参数绑定到 SQL 语句中。通过使用占位符(例如 ?:name)来代替具体的参数值,然后通过 bind 方法将参数值与占位符关联起来。这样可以避免 SQL 注入攻击,并且可以轻松地在多次执行之间更改参数值。
  3. 性能优化:通过使用 prepare 方法编译 SQL 语句,可以利用 SQLite 数据库的内置编译器和优化器来提高查询的性能。这些优化器可以对查询计划进行优化,选择最佳的索引和执行策略,从而提高查询速度。

3.事务处理

SQLite 会开启一个新的事务,并将所有插入操作放到这个事务中执行。如果在执行过程中发生了错误,整个事务会被回滚,保证数据的一致性。否则,当插入操作全部执行完毕后,SQLite 会自动提交这个事务。

js 复制代码
const insert = db.prepare('INSERT INTO cats (name, age) VALUES (@name, @age)'); 
const insertMany = db.transaction((cats) => { 
    // 执行一组 sql 语句,如果其中有一个发生错误,整个事务会被回滚
    insert({ name: 'Joey', age: 2 });
    insert({ name: 'Sally', age: 4 })
}); 
insertMany()

4. 更多Api

更多 api 语法会在后续 TDD 测试中使用和介绍。比如

statement 对象存在的方法

  • run() 方法用于执行 INSERT、UPDATE 或 DELETE 操作,并返回一个代表执行结果的 Statement 对象。
  • all() 方法用于执行 SELECT 操作,并返回一个包含所有匹配记录的数组。
  • get() 方法用于执行 SELECT 操作,并返回第一条匹配记录。

Database api

  • Database#aggregate()
  • Database#table()
  • Database#loadExtension()
  • Database#exec()
  • Database#close()
  • Properties

jest

当您需要编写单元测试时,Jest是一个非常流行和强大的JavaScript测试框架。它被广泛用于测试各种前端和后端应用程序,包括Node.js项目。

Jest提供了易于使用的API和丰富的功能,使得编写和运行测试变得简单而高效。它支持自动化和并行执行测试,并提供了丰富的断言库、模拟和模拟函数、覆盖率报告等功能。

jest 钩子函数

  1. beforeAll(fn, timeout): 在所有测试用例执行之前执行的钩子函数。
  2. afterAll(fn, timeout): 在所有测试用例执行之后执行的钩子函数。
  3. beforeEach(fn, timeout): 在每个测试用例执行之前执行的钩子函数。
  4. afterEach(fn, timeout): 在每个测试用例执行之后执行的钩子函数。

在所有测试用例之前执行的钩子函数 beforeAll() 只会执行一次,而在每个测试用例之前执行的钩子函数 beforeEach() 会在每个测试用例执行前都会执行一次。它们的差别主要体现在执行次数和执行时机上。

举个例子,假设我们有两个测试用例 test1test2

js 复制代码
beforeAll(() => {
  console.log('在所有测试用例之前执行');
});

beforeEach(() => {
  console.log('在每个测试用例之前执行');
});

test('test1', () => {
  console.log('执行 test1');
});

test('test2', () => {
  console.log('执行 test2');
});

当我们运行这段代码时,输出结果如下:

在所有测试用例之前执行
在每个测试用例之前执行
执行 test1
在每个测试用例之前执行
执行 test2

组织测试代码

  1. describe(name, fn): 用于创建一个测试套件,可以包含多个相关的测试用例。name 参数是套件的描述信息,fn 参数是一个回调函数,用于编写套件中的测试用例。
  2. test(name, fn, timeout): 用于定义一个测试用例。name 参数是测试用例的描述信息,fn 参数是一个回调函数,用于编写测试用例的具体逻辑。可选的 timeout 参数用于设置测试用例的超时时间。
  3. expect(value): 用于断言测试结果是否符合预期。它提供了一系列的匹配器(matchers)来进行断言,例如 toBe()toEqual()toContain() 等。可以通过链式调用来组合多个断言。

开始单元测试

现在,让我们开始第一个单元测试。我们可以创建一个测试用例来验证Better-Sqlite3是否能够成功连接到数据库并执行查询。

js 复制代码
// 引入所需的库和模块
const sqlite = require('better-sqlite3');
const db = new sqlite(':memory:'); // 在内存中创建一个临时数据库

// 定义要测试的函数或类
function getUserById(id) {
  const query = db.prepare('SELECT * FROM users WHERE id = ?');
  return query.get(id);
}

// 编写测试用例
test('getUserById should return the correct user', () => {
  // 插入测试数据
  db.exec(`
    CREATE TABLE users (
      id INTEGER PRIMARY KEY,
      name TEXT,
      email TEXT
    );
    INSERT INTO users (id, name, email) VALUES (1, 'John', 'john@example.com');
  `);

  // 调用被测试的函数
  const user = getUserById(1);

  // 断言结果是否符合预期
  expect(user).toEqual({
    id: 1,
    name: 'John',
    email: 'john@example.com'
  });
});

在上述例子中,我们使用Jest编写了一个名为getUserById的函数,它会从数据库中获取指定ID的用户信息。然后,我们编写了一个测试用例来验证该函数是否返回了正确的用户对象。我们首先在临时数据库中创建了一个用户表,并插入了一条测试数据。接下来,我们调用getUserById函数并使用expect断言来验证返回结果是否与预期相符。

结尾

在下一篇文章中,我们将正式开始介绍 SQL 单元测试的内容。SQL 单元测试是确保数据库相关代码质量和正确性的重要手段。我们将探讨如何使用 Jest 和 better-sqlite3 来编写和运行 SQL 单元测试。我们将介绍 SQL 单元测试的基本概念,包括如何构造测试数据、如何编写测试用例以及如何验证查询结果的准确性。

相关推荐
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui
尝尝你的优乐美6 小时前
vue3.0中h函数的简单使用
前端·javascript·vue.js
windy1a7 小时前
【C语言】js写一个冒泡顺序
javascript
会发光的猪。7 小时前
如何使用脚手架创建一个若依框架vue3+setup+js+vite的项目详细教程
前端·javascript·vue.js·前端框架