ใช้ better-sqlite3 พร้อม prepared statement, ทรานแซกชัน และ repository เฉพาะทาง เพื่อการเก็บข้อมูลถาวรที่รวดเร็วและปลอดภัย
เปิดบทเรียนนี้ใน KodokonSQLite ทำงาน ภายในโปรเซสของคุณ ไม่มีเซิร์ฟเวอร์ ไม่มีเครือข่าย ความหน่วงต่อคำสั่งอยู่ในระดับไมโครวินาที นั่นคือสิ่งที่ทำให้ API แบบ ซิงโครนัส ของ better-sqlite3 ไม่เพียงแต่ยอมรับได้ แต่มักเร็วกว่าไดรเวอร์แบบอะซิงโครนัสด้วยซ้ำ ไม่มีเหตุผลที่จะต้องจ่ายค่า promise และการวนรอบ event loop ให้กับการอ่านที่สั้นกว่าหนึ่ง tick การแลกเปลี่ยนนั้นมีจริงในฝั่งการเขียน คือ คำสั่งที่ช้าจะบล็อกลูป จงทำให้คำสั่งของคุณมีดัชนีและสั้น แล้ว SQLite จะรับมือการอ่านได้หลายหมื่นครั้งต่อวินาที
npm install better-sqlite3เมื่อเปิดฐานข้อมูล มีการตั้งค่าสองอย่างที่สร้างความแตกต่าง โหมด WAL (write-ahead logging) อนุญาตให้อ่านได้ระหว่างที่กำลังเขียน แทนที่จะล็อกทั้งไฟล์ และสคีมาถูกนำมาใช้แบบ idempotent ด้วย CREATE TABLE IF NOT EXISTS ซึ่งดีเพียงพอแล้วก่อนที่คุณจะนำเครื่องมือ migration จริงจังมาใช้
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;
}ชั้นการเข้าถึงข้อมูล คือ repository เป็นมอดูลเดียวที่พูดภาษา SQL คำสั่งถูกเตรียม (prepare) ไว้ ครั้งเดียว ตอนสร้าง แล้วนำกลับมาใช้ซ้ำในทุกการเรียก เอนจินไม่ต้องแจงข้อความ SQL ใหม่ และค่าต่าง ๆ ถูกส่งผ่าน placeholder ? ไม่ใช่ผ่านการต่อสตริง มันคืออินเทอร์เฟซ findAll, findByEmail, insert ที่ service จากบทที่แล้วคาดหวังไว้พอดี
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' },
]);? ใน prepared statement รับประกันอะไร?db.transaction ให้ประโยชน์สองต่ออะไรบ้างสำหรับการ insert พันครั้ง?