Bun 原生实现了高性能的 SQLite3 驱动。要使用它,请从内置的 bun:sqlite 模块导入。
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
const query = db.query("select 'Hello world' as message;");
query.get(); // => { message: "Hello world" }
API 简单、同步且速度快。感谢 better-sqlite3 及其贡献者为 bun:sqlite API 带来的启发。
功能包括
- 事务
- 参数(命名和位置)
- 预编译语句
- 数据类型转换(
BLOB变为Uint8Array) - 无需 ORM 即可将查询结果映射到类 -
query.as(MyClass) - JavaScript 中任何 SQLite 驱动程序中最快的性能
bigint支持- 单次调用 database.run(query) 中的多查询语句(例如
SELECT 1; SELECT 2;)
对于读取查询,bun:sqlite 模块比 better-sqlite3 快约 3-6 倍,比 deno.land/x/sqlite 快 8-9 倍。每个驱动程序都针对 Northwind Traders 数据集进行了基准测试。查看并运行 基准测试源码。

数据库
打开或创建 SQLite3 数据库
import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite");
打开内存数据库
import { Database } from "bun:sqlite";
// all of these do the same thing
const db = new Database(":memory:");
const db = new Database();
const db = new Database("");
以 readonly 模式打开
import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite", { readonly: true });
如果文件不存在,则创建数据库
import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite", { create: true });
严格模式
自 Bun v1.1.14 起添加
默认情况下,bun:sqlite 要求绑定参数包含 $、: 或 @ 前缀,并且在参数缺失时不会抛出错误。
要改为在参数缺失时抛出错误并允许在没有前缀的情况下进行绑定,请在 Database 构造函数上设置 strict: true。
import { Database } from "bun:sqlite";
const strict = new Database(
":memory:",
{ strict: true }
);
// throws error because of the typo:
const query = strict
.query("SELECT $message;")
.all({ message: "Hello world" });
const notStrict = new Database(
":memory:"
);
// does not throw error:
notStrict
.query("SELECT $message;")
.all({ message: "Hello world" });
通过 ES 模块导入加载
您也可以使用 import attribute 来加载数据库。
import db from "./mydb.sqlite" with { "type": "sqlite" };
console.log(db.query("select * from users LIMIT 1").get());
这等同于以下内容
import { Database } from "bun:sqlite";
const db = new Database("./mydb.sqlite");
.close(throwOnError: boolean = false)
要关闭数据库连接,但允许现有查询完成,请调用 .close(false)。
const db = new Database();
// ... do stuff
db.close(false);
要关闭数据库并在存在任何挂起查询时抛出错误,请调用 .close(true)。
const db = new Database();
// ... do stuff
db.close(true);
注意:close(false) 在数据库被垃圾回收时自动调用。可以安全地多次调用,但第一次之后将不起作用。
using 语句
您可以使用 using 语句来确保在退出 using 块时关闭数据库连接。
import { Database } from "bun:sqlite";
{
using db = new Database("mydb.sqlite");
using query = db.query("select 'Hello world' as message;");
console.log(query.get()); // => { message: "Hello world" }
}
.serialize()
bun:sqlite 支持 SQLite 内置的将数据库 序列化 和 反序列化 到和从内存的机制。
const olddb = new Database("mydb.sqlite");
const contents = olddb.serialize(); // => Uint8Array
const newdb = Database.deserialize(contents);
内部,.serialize() 调用 sqlite3_serialize。
.query()
在您的 Database 实例上使用 db.query() 方法来 准备 SQL 查询。结果是一个 Statement 实例,它将在 Database 实例上缓存。查询不会被执行。
const query = db.query(`select "Hello world" as message`);
注意 — 使用 .prepare() 方法来准备一个查询,而不将其缓存到 Database 实例上。
// compile the prepared statement
const query = db.prepare("SELECT * FROM foo WHERE bar = ?");
WAL 模式
SQLite 支持 写前日志模式 (WAL),它极大地提高了性能,尤其是在许多并发读取器和单个写入者的情况下。对于大多数典型应用程序,广泛建议启用 WAL 模式。
要启用 WAL 模式,请在应用程序的开头运行此 pragma 查询。
db.exec("PRAGMA journal_mode = WAL;");
什么是 WAL 模式
语句
Statement 是一个预编译的查询,这意味着它已经被解析并编译成高效的二进制格式。它可以被高性能地执行多次。
使用 Database 实例上的 .query 方法创建语句。
const query = db.query(`select "Hello world" as message`);
查询可以包含参数。这些可以是数字(?1)或命名($param 或 :param 或 @param)。
const query = db.query(`SELECT ?1, ?2;`);
const query = db.query(`SELECT $param1, $param2;`);
在执行查询时,值会被绑定到这些参数。Statement 可以使用几种不同的方法来执行,每种方法以不同的形式返回结果。
绑定值
要将值绑定到语句,请将一个对象传递给 .all()、.get()、.run() 或 .values() 方法。
const query = db.query(`select $message;`);
query.all({ $message: "Hello world" });
您也可以使用位置参数来绑定。
const query = db.query(`select ?1;`);
query.all("Hello world");
strict: true 允许您在不使用前缀的情况下绑定值。
自 Bun v1.1.14 起添加
默认情况下,在将值绑定到命名参数时,会**包含** $、: 和 @ 前缀。要在不使用这些前缀的情况下进行绑定,请在 Database 构造函数中使用 strict 选项。
import { Database } from "bun:sqlite";
const db = new Database(":memory:", {
// bind values without prefixes
strict: true,
});
const query = db.query(`select $message;`);
// strict: true
query.all({ message: "Hello world" });
// strict: false
// query.all({ $message: "Hello world" });
.all()
使用 .all() 来执行查询并以对象数组的形式返回结果。
const query = db.query(`select $message;`);
query.all({ $message: "Hello world" });
// => [{ message: "Hello world" }]
内部,此方法调用 sqlite3_reset 并重复调用 sqlite3_step 直到它返回 SQLITE_DONE。
.get()
使用 .get() 来执行查询并返回第一个结果作为对象。
const query = db.query(`select $message;`);
query.get({ $message: "Hello world" });
// => { $message: "Hello world" }
内部,此方法调用 sqlite3_reset,然后调用 sqlite3_step 直到它不再返回 SQLITE_ROW。如果查询没有返回行,则返回 undefined。
.run()
使用 .run() 来执行查询并返回 undefined。这对于修改架构的查询(例如 CREATE TABLE)或批量写入操作很有用。
const query = db.query(`create table foo;`);
query.run();
// {
// lastInsertRowid: 0,
// changes: 0,
// }
内部,此方法调用 sqlite3_reset 并调用 sqlite3_step 一次。当您不关心结果时,没有必要步进所有行。
自 Bun v1.1.14 起,.run() 返回一个具有两个属性的对象:lastInsertRowid 和 changes。
lastInsertRowid 属性返回最后插入数据库的行的 ID。changes 属性是受查询影响的行数。
.as(Class) - 将查询结果映射到类
自 Bun v1.1.14 起添加
使用 .as(Class) 来执行查询并以类的实例的形式返回结果。这允许您将方法和 getter/setter 附加到结果上。
class Movie {
title: string;
year: number;
get isMarvel() {
return this.title.includes("Marvel");
}
}
const query = db.query("SELECT title, year FROM movies").as(Movie);
const movies = query.all();
const first = query.get();
console.log(movies[0].isMarvel); // => true
console.log(first.isMarvel); // => true
作为性能优化,不会调用类构造函数,默认初始化程序也不会运行,私有字段也无法访问。这更像是使用 Object.create 而不是 new。类的原型被分配给对象,方法被附加,getter/setter 被设置,但构造函数不会被调用。
数据库列被设置为类实例上的属性。
.iterate() (@@iterator)
使用 .iterate() 来运行查询并逐步返回结果。这对于您想一次处理一行的大型结果集非常有用,而无需将所有结果加载到内存中。
const query = db.query("SELECT * FROM foo");
for (const row of query.iterate()) {
console.log(row);
}
您也可以使用 @@iterator 协议
const query = db.query("SELECT * FROM foo");
for (const row of query) {
console.log(row);
}
此功能已在 Bun v1.1.31 中添加。
.values()
使用 values() 来运行查询并将所有结果作为数组的数组返回。
const query = db.query(`select $message;`);
query.values({ $message: "Hello world" });
query.values(2);
// [
// [ "Iron Man", 2008 ],
// [ "The Avengers", 2012 ],
// [ "Ant-Man: Quantumania", 2023 ],
// ]
内部,此方法调用 sqlite3_reset 并重复调用 sqlite3_step 直到它返回 SQLITE_DONE。
.finalize()
使用 .finalize() 来销毁一个 Statement 并释放与之关联的任何资源。一旦最终确定,Statement 就无法再次执行。通常,垃圾回收器会为您完成此操作,但在性能敏感的应用程序中,显式最终确定可能很有用。
const query = db.query("SELECT title, year FROM movies");
const movies = query.all();
query.finalize();
.toString()
在 Statement 实例上调用 toString() 会打印展开的 SQL 查询。这对于调试很有用。
import { Database } from "bun:sqlite";
// setup
const query = db.query("SELECT $param;");
console.log(query.toString()); // => "SELECT NULL"
query.run(42);
console.log(query.toString()); // => "SELECT 42"
query.run(365);
console.log(query.toString()); // => "SELECT 365"
内部,这将调用 sqlite3_expanded_sql。参数使用最近绑定的值进行展开。
参数
查询可以包含参数。这些可以是数字(?1)或命名($param 或 :param 或 @param)。在执行查询时将值绑定到这些参数。
const query = db.query("SELECT * FROM foo WHERE bar = $bar");
const results = query.all({
$bar: "bar",
});
[
{ "$bar": "bar" }
]
数字(位置)参数也可以工作
const query = db.query("SELECT ?1, ?2");
const results = query.all("hello", "goodbye");
[
{
"?1": "hello",
"?2": "goodbye"
}
]
整数
sqlite 支持带符号的 64 位整数,但 JavaScript 只支持带符号的 52 位整数或带有 bigint 的任意精度整数。
bigint 输入在所有地方都受支持,但默认情况下 bun:sqlite 返回的整数是 number 类型。如果您需要处理大于 2^53 的整数,请在创建 Database 实例时将 safeIntegers 选项设置为 true。这也会验证传递给 bun:sqlite 的 bigint 不超过 64 位。
默认情况下,bun:sqlite 返回的整数是 number 类型。如果您需要处理大于 2^53 的整数,您可以使用 bigint 类型。
safeIntegers: true
自 Bun v1.1.14 起添加
当 safeIntegers 为 true 时,bun:sqlite 会将整数作为 bigint 类型返回。
import { Database } from "bun:sqlite";
const db = new Database(":memory:", { safeIntegers: true });
const query = db.query(
`SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`,
);
const result = query.get();
console.log(result.max_int); // => 9007199254741093n
当 safeIntegers 为 true 时,如果绑定参数中的 bigint 值超过 64 位,bun:sqlite 将抛出错误。
import { Database } from "bun:sqlite";
const db = new Database(":memory:", { safeIntegers: true });
db.run("CREATE TABLE test (id INTEGER PRIMARY KEY, value INTEGER)");
const query = db.query("INSERT INTO test (value) VALUES ($value)");
try {
query.run({ $value: BigInt(Number.MAX_SAFE_INTEGER) ** 2n });
} catch (e) {
console.log(e.message); // => BigInt value '81129638414606663681390495662081' is out of range
}
safeIntegers: false (默认)
当 safeIntegers 为 false 时,bun:sqlite 将整数作为 number 类型返回,并截断超过 53 位的任何位。
import { Database } from "bun:sqlite";
const db = new Database(":memory:", { safeIntegers: false });
const query = db.query(
`SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`,
);
const result = query.get();
console.log(result.max_int); // => 9007199254741092
事务
事务是一种以原子方式执行多个查询的机制;也就是说,要么所有查询都成功,要么都不成功。使用 db.transaction() 方法创建事务。
const insertCat = db.prepare("INSERT INTO cats (name) VALUES ($name)");
const insertCats = db.transaction(cats => {
for (const cat of cats) insertCat.run(cat);
});
在这个阶段,我们还没有插入任何猫!调用 db.transaction() 会返回一个新函数(insertCats),该函数包装了执行查询的函数。
要执行事务,请调用此函数。所有参数都将传递给包装的函数;包装函数的返回值将由事务函数返回。包装的函数还可以访问在事务执行处定义的 this 上下文。
const insert = db.prepare("INSERT INTO cats (name) VALUES ($name)");
const insertCats = db.transaction(cats => {
for (const cat of cats) insert.run(cat);
return cats.length;
});
const count = insertCats([
{ $name: "Keanu" },
{ $name: "Salem" },
{ $name: "Crookshanks" },
]);
console.log(`Inserted ${count} cats`);
驱动程序将在调用 insertCats 时自动 begin 事务,并在包装函数返回时 commit 它。如果抛出异常,事务将被回滚。异常将按原样传播;它不会被捕获。
嵌套事务 — 事务函数可以从其他事务函数内部调用。进行此操作时,内部事务将成为一个 savepoint。
查看嵌套事务示例
事务还附带 deferred、immediate 和 exclusive 版本。
insertCats(cats); // uses "BEGIN"
insertCats.deferred(cats); // uses "BEGIN DEFERRED"
insertCats.immediate(cats); // uses "BEGIN IMMEDIATE"
insertCats.exclusive(cats); // uses "BEGIN EXCLUSIVE"
.loadExtension()
要加载 SQLite 扩展,请在您的 Database 实例上调用 .loadExtension(name)。
import { Database } from "bun:sqlite";
const db = new Database();
db.loadExtension("myext");
macOS 用户
.fileControl(cmd: number, value: any)
要使用高级 sqlite3_file_control API,请在您的 Database 实例上调用 .fileControl(cmd, value)。
import { Database, constants } from "bun:sqlite";
const db = new Database();
// Ensure WAL mode is NOT persistent
// this prevents wal files from lingering after the database is closed
db.fileControl(constants.SQLITE_FCNTL_PERSIST_WAL, 0);
value 可以是
numberTypedArrayundefined或null
Reference
class Database {
constructor(
filename: string,
options?:
| number
| {
readonly?: boolean;
create?: boolean;
readwrite?: boolean;
},
);
query<Params, ReturnType>(sql: string): Statement<Params, ReturnType>;
run(
sql: string,
params?: SQLQueryBindings,
): { lastInsertRowid: number; changes: number };
exec = this.run;
}
class Statement<Params, ReturnType> {
all(params: Params): ReturnType[];
get(params: Params): ReturnType | undefined;
run(params: Params): {
lastInsertRowid: number;
changes: number;
};
values(params: Params): unknown[][];
finalize(): void; // destroy statement and clean up resources
toString(): string; // serialize to SQL
columnNames: string[]; // the column names of the result set
columnTypes: string[]; // types based on actual values in first row (call .get()/.all() first)
declaredTypes: (string | null)[]; // types from CREATE TABLE schema (call .get()/.all() first)
paramsCount: number; // the number of parameters expected by the statement
native: any; // the native object representing the statement
as(Class: new () => ReturnType): this;
}
type SQLQueryBindings =
| string
| bigint
| TypedArray
| number
| boolean
| null
| Record<string, string | bigint | TypedArray | number | boolean | null>;
数据类型
| JavaScript 类型 | SQLite 类型 |
|---|---|
string | TEXT |
number | INTEGER 或 DECIMAL |
boolean | INTEGER (1 或 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |