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
它。如果拋出異常,交易將會回滾。異常將照常傳播;它不會被捕獲。
巢狀交易 — 交易函數可以從其他交易函數內部呼叫。執行此操作時,內部交易將變為儲存點。
檢視巢狀交易範例
交易也提供 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
可以是
number
TypedArray
undefined
或null
參考
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
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 類型 |
---|---|
字串 | TEXT |
number | INTEGER 或 DECIMAL |
布林值 | INTEGER (1 或 0) |
Uint8Array | BLOB |
Buffer | BLOB |
bigint | INTEGER |
null | NULL |