Das Problem mit Booleans in SQLite
Wenn Sie jemals mit SQLite gearbeitet haben, sollten Sie die unterstützten Datentypen und Boolean
kennen ist keiner von ihnen. Genauer gesagt wie hier angegeben:
2.1. Boolescher Datentyp
SQLite hat keine separate boolesche Speicherklasse. Stattdessen werden boolesche Werte als ganze Zahlen 0 (falsch) und 1 (wahr) gespeichert.
SQLite erkennt die Schlüsselwörter „TRUE“ und „FALSE“ ab Version 3.23.0 (2018-04-02), aber diese Schlüsselwörter sind wirklich nur alternative Schreibweisen für die Integer-Literale 1 bzw. 0.
Die meisten JavaScript-Bibliotheken für SQLite3 unterstützen TRUE
nicht und FALSE
Schlüsselwörter und erfordern, dass Sie die Anweisungen in Ihrem Code mithilfe von Ganzzahlen vorbereiten. In better-sqlite3 müssten Sie beispielsweise Folgendes tun:
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;
Mit number
statt boolean
in Ihrer gesamten App würde zu einer schrecklichen Entwicklererfahrung führen (und wahrscheinlich mehr Speicher verbrauchen).
Sie könnten eine Hilfsfunktion verwenden, um den booleschen Wert Ihrer Payload-Objekte umzuwandeln Eigenschaften zu Zahlen (Ich hatte das in der Vergangenheit tatsächlich einmal gemacht), aber dann müssten Sie es vor jeder Abfrage manuell ausführen. Huch. Wäre es nicht großartig, wenn diese Logik jedes Mal im Hintergrund ausgeführt würde, wenn wir eine Erklärung vorbereiten und ausführen?
Willkommen ES6-Proxys 👋
Eines der neueren JavaScript-Features ist der Proxy
Objekt. Proxys sind im Wesentlichen "Fallen", die Objektoperationen wie Getter, Setter und Funktionsaufrufe abfangen. Verwendung von Proxys Wir können die SQLite JS-Wrapper-Bibliothek modifizieren, um unsere eigene Logik auszuführen, ähnlich wie eine Middleware.
Schreiben der Hilfsfunktion
Zur Vereinfachung der Entwicklung werden wir mapValues
verwenden &isPlainObject
Hilfsfunktionen von lodash , aber Sie können natürlich auch Ihre eigenen codieren. Die folgende Funktion wird durch ein Objekt abgebildet (eine Ebene tief) und Werte vom Typ boolean
konvertieren um number
einzugeben .
import { mapValues } from 'lodash';
const booleanEntriesToNumbers = (object) =>
mapValues(object, (value) =>
typeof value === 'boolean' ? Number(value) : value
);
Verwenden von Proxys zum Abfangen von Abfrageaufrufen
Unten importieren wir better-sqlite3
Bibliothek und erstellen Sie eine neue Datenbankinstanz. Danach überschreiben wir die Voreinstellung prepare
Methode mit unserer eigenen, die wiederum die Methoden run
überschreibt , get
und all
, indem Sie für jeden einen neuen Proxy erstellen. Sie können natürlich einen Proxy für jede andere Methode erstellen, die Sie möchten.
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;
Im Wesentlichen einmal einen Aufruf zum prepare
Methode ausgelöst wird, teilen wir JavaScript mit:Warte! Wir wollen diesen Funktionsaufruf modifizieren. Anstatt die vom ursprünglichen Entwickler beabsichtigte Logik auszuführen, möchten wir zuerst unsere eigene Logik ausführen (die die Zuordnung der Objektnutzlast darstellt). Nachdem wir unsere eigene Logik ausgeführt haben, geben wir das Ergebnis des Aufrufs der ursprünglichen Methode zurück, indem wir call
verwenden um den this
zu binden Streit. Wenn Sie mehr darüber erfahren möchten, wie Proxys funktionieren, lesen Sie hier. Für unsere Implementierung haben wir den apply
verwendet Methode hier.
Vielen Dank für das Lesen dieses Beitrags. Ich hoffe, er hat jemandem geholfen, der mit SQLite in JavaScript arbeitet 👊