better-sqlite3をプリペアドステートメント、トランザクション、専用のリポジトリとともに使い、速く安全な永続化を実現しましょう。
このレッスンを Kodokon で開くSQLiteはあなたのプロセスの中で動きます。サーバーもネットワークもなく、クエリごとのレイテンシはマイクロ秒のオーダーです。それこそがbetter-sqlite3の同期的なAPIを、許容できるどころか、しばしば非同期ドライバーより速くしている理由です。ティックより短い読み取りのために、プロミスとイベントループの一巡の代償を払う意味はありません。トレードオフは書き込みで現実になります。遅いクエリはループをブロックするのです。クエリにインデックスを張って短く保てば、SQLiteは毎秒数万回の読み取りをこなします。
npm install better-sqlite3データベースを開くとき、二つの設定が違いを生みます。WALモード(ライトアヘッドロギング)は、ファイル全体をロックする代わりに、書き込み中の読み取りを許します。そしてスキーマはCREATE TABLE IF NOT EXISTSで冪等に適用されます。本物のマイグレーションツールを導入する前なら、これで十分すぎるほどです。
import Database from 'better-sqlite3';
export function createDb(path = 'app.db') {
const db = new Database(path);
db.pragma('journal_mode = WAL');
db.exec(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL
)`);
return db;
}データアクセス層、すなわちリポジトリは、SQLを話す唯一のモジュールです。クエリは構築時に一度だけ準備され、その後は呼び出しごとに再利用されます。エンジンはSQLテキストを再解析せず、値はプレースホルダー?を通って渡され、決して連結を通りません。これはまさに、前のレッスンのサービスが期待するfindAll、findByEmail、insertというインターフェースです。
export function createUserRepository(db) {
const insertStmt = db.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)',
);
const byEmailStmt = db.prepare(
'SELECT * FROM users WHERE email = ?',
);
const allStmt = db.prepare(
'SELECT * FROM users ORDER BY id',
);
return {
insert(user) {
const info = insertStmt.run(user.email, user.name);
return { id: info.lastInsertRowid, ...user };
},
findByEmail(email) {
return byEmailStmt.get(email);
},
findAll() {
return allStmt.all();
},
};
}import { createDb } from './connection.js';
const db = createDb();
const insertStmt = db.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)',
);
const insertMany = db.transaction((users) => {
for (const user of users) {
insertStmt.run(user.email, user.name);
}
});
insertMany([
{ email: 'ada@example.com', name: 'Ada' },
{ email: 'linus@example.com', name: 'Linus' },
]);?プレースホルダーは何を保証しますか?db.transactionは千回のインサートに対して、どんな二重の利点をもたらしますか?