Problemet med Booleans i SQLite
Hvis du nogensinde har arbejdet med SQLite, bør du være opmærksom på de understøttede datatyper og Boolean
er ikke en af dem. Mere specifikt som angivet her:
2.1. boolesk datatype
SQLite har ikke en separat boolesk lagerklasse. I stedet gemmes booleske værdier som heltal 0 (falsk) og 1 (sand).
SQLite genkender nøgleordene "TRUE" og "FALSE", fra og med version 3.23.0 (2018-04-02), men disse nøgleord er egentlig bare alternative stavemåder for de heltallige bogstaver henholdsvis 1 og 0.
De fleste JavaScript-biblioteker til SQLite3 understøtter ikke TRUE
og FALSE
nøgleord, og de kræver, at du forbereder udsagn i din kode ved hjælp af heltal. For eksempel, i better-sqlite3 skulle du gøre dette:
const payload = {
isActive: 1, // <======
username: 'Brad',
password: '1234',
email: '[email protected]',
};
const result = database
.prepare(
`INSERT INTO accounts(isActive, username, password, email) VALUES(@isActive, @username, @password, @email) `
)
.run({ bucketID, taskSiteID, name, username, password, email }).changes;
Brug af number
i stedet for boolean
på tværs af hele din app ville give en forfærdelig udvikleroplevelse (plus sandsynligvis bruge mere hukommelse).
Du kan bruge en hjælpefunktion til at transformere dine nyttelastobjekters boolean egenskaber til numre (Jeg havde faktisk gjort dette en gang tidligere), men så skulle du køre det manuelt før hver forespørgsel. Yikes. Ville det ikke være fantastisk, hvis denne logik blev udført i baggrunden, hver gang vi forberedte og kørte en erklæring?
Velkommen til ES6 Proxies 👋
En af de nyere JavaScript-funktioner er Proxy
objekt. Fuldmagter er i det væsentlige "fælder", der opsnapper objektoperationer som gettere, sættere og funktionskald. Brug af Proxyer vi kan ændre SQLite JS wrapper-biblioteket til at udføre vores egen logik, lidt som en middleware.
Skrivning af hjælpefunktionen
For at lette udviklingen vil vi bruge mapValues
&isPlainObject
hjælpefunktioner fra lodash , men du kan selvfølgelig kode din egen. Funktionen nedenfor vil kortlægge et objekt (et niveau dybt) og konvertere værdier af typen boolean
for at indtaste number
.
import { mapValues } from 'lodash';
const booleanEntriesToNumbers = (object) =>
mapValues(object, (value) =>
typeof value === 'boolean' ? Number(value) : value
);
Brug af proxyer til at opsnappe forespørgselsopkald
Nedenfor importerer vi better-sqlite3
bibliotek og opret en ny databaseinstans. Bagefter tilsidesætter vi standard prepare
metode med vores egen, som igen tilsidesætter metoderne run
, get
og all
, ved at oprette en ny proxy for hver enkelt. Du kan selvfølgelig oprette en proxy for enhver anden metode, du ønsker.
import Database from 'better-sqlite3';
// Create new database instance
const db = new Database(dbFilePath);
// We will use this function to override the default "prepare" method
const proxiedPrepare = new Proxy(db.prepare, {
apply: (prepare, prepareThisArg, [stringStatement]) => {
const statement = prepare.call(prepareThisArg, stringStatement);
// Override the default "run" method
statement.run = new Proxy(statement.run, {
apply: (run, runThisArg, args) => {
const mappedArgs = args.map((arg) =>
isPlainObject(arg) ? booleanEntriesToNumbers(arg) : arg
);
return run.call(runThisArg, ...mappedArgs);
},
});
// Override the default "get" method
statement.get = new Proxy(statement.get, {
apply: (get, getThisArg, args) => {
const mappedArgs = args.map((arg) =>
isPlainObject(arg) ? booleanEntriesToNumbers(arg) : arg
);
return get.call(getThisArg, ...mappedArgs);
},
});
// Override the default "all" method
statement.all = new Proxy(statement.all, {
apply: (all, allThisArg, args) => {
const mappedArgs = args.map((arg) =>
isPlainObject(arg) ? booleanEntriesToNumbers(arg) : arg
);
return all.call(allThisArg, ...mappedArgs);
},
});
return statement;
},
});
// Override the default "prepare" method
db.prepare = proxiedPrepare;
I det væsentlige, når et opkald til prepare
metode udløses, fortæller vi JavaScript:Vent! Vi ønsker at ændre dette funktionskald. I stedet for at udføre den logik, som den oprindelige udvikler havde til hensigt, ønsker vi i stedet at udføre vores egen logik først (som er kortlægningen af objektets nyttelast). Efter at have udført vores egen logik, returnerer vi resultatet af at kalde den oprindelige metode ved at bruge call
for at binde this
argument. Hvis du vil læse mere om, hvordan fuldmagter fungerer, så læs med her. Til vores implementering brugte vi apply
metode her.
Tak fordi du læste dette indlæg, jeg håber det hjalp nogen, der arbejder med SQLite i JavaScript 👊