better-sqlite3之exec方法

news2025/3/7 5:10:27

better-sqlite3 中,.exec() 方法用于执行包含多个 SQL 语句的字符串。与预编译语句相比,这种方法性能较差且安全性较低,但有时它是必要的,特别是当你需要从外部文件(如 SQL 脚本)中执行多个 SQL 语句时。

使用 .exec() 方法

以下是如何使用 .exec() 方法来执行从文件中读取的 SQL 脚本,并确保正确处理错误和事务回滚。

示例代码

假设你有一个名为 migrate-schema.sql 的 SQL 文件,其中包含多个 SQL 语句,以下是完整的示例代码:

-- 创建 users 表
CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 创建 posts 表
CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

-- 插入一些初始用户数据
INSERT INTO users (name, age) VALUES ('Alice', 28);
INSERT INTO users (name, age) VALUES ('Bob', 25);
INSERT INTO users (name, age) VALUES ('Charlie', 30);

-- 插入一些初始帖子数据
INSERT INTO posts (user_id, title, content) VALUES (1, 'My First Post', 'This is my first post.');
INSERT INTO posts (user_id, title, content) VALUES (1, 'Another Post', 'This is another post.');
INSERT INTO posts (user_id, title, content) VALUES (2, 'Hello World', 'Hello everyone!');

-- 更新 Alice 的年龄为 29
UPDATE users SET age = 29 WHERE name = 'Alice';

-- 删除 Bob 的所有帖子
DELETE FROM posts WHERE user_id = (SELECT id FROM users WHERE name = 'Bob');

-- 查询所有用户及其帖子
SELECT u.id AS user_id, u.name, p.id AS post_id, p.title, p.content
FROM users u
LEFT JOIN posts p ON u.id = p.user_id;
const fs = require('fs');
const path = require('path');
const Database = require('better-sqlite3');

// 打开数据库连接
const db = new Database('mydb.sqlite');

// 读取 SQL 文件内容
const migrationFilePath = path.join(__dirname, 'migrate-schema.sql');
const migration = fs.readFileSync(migrationFilePath, 'utf8');

try {
    // 开始事务
    db.exec('BEGIN TRANSACTION;');

    // 执行 SQL 文件中的所有语句
    db.exec(migration);

    // 提交事务
    db.exec('COMMIT;');
    console.log('Migration completed successfully.');
} catch (error) {
    // 如果发生错误,回滚事务
    db.exec('ROLLBACK;');
    console.error('An error occurred during migration:', error.message);
}

// 关闭数据库连接
db.close();
console.log('Database connection closed.');

详细解释

  1. 读取 SQL 文件

    • 使用 fs.readFileSync() 读取 SQL 文件的内容。这里我们使用 path.join() 来确保路径的兼容性。
  2. 开始事务

    • 在执行 SQL 语句之前,首先调用 db.exec('BEGIN TRANSACTION;') 开始一个事务。这可以确保所有的 SQL 语句要么全部成功,要么全部失败,从而保持数据的一致性。
  3. 执行 SQL 文件中的所有语句

    • 使用 db.exec(migration) 执行从文件中读取的所有 SQL 语句。注意,.exec() 可以执行包含多个 SQL 语句的字符串。
  4. 提交事务

    • 如果所有 SQL 语句都成功执行,则调用 db.exec('COMMIT;') 提交事务。
  5. 错误处理和事务回滚

    • 如果在执行 SQL 语句的过程中发生错误,捕获异常并调用 db.exec('ROLLBACK;') 回滚事务,以防止部分更新导致的数据不一致问题。
  6. 关闭数据库连接

    • 最后,调用 db.close() 关闭数据库连接。

错误处理和日志记录

为了更好地调试和维护,建议增加更多的错误处理和日志记录。例如,可以在捕获异常时记录详细的错误信息:

try {
    // 开始事务
    db.exec('BEGIN TRANSACTION;');

    // 执行 SQL 文件中的所有语句
    db.exec(migration);

    // 提交事务
    db.exec('COMMIT;');
    console.log('Migration completed successfully.');
} catch (error) {
    // 如果发生错误,回滚事务
    db.exec('ROLLBACK;');
    
    // 记录详细的错误信息
    console.error('An error occurred during migration:');
    console.error('Error message:', error.message);
    console.error('Stack trace:', error.stack);
}

注意事项

  • 安全性:由于 .exec() 直接执行 SQL 字符串,因此存在 SQL 注入的风险。尽量避免直接将用户输入插入到 .exec() 调用中。如果必须这样做,请先进行严格的验证和清理。

  • 性能:与预编译语句相比,.exec() 的性能较差。如果可能的话,尽量使用预编译语句来提高性能和安全性。

  • 事务管理:当执行多个 SQL 语句时,务必使用事务来确保数据一致性。如果没有使用事务,部分语句的成功执行可能会导致数据库处于不一致状态。

完整示例

以下是一个更完整的示例,展示了如何结合信号处理机制来确保在应用程序退出时正确关闭数据库连接:

const fs = require('fs');
const path = require('path');
const process = require('process');
const Database = require('better-sqlite3');

// 打开数据库连接
const db = new Database('mydb.sqlite');

// 监听进程退出事件和其他终止信号
function handleExit() {
    try {
        db.close();
        console.log('Database connection closed gracefully.');
    } catch (error) {
        console.error('Error closing database:', error.message);
    }
}

['exit', 'SIGINT', 'SIGTERM', 'SIGHUP'].forEach((signal) => {
    process.on(signal, handleExit);
});

// 读取 SQL 文件内容
const migrationFilePath = path.join(__dirname, 'migrate-schema.sql');
const migration = fs.readFileSync(migrationFilePath, 'utf8');

try {
    // 开始事务
    db.exec('BEGIN TRANSACTION;');

    // 执行 SQL 文件中的所有语句
    db.exec(migration);

    // 提交事务
    db.exec('COMMIT;');
    console.log('Migration completed successfully.');
} catch (error) {
    // 如果发生错误,回滚事务
    db.exec('ROLLBACK;');
    console.error('An error occurred during migration:');
    console.error('Error message:', error.message);
    console.error('Stack trace:', error.stack);
}

// 模拟长时间运行的任务
setTimeout(() => {
    console.log('Long-running task completed.');
}, 60000); // 1分钟

通过这种方式,你可以确保在任何情况下都能正确关闭数据库连接,并且在执行复杂的 SQL 脚本时保持数据的一致性和完整性。如果有更多问题或需要进一步的帮助,请随时提问!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2310867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【有啥问啥】深入浅出:大模型应用工具 Ollama 技术详解

深入浅出:大模型应用工具 Ollama 技术详解 引言 近年来,大型模型(Large Models,LLMs)技术突飞猛进,在自然语言处理、计算机视觉、语音识别等领域展现出强大的能力。然而,部署和运行这些庞大的…

利用opencv_python(pdf2image、poppler)将pdf每页转为图片

1、安装依赖pdf2image pip install pdf2image 运行.py报错,因为缺少了poppler支持。 2、安装pdf2image的依赖poppler 以上命令直接报错。 改为手工下载: github: Releases oschwartz10612/poppler-windows GitHub 百度网盘: 百度网盘…

大数据测试总结

总结测试要点: 参考产品文档,技术文档梳理以下内容 需求来源 业务方应用场景 数据源,数据格转,数据产出,数据呈现方式(数据消亡史),数据量级(增量,全量&am…

Redis面试常见问题——集群方案

Redis集群方案 在Redis中提供的集群方案总共有三种 主从复制 哨兵模式 分片集群 主从复制 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 主从数据同步原理 单节点Redis的并发能力是有…

Qt:day4

一、作业 1:实现绘图的时候,颜色的随时调整; 2:追加橡皮擦功能; 3:配合键盘事件,实现功能; 当键盘按 ctrlz 的时候,撤销最后一次绘图。 【Headers / widget.h】&#xff…

vue3之echarts仪表盘

vue3之echarts仪表盘 效果如下&#xff1a; 版本 "echarts": "^5.5.1" 核心代码&#xff1a; <template><div ref"chartRef" class"circle"></div> </template> <script lang"ts" setup>…

将PDF转为Word的在线工具

参考视频&#xff1a;外文翻译 文章目录 一、迅捷PDF转换器二、Smallpdf 一、迅捷PDF转换器 二、Smallpdf

MWC 2025|紫光展锐联手美格智能发布5G通信模组SRM812

在2025年世界移动通信大会&#xff08;MWC 2025&#xff09;期间&#xff0c;紫光展锐携手美格智能正式推出了基于紫光展锐V620平台的第二代5G Sub6G R16模组SRM812&#xff0c;以超高性价比方案&#xff0c;全面赋能合作伙伴&#xff0c;加速5G规模化应用在各垂直领域的全面落…

前端基础之ajax

vue-cli配置代理服务器解决跨域问题 我们可以使用一个代理服务器8080&#xff0c;Vue项目8080发送请求向代理服务器8080发送请求&#xff0c;再由在理服务器转发给后端服务器 首先需要在vue.config.js中配置代理服务器 const { defineConfig } require(vue/cli-service) modul…

【无标题】FrmImport

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;四、项目展示五、资源链接 前言 我能抽象出整个世界&#xff0c;但是我不能抽象你。 想让你成为私有常量&#xff0c;这样外部函数就无法访问你。 又想让你成为全局常量&#xff0c;这样在我的…

IP-Guard软件设置P2P升级功能

日常使用IP-Guard软件遇到客户端升级&#xff0c;需要从服务器下载升级包&#xff0c;为了让快速升级&#xff0c;可以配置参数&#xff0c;具体设置见下图&#xff1a; 控制台—策略—定制配置—新增 关键字&#xff1a;obt_dislble_p2p2 内容&#xff1a;2

【Mac】git使用再学习

目录 前言 如何使用github建立自己的代码库 第一步&#xff1a;建立本地git与远程github的联系 生成密钥 将密钥加入github 第二步&#xff1a;创建github仓库并clone到本地 第三步&#xff1a;上传文件 常见的git命令 git commit git branch git merge/git rebase …

java后端开发day27--常用API(二)正则表达式爬虫

&#xff08;以下内容全部来自上述课程&#xff09; 1.正则表达式&#xff08;regex&#xff09; 可以校验字符串是否满足一定的规则&#xff0c;并用来校验数据格式的合法性。 1.作用 校验字符串是否满足规则在一段文本中查找满足要求的内容 2.内容定义 ps&#xff1a;一…

【TCP/IP协议栈】【传输层】端口号、套接字、多路复用/分解、网络字节序

参考资料&#xff1a; 前言&#xff1a; 总结&#xff1a; 【计算机网络】套接字&#xff08;应用层和传输层之间的接口&#xff09; 套接字是一个通用的通信接口抽象不仅限于TCP/IP协议族作为应用层和传输层之间的桥梁支持多种通信方式和协议族 套接字定义 在 TCP 或者 UDP…

【漫话机器学习系列】120.参数化建模(Parametric Modeling)

参数化建模&#xff08;Parametric Modeling&#xff09;详解 1. 引言 在数据建模和机器学习中&#xff0c;参数化建模&#xff08;Parametric Modeling&#xff09;是一种广泛应用的建模方法。它通过假设一个函数形式来表达变量之间的关系&#xff0c;并估算该函数的参数&am…

Web3 的未来:去中心化如何重塑互联网

Web3 的未来&#xff1a;去中心化如何重塑互联网 在这个信息爆炸的时代&#xff0c;我们正站在一个新的技术革命的门槛上——Web3。Web3 不仅仅是一个技术术语&#xff0c;它代表了一种全新的互联网理念&#xff0c;即去中心化。这种理念正在逐步改变我们对互联网的使用方式和…

DApp开发从入门到精通:以太坊/Solana公链生态实战解析

在区块链技术的推动下&#xff0c;去中心化应用&#xff08;DApp&#xff09;逐渐摆脱传统中心化后台的依赖&#xff0c;转向以智能合约为核心的全合约化开发模式。这种模式通过区块链网络的分布式特性&#xff0c;实现了数据存储、业务逻辑与用户交互的完全去中心化。 一、全合…

【计算机网络入门】TCP拥塞控制

目录 1. TCP拥塞控制和TCP流量控制的区别 2. 检测到拥塞该怎么办 2.1 如何判断网络拥塞&#xff1f; 3. 慢开始算法 拥塞避免算法 4.快重传事件->快恢复算法 5. 总结 1. TCP拥塞控制和TCP流量控制的区别 TCP流量控制是控制端对端的数据发送量。是局部的概念。 TCP拥…

【Maven】入门介绍 与 安装、配置

文章目录 一、Maven简介1. Maven介绍2. Maven软件工作原理模型图 二、Maven安装和配置1. Maven安装2. Maven环境配置3. Maven功能配置4. IDEA配置本地Maven软件 一、Maven简介 1. Maven介绍 https://maven.apache.org/what-is-maven.html Maven 是一款为 Java 项目管理构建、…

springbootWeb入门--创建springbootweb项目

步骤&#xff1a; 1.建立空工程 2.选择项目的jdk版本 3.在工程中建立模块&#xff0c;选择“spring initilazer”,类型勾选“maven” 4.勾选“spring web”之后&#xff0c;就无需再自行写dependcy了。 5.等待联网下载 6.生成的工程文件&#xff0c;如下绿色框中文件&…