Le module de code JavaScript Sqlite.jsm
offre une interface de stockage/SQLite. Sqlite.jsm
fonctionne avec des interfaces XPCOM de stockage de bas niveau:
- la gestion de la déclaration est automatique. Sqlite.jsm va créer, gérer et détruire les instances d'instructions pour vous. Vous ne devez pas vous soucier de la mise en cache des instances des états créés, les détruire lorsque vous avez terminé, etc. Cela se traduit par moins de lignes de code pour communiquer avec SQLite.
- Toutes les opérations sont asynchrones. Utilisation des API de stockage synchrones est déconseillée, car ils bloquent le thread principal. Toutes les fonctionnalités de
Sqlite.jsm
sont asynchrone. - La gestion de la mémoire est plus facile.
Sqlite.jsm
gère les déclarations pour vous, il peut effectuer des actions intelligentes comme purger toutes les déclarations mises en cache qui ne sont pas utilisés, ce qui libère la mémoire dans le processus. Il y a même une APIshrinkMemory
qui permettra de minimiser automatiquement l'utilisation de la mémoire de la connexion. - Des opérations sont simples. Sqlite.jsm utilise une API de transaction construit avec task.jsm qui permet aux transactions d'être écrites en tant que fonctions de procédure JavaScript (par opposition à une série d'opérations motrices de rappel). Si la fonction est lancée, la transaction est automatiquement annulée. Cela rend le code facile à lire et à écrire.
Sqlite.jsm
est un module JavaScript pur. Les complexités de XPCOM sont la plupart du temps cachés. Les programmeurs JavaScript doivent se sentir à l'aise en l'utilisant.
Avant de pouvoir utiliser ce module, vous devez l'importer dans votre champ d'application:
let { Cu } = require('chrome')
Cu.import("resource://gre/modules/Sqlite.jsm")
Obtention d'une connexion
Sqlite.jsm
exporte le symbole Sqlite
. Ce symbole est un objet avec une seule fonction: openConnection
. Cette fonction prend un objet dictionnaire définissant les options de connexion:
- path
- (Obligatoire) Le fichier de base de données à ouvrir. Cela peut être un chemin absolu ou relatif. Si un chemin relatif est donné, il est interprété comme relatif au répertoire du profil actuel. Si le chemin n'existe pas, une nouvelle base de données SQLite sera créé. La nom se termine généralement par
.sqlite
. - sharedMemoryCache
- (En option) booléenne indiquant si plusieurs connexions à la base de données partagent la même mémoire cache. Toutefois, le partage nécessite également des connexions pour obtenir un verrou, rendant éventuellement l'accès de base de données plus lente. Par défaut,
true
. - shrinkMemoryOnConnectionIdleMS
- (En option) Si défini, le de connexion va tenter de minimiser son utilisation de la mémoire après un grand nombre millisecondes de connexion inactive. La connexion est inactive lorsque aucun états n'est exécuter. Noter que ceci n'est pas une minuterie qui pourrait se déclencher pendant que l'Application est active.
openConnection(options)
retourne une promise d'une instance de connexion ouverte ou est rejetée si une erreur survient lors de l'ouverture de la base de données.
Voici un exemple:
let { Cu } = require('chrome') Cu.import("resource://gre/modules/Sqlite.jsm"); Sqlite.openConnection({ path: "myDatabase.sqlite", sharedMemoryCache: false }).then( function onConnection(connection) { // connection is the opened SQLite connection (see below for API). }, function onError(error) { // The connection could not be opened. error is an Error describing what went wrong. } );
Utilisation des connexions ouvertes
Les connexions ouvertes sont ce que vous interfacer avec Sqlite.jsm
. Les sections suivantes détaillent l'API d'une instance de connexion ouverte.
Gestion des connexions
Ces API sont utilisées pour gérer et vérifier l'état de la connexion.
close()
Ferme la connexion de base de données. Doit être appelé pour chaque connexion ouverte.
Cette fonction retourne une promise qui sera résolu lorsque la base de données sera fermée.
Si une transaction est en cours d'execution au moment où cette fonction est appelée, la transaction sera annulée.
Si des déclarations sont en cours au moment où cette fonction est appelée, elles seront annulées.
Les utilisateurs ne doivent pas tenter d'utiliser la connexion après avoir appelé cette méthode que la connexion sera inutilisable.
clone (readOnly)
Cette fonction retourne un clone de la connexion-promise actuelle.
Ces fonctions reçoivent l' argument:
- readOnly
- (En option) Si
true
le clone sera en lecture seule,false
par défaut. Si la connexion d'origine est déjà en lecture seule, le clone le sera, indépendamment de cette option. Si la connexion d'origine utilise le cache partagé, ce paramètre sera ignoré et le clone sera aussi privilégié que la connexion d'origine.
transactionInProgress
Cette propriété booléenne indique si une opération est en cours. Cela est rarement nécessaire par les appelants externes.
shrinkMemory()
Cette fonction peut être appelée pour diminuer l'utilisation de la mémoire de la connexion. Ceci est un wrapper utilisé dans le PRAGMA shrink_memory
, qui impose à SQLite la réduiction d'utilisation de la mémoire (par les caches de compensation, etc.).
Elle peut rendre votre base de données plus lent. Par conséquent, il faut être prudent avant d'appeler cette fonction.
Cela renvoie une promise qui est résolu lorsque l'opération est terminée.
discardCachedStatements()
Cette fonction est utilisée pour éliminer les instances d'instructions mises en cache, ce qui libère la mémoire dans le processus. Les déclarations de cache actifs ne seront pas effacées. Peut être appeler à tout moment.
Cela renvoie le nombre de déclarations mises en cache qui ont été rejetés.
Table et gestion du schéma
Ces API traitent de la gestion des tables et le schéma de base de données.
getSchemaVersion()
La version définie par l'utilisateur associé au schéma de la base de données actuelle. Retourne 0 si aucune version de schéma a été défini.
setSchemaVersion(value)
Definie la valeur value
de la nouvelle version associée au schéma de la base de données actuelle. Ceci est un wrapper autour de PRAGMA user_version
.
tableExists(name)
Cette fonction détermine si une table existe dans la base de données actuelle. Elle renvoie une promise qui est résolu avec une valeur booléenne indiquant si la table existe.
indexExists(name)
Cette fonction détermine si un index nommé existe dans la base de données actuelle. Elle renvoie une promesse qui est résolu avec une valeur booléenne indiquant si l'index existe.
Déclaration d'exécution
Ces API facilitent l'exécution des instructions de connexion.
executeCached(sql, params, onRow)
execute(sql, params, onRow)
Ces fonctions similaires sont utilisés pour exécuter une instruction SQL unique sur la connexion. Comme vous l'avez deviné par le nom, il y a 2 options: mises en cache et non mis en cache. En dehors de cela, ils se comportent de manière identique.
Ces fonctions reçoivent les arguments suivants:
- sql
- (Obligatoire) chaîne SQL à exécuter. Le point-virgule final n'est pas nécessaire.
- params
- (En option) Paramètres liés à cette déclaration. Cela peut être un tableau ou un objet. Voir les notes ci-dessous.
- onRow
- (En option) Fonction qui est appelée lorsqu'une ligne a été reçue.
La valeur de retour est une promise qui est résolu lorsque l'instruction a terminé l'exécution.
Quand une instruction est exécutée via executeCached()
, l'objet instruction préparée est mis en cache à l'intérieur de la connexion ouverte. La prochaine fois que cette même instruction SQL est exécutée (sql
argument est identique à celui passé avant), l'ancien objet de la déclaration est réutilisée. Cela permet d'économiser du temps associé à l'analyse de l'instruction SQL et la création d'un nouvel objet de déclaration. Inconvénient: l'objet de la déclaration en cache persiste dans la connexion ouverte, en prenant de la mémoire.
Quand une instruction est exécutée via execute()
, l'objet de la déclaration sous-jacente est jeté au terme de l'exécution.
executeCached()
est recommandé pour les déclarations qui seront exécutées plusieurs fois. execute()
est recommandé pour les déclarations qui seront exécutées rarement ou une fois.
Noter que les utilisateurs ne doivent pas préparer les états manuellement avant l'exécution. Il suffit d'appeler executeCached()
et la déclaration sera préparée pour vous automatiquement.
Les paramètres peuvent être liés à la déclaration faite par la définition de l'argument params
. Cet argument peut être un tableau de paramètres ordonnées ou un objet de type dicionnaire. Si la déclaration ne contient pas de paramètres liés, cet argument peut être omis ou spécifié comme nulle.
onRow
n'est pas défini, les résultats complets de l'opération sont tamponnés avant que l'appelant soit informé de la déclaration d'achèvement. Pour INSERT
, UPDATE
et DELETE
, ce n'est pas pertinentes. Cependant, il peut y avoir des conséquences importante pour SELECT
. Si votre déclaration SELECT
retourne beaucoup de données, cette mise en mémoire tampon peut entraîner une utilisation excessive de la mémoire. Par conséquent, il est recommandé d'utiliser onRow
avec SELECT
.StopIteration
est emis lors de l'exécution d'un gestionnaire onRow
, l'exécution de l'instruction est immédiatement annulée. Les lignes suivantes ne seront pas traitées. La promise est résolu immédiatement.StopIteration
est levée par le gestionnaire onRow
, l'exception est enregistré et le traitement des lignes suivantes se poursuit normalement. La promise est toujours résolue (pas rejetée).La promise sera rejeté avec une Error
, si la déclaration n'a pas terminé l'exécution complète. L'Error
peut avoir une propriété errors
. Si elle est définie, ce sera un tableau d'objets décrivant les erreurs. Chaque objet possède les propriétés result
et message
. result
est un code d'erreur numérique et message
est une chaîne décrivant le problème.
Si onRow
est spécifié, la promise sera résolu avec un booléen indiquant si le gestionnaire onRow a été appelé. Sinon, la valeur réglée sera un tableau de mozIStorageRow
.
executeTransaction(func, type)
Cette fonction est utilisée pour exécuter une transaction de base de données. Une transaction est une série de déclarations connexes traités comme une seule unité fonctionnelle. Si la transaction réussit, toutes les déclarations contenues dedans sont engagés comme une seule unité. Si la transaction échoue, la base de données revient à son état avant le début de la transaction.
Cette fonction reçoit les arguments suivants:
- func
- La fonction définissant le corps de la transaction.
- type
- Le type de transaction à effectuer. Ce doit être l'une des constantes de TRANSACTION_* sur l'instance de connexion ouverte. Les valeurs valides sont
TRANSACTION_DEFERRED
,TRANSACTION_IMMEDIATE
,TRANSACTION_EXCLUSIVE
. Consultez la documentation SQLite pour leur signification. La valeur par défaut estTRANSACTION_DEFERRED
.
La fonction passée est une fonction de générateur compatible Task.jsm. Lorsqu'elle est appelée, la fonction reçoit comme argument l'instance de connexion actuelle. Cette fonction de générateur devrait produire des promises, probablement ceux qui sont renvoyés en appelant executeCached()
et execute()
.
Si nous arrivons à la fin de la fonction de générateur sans erreur, la transaction est validée. Si une erreur se produit, la transaction est abandonnée.
La valeur retournée par cette fonction est une promise qui est résolu lorsque la transaction a été exécutée ou rejetée si la transaction a été annulée.
Exemples
Open, Execute, Close
Dans cet exemple, nous ouvrons une connexion, exécutons une instruction simple, puis fermons la connexion.
Sqlite.openConnection({path: "MyDB.sqlite"}).then( function onOpen(conn) { conn.execute("SELECT 1").then( function onStatementComplete(result) { conn.close().then( function onClose() { alert("We are done!"); } ) } ) } )
Ce n'est pas un excellent exemple parce qu'il ne comprend pas la gestion des erreurs et est un peu difficile à lire.
Voici la même chose mais avec Task.jsm:
Task.spawn(function* demoDatabase() { let conn = yield Sqlite.openConnection({path: "MyDB.sqlite"}); try { let result = yield conn.execute("SELECT 1"); } finally { yield conn.close(); } });
Bound Parameters
Voici quelques exemples montrant des paramètres liés. Supposons que nous ouvrons une connexion avec la variable conn
.
let dataToInsert = [ ["foo", "bar"], ["biz", "baz"], ["yo", "ho"], ]; Task.spawn(function* doInsert() { for (let data of dataToInsert) { yield conn.executeCached("INSERT INTO myTable VALUES (?, ?)", data); } });
Et la même chose avec des paramètres nommés.
let dataToInsert = [ {paramA: "foo", paramB: "bar"}, {paramA: "biz", paramB: "baz"}, {paramA: "yo", paramB: "ho"}, ]; Task.spawn(function* doInsert() { for (let data of dataToInsert) { yield conn.executeCached("INSERT INTO myTable VALUES (:paramA, :paramB)", data); } });
Transactions
Ces exemples montrent comment fonctionnent les transactions.
conn.executeTransaction(function* simpleTransaction() { yield conn.execute("INSERT INTO myTable VALUES (?, ?)", ["foo", "bar"]); yield conn.execute("INSERT INTO myTable VALUES (?, ?)", ["biz", "baz"]); });
L'exemple ci-dessus se traduira par 2 instructions INSERT dans une transaction différée (en supposant que les inserts procèdent sans erreur, bien sûr).
Voici un exemple où nous voulons forcer une annulation de la transaction.
conn.executeTransaction(function* complexTransaction() { yield conn.execute("INSERT INTO myTable VALUES (?, ?)", ["foo", "bar"]); let data = yield conn.execute("SELECT * FROM myTable"); if (data.length < 5) { throw new Error("We don't have the expected 5 rows to perform the next operation!"); } // ... });
Sélection et retour des données
Ces exemples montrent comment accéder aux données qui sont retournées.
Cet exemple montre plusieurs lignes d'une table retournées en utilisant le paramètre onRow
.
let accounts = []; let accountId, userName; let statement = "SELECT account_id, username FROM accounts ORDER BY username ASC"; conn.executeCached(statement, null, function(row) { accountId = row.getResultByName("account_id"); userName = row.getResultByName("username"); accounts.push({ accountId: accountId, userName: userName }); }).then(function onStatementComplete(result) { // All accounts returned successfully, so do something with them. console.log(result); // It worked! if (callback) { callback(null, accounts); } }, function onError(err) { // An error occurred. console.log(err); // Error, Oh noes! if (callback) { callback(err); } });
Remarque: les> paramètres then
peuvent être des fonctions anonymes (i.e. function()
) , les seulements nomées sont onStatementComplete
et onError
pour la lisibilité.
Cet exemple démontre la récupération d'une ligne sans utiliser le paramètre onRow
, en utilisant le résultat de conn.execute
. Cet exemple montre également la récupération de la clé primaire de la dernière ligne insérée.
Task.spawn(function* () { try { conn = yield Sqlite.openConnection({ path: dbFile.path }); let statement = "INSERT INTO accounts (username, details) VALUES (:username, :details)" let params = { username:"LordBusiness", details: "All I'm asking for is total perfection." }; yield conn.execute(statement,params); // Get accountId of the INSERT. statement = "SELECT last_insert_rowid() AS lastInsertRowID"; result = yield conn.execute(statement); // Only one row is returned. let row = result[0]; let accountId = row.getResultByName("lastInsertRowID"); if (callback) { callback(null, accountId); } } catch (err) { if (callback) { callback(err); } } finally { conn.close(); } });
Remarque: La valeur retournée par last_insert_rowid() l'est par connexion, de sorte que vous devrez peut-être ouvrir des connexions séparées lorsque vous faites plusieurs INSERT
à différents endroits, pour être sûr que l'identifiant de ligne qui est retourné corresponde.