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
) - 所有 JavaScript SQLite 驱动程序中最快的性能
对于读取查询,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("");
以只读
模式打开
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 });
通过 ES 模块导入加载
您还可以使用 import 属性来加载数据库。
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()
使用 .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();
// => undefined
在内部,这会调用 sqlite3_reset
并调用 sqlite3_step
一次。当您不关心结果时,无需遍历所有行。
.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"
}
]
事务
事务是一种以原子方式执行多个查询的机制;也就是说,所有查询都成功或都不成功。使用 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
它。如果抛出异常,事务将回滚。异常将按通常方式传播;它不会被捕获。
嵌套事务 — 事务函数可以从其他事务函数内部调用。这样做时,内部事务将变为保存点。
查看嵌套事务示例
事务还带有 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
可以是
数字
TypedArray
undefined
或null
参考
class Database {
constructor(
filename: string,
options?:
| number
| {
readonly?: boolean;
create?: boolean;
readwrite?: boolean;
},
);
query<Params, ReturnType>(sql: string): Statement<Params, ReturnType>;
}
class Statement<Params, ReturnType> {
all(params: Params): ReturnType[];
get(params: Params): ReturnType | undefined;
run(params: Params): void;
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
paramsCount: number; // the number of parameters expected by the statement
native: any; // the native object representing the statement
}
type SQLQueryBindings =
| string
| bigint
| TypedArray
| number
| boolean
| null
| Record<string, string | bigint | TypedArray | number | boolean | null>;
数据类型
JavaScript 类型 | SQLite 类型 |
---|---|
string | TEXT |
数字 | INTEGER 或 DECIMAL |
boolean | INTEGER (1 或 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |