node 下 sqlite3 的使用

原文连接 github npm APIjava

Asynchronous, non-blocking SQLite3 bindings for Node.jsnode

sqlite3 是一个专为 nodejs 设计的,node 上面的轻量级嵌入式数据库,做为嵌入式数据库的表明,sqlite 无疑是个理想的选择方案。linux

sqlite3 几乎支持全部版本的 nodejs,同时也能够和 nwjs 集成。git

安装

基于 npm 安装github

npm install sqlite3
复制代码

这样除了安装完 sqlite3 的 npm 包,最主要的是也装完了 sqlite 数据库,由于 sqlite 是嵌入式数据库,嵌入到客户端中。sqlite3 使用 node-pre-gyp 为各个平台下载指定的预编译的二进制文件。若是没法下载到预编译的二进制文件,sqlite3 将使用 node-gyp 和源代码来构建扩展。sql

这个过程出现两个的库——node-pre-gyp 和 node-gyp。他们到底是什么呢?数据库

node-gyp 是一个跨平台的命令行工具,用于编译 C++编写的 nodejs 扩展,首先 gyp 是为 Chromium 项目建立的项目生成工具,能够从平台无关的配置生成平台相关的 Visual Studio、Xcode、Makefile 的项目文件,node-gyp 就是将其集成到 nodejs 中。由于 linux 的二进制分发快平台作的并很差,全部 npm 为了方便干脆就直接源码分发,用户装的时候再现场编译。不过对有些项目二进制分发就比源码分发简单多了,因此还有个 node-pre-gyp 来直接二进制扩展的分发。npm

二者区别在于 node-gyp 是发布扩展的源码,而后安装时候编译;node-pre-gyp 是直接发布编译后的二级制形式的扩展。json

和 sqlite3 同样的须要基于 node-gyp 安装的 npm 模块也有不少,好比 node-sass 等,都是发布源代码,而后编译安装。api

基础 api

sqlite3 的 api 都是基于函数回调的,由于 nodejs 中没有像 java 的 jdbc 那种官方的数据库客户端接口,所以每一个数据库的 api 都不同,这里简单介绍几个 sqlite3 重要的 api。

新建并打开数据库

new sqlite3.Database(filename, [mode], [callback]);

该方法返回一个自动打开的数据库对象,参数:

filename:有效值是一个文件名,如:“mydatebase.db”,数据库打开以后会建立一个“mydatebase.db”的文件用于保存数据。若是文件名是“:memory:”,表示是一个内存数据库(相似 h2 那种),数据不会持久化保存,当关闭数据库时,内容将丢失。

mode(可选):数据库的模式,共 3 种值:sqlite3.OPEN_READONLY(只读),sqlite3.OPEN_READWRITE(可读写)和 sqlite3.OPEN_CREATE(能够建立)。 默认值为 OPEN_READWRITE |OPEN_CREATE。

callback(可选):则当数据库成功打开或发生错误时,将调用此函数。 第一个参数是一个错误对象,当它为空时,表示打开成功。

打开一个数据库

//数据库的名字是"mydatebase.db"
var database;
database = new sqlite3.Database("mydatebase.db", function(e) {
  if (err) throw err;
});
//也可使用内存型,数据不会永久保存
database = new sqlite3.Database(":memory:", function(e) {
  if (err) throw err;
});
复制代码

执行后会在项目的根目录生成一个“mydatebase.db”文件,这就是 sqlite 保存数据的文件了。

关闭数据库

Database#close([callback])

该方法能够关闭一个数据库链接对象,参数:

callback(可选):关闭成功的回调。 第一个参数是一个错误对象,当它为“null”时,表示关闭成功。

执行 DDL 和 DML 语句

Database#run(sql, [param, ...], [callback])

该方法能够执行 DDL 和 DML 语句,如建表、删除表、删除行数据、插入行数据等,参数:

sql:要运行的 SQL 字符串。sql 的类型是 DDL 和 DML,DQL 不能使用这个命令。执行后返回值不包含任何结果,必须经过 callback 回调函数获取执行结果。

param,...(可选):当 SQL 语句包含占位符(?)时,这里能够传对应的参数。 这里有三种传值方法,如:

// 直接经过参数传值.
db.run("UPDATE tbl SET name = ? WHERE id = ?", "bar", 2);

// 将值封装为一个数组传值.
db.run("UPDATE tbl SET name = ? WHERE id = ?", ["bar", 2]);

// 使用一个 json 传值.参数的前缀能够是“:name”,“@name”和“$name”。推荐用“$name”形式
db.run("UPDATE tbl SET name = $name WHERE id = $id", {
  $id: 2,
  $name: "bar"
});
复制代码

关于占位符的命名,sqlite3 还支持更复杂的形式,这里再也不扩展,有兴趣了解的话请查看官方文档。

callback(可选):若是执行成功,则第一个参数为 null,不然就是出错。

若是执行成功,上下文 this 包含两个属性:lastID 和 changes。lastID 表示在执行 INSERT 命令语句时,最后一条数据的 id;changes 表示 UPADTE 命令和 DELETE 命令时候,影响的数据行数。

db.run("UPDATE foo SET id = 1 WHERE id <= 500", function(err) {
  if (err) throw err;
  //使用 this.changes 获取改变的行数
  assert.equal(500, this.changes);
  done();
});
复制代码

执行多条语句

Database#exec(sql, [callback])

Database#execDatabase#run 函数同样,都是 DDL 和 DML 语句,可是 Database#exec 能够执行多条语句,而且不支持占位符参数。

database.run("CREATE TABLE foo (id INT)", function(e) {
  if (e !== null) {
    throw e;
  }
  //循环生成 sql 语句,批次插入多条数据
  var sql = "";
  for (var i = 0; i < 500; i++) {
    sql += "INSERT INTO foo VALUES(" + i + ");";
  }
  database.exec(sql, done);
});
复制代码

查询一条数据

Database#get(sql, [param, ...], [callback])

sql:要运行的 SQL 字符串。sql 的类型是 DQL。这里仅返回第一条查询到的数据。

param,...(可选):同 Database#run 的 param 参数

callback(可选):一样是返回 null 表明执行成功。回调的签名是 function(err,row)。若是查询结果集为空,则第二个参数为 undefined;不然第二个参数值是查询到的第一个对象,他是个 json 对象,属性名称对应于结果集的列名称,所以查询的每一列都应该给出一个列表名。

查询全部数据

Database#all(sql, [param, ...], [callback])

sql:要运行的 SQL 字符串。sql 的类型是 DQL。和 Database#get 不一样,Database#all 会返回全部查询到的语句。

param,...(可选):同 Database#run 的 param 参数

callback(可选):一样是返回 null 表明执行成功。回调的签名是 function(err, rows) 。rows 是一个数组,若是查询结果集为空数组。

! 注意,Database#all 首先检索全部结果行并将其存储在内存中。 对于数据量可能很大的查询命令时候,请使用 Database#each 函数或 Database#prepare 代替这个方法。

遍历数据

Database#each(sql, [param, ...], [callback], [complete])

Database#run 函数相同,都是查询多条数据,可是具备如下区别:

回调的签名是 function(err,row)。若是结果集成功但为空,则不会调用回调。对于每一个检索到的行,该方法都会调用一次回调。执行顺序与结果集中的行顺序彻底对应。

调用全部行回调后,若是存在 complete 回调函数,将调用这个回调。第一个参数是一个错误对象,第二个参数是检索行数。

预编译 SQL 相关 api(Using Prepared Statements)

在 java 的 jdbc 中,有个 PreparedStatement 相关的 api,能够预编译 sql 语句,执行的时候再连接具体参数。这样的好处是能够减小 sql 语句被编译的次数。在 sqlite3 中,也存在实现这样功能的 api。

Database#prepare(sql, [param, ...], [callback])

Database#prepare 执行后,会返回一个命令对象,这个命令对象能够反复执行。下面看看这个命令对象(statement )的 api:

Statement#run([param, ...], [callback])

Statement#get([param, ...], [callback])

Statement#all([param, ...], [callback])

Statement#each([param, ...], [callback], [complete])

以上 api 方法与 Database 的同名方法调用方式相同。不一样点是这里的 Statement 对象是能够复用的,避免了重复编译 sql 语句,所以项目中更推荐使用上述方法。

! 注意,这些方法的 param 参数都会对 Statement 对象绑定参数,在下一次执行的时候,若是没有从新绑定参数,是会使用上一次参数的。

绑定参数

Statement#bind([param, ...], [callback])

Database#prepare 执行的时候,是能够绑定参数的。不过使用此方法能够全重置语句对象和行游标,并删除全部先前绑定的参数,实现从新绑定的功能。

重置语句的行游标

Statement#reset([callback])

重置语句的行游标,并保留参数绑定。使用此功能可使用相同的绑定从新执行相同的查询。

数据库事务事务是关系型数据库中的一个重要部分,sqlite 天然也是支持事务的,可是 sqlite3 并无提供特殊 API 去实现的事务相关的操做,只能靠 SQL 语句去控制事务。这里举一个事务相关的例子。

var db = new sqlite3.Database(db_path);
db.run("CREATE TABLE foo (id INT, txt TEXT)");
db.run("BEGIN TRANSACTION");
var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)");
for (var i = 0; i < count; i++) {
  stmt.run(i, randomString());
}
db.run("COMMIT TRANSACTION");
复制代码

语句执行顺序(Control Flow)

sqlite3 的 API 都是异步的,这就会出现可能有若干个命令同时进行的状况,所以 sqlite3 提供了两个函数来帮助控制语句的执行流程。默认是并行模式。

序列化执行

Database#serialize([callback])

若是提供回调,它将当即被调用,即此方法的回调不是异步回调。在该回调中调度的全部数据库语句将被序列化运行,即一个接一个地执行。 函数返回后,数据库将再次设置为其原始模式。

// 这里执行的命令是并行的
db.serialize(function() {
  // 这里执行的命令是串行的
  db.serialize(function() {
    // 这里执行的命令是串行的
  });
  // 这里执行的命令是串行的
});
// 这里执行的命令是并行的并行执行模式
复制代码

Database#parallelize([callback])

若是提供回调,它将当即被调用,即此方法的回调不是异步回调。在该回调中调度的全部数据库语句将并行运行。函数返回后,数据库将再次设置为其原始模式。

db.serialize(function() {
  // 这里执行的命令是串行的
  db.parallelize(function() {
    // 这里执行的命令是并行的
  });
  // 这里执行的命令是串行的
});
复制代码

对 SQLCipher 的支持

SQLCipher 是一个在 SQLite 基础之上进行扩展的开源数据库,他和 SQLite 不一样就是提供了对数据的加密,可提供数据库文件的透明 256 位 AES 加密。

sqlite3 的官网特地说起他对 SQLCipher 的集成,若是要集成 sqlcipher 须要在编译时候经过构建选项告诉 sqlite3 要集成的是 SQLCipher:

npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/
node -e 'require("sqlite3")'
复制代码

不过笔者并没尝试对 SQLCipher 的集成,具体集成方法请自行查阅官网对这部分的详细介绍。

基于 promise 对 sqlite3API 的封装

sqlite3 的 API 是 node 早期的 API 风格,对异步的书写风格并不友好,很容易出现“金字塔回调”式的代码。

为了让对 API 的调用更加优雅,咱们每每会把回调封装成 Promise。

事实上这个工做并不须要咱们本身作,sqlite3 生态下已经有其余库能够实现这样的功能。

sqlite 就是一个这样的库。他基于 sqlite3,只手用 Promise 从新封装了一下 sqlite3 的 API,使其代码风格更加优雅,也更容易使用。

API

Main

  • new sqlite3.Database(filename, [mode], [callback])
  • sqlite3.verbose()

Database

  • Database#close([callback])
  • Database#configure(option, value)
  • Database#run(sql, [param, ...], [callback])
  • Database#get(sql, [param, ...], [callback])
  • Database#all(sql, [param, ...], [callback])
  • Database#each(sql, [param, ...], [callback], [complete])
  • Database#exec(sql, [callback])
  • Database#prepare(sql, [param, ...], [callback])

Statement

  • Statement#bind([param, ...], [callback])
  • Statement#reset([callback])
  • Statement#finalize([callback])
  • Statement#run([param, ...], [callback])
  • Statement#get([param, ...], [callback])
  • Statement#all([param, ...], [callback])
  • Statement#each([param, ...], [callback], [complete])

相关文章
相关标签/搜索