更新记录

0.1.0(2025-12-21) 下载此版本

初始版本,支持微信小程序使用SQLite数据库,支持数据库导入导出,支持查询插入等常用语句。


平台兼容性

uni-app(4.45)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × × × × × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
× × × × × × × × × ×

uni-app x(4.45)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × × × ×

其他

多语言 暗黑模式 宽屏模式

lleh-mpsqlite

SQLite 数据库插件,适用于 uni-app 小程序环境。

平台支持:目前支持微信小程序。

重要说明:不支持数据库操作直接读写数据库文件,所有修改都在内存中进行。如需持久化,需要手动调用 export() 方法导出数据库,然后保存到文件。

目录

快速开始

导入

import { sqliteOpen } from "/uni_modules/lleh-mpsqlite";

最简单的例子

// 打开空数据库
const db = await sqliteOpen(null);

// 创建表
db.run(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    age INTEGER
  )
`);

// 插入数据
db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Alice", 25]);
db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Bob", 30]);

// 查询数据
const result = db.exec("SELECT * FROM users");
console.log(result[0].values);
// 输出: [[1, "Alice", 25], [2, "Bob", 30]]

// 关闭数据库(重要!)
db.close();

打开数据库

sqliteOpen() 支持三种方式打开数据库:

1. 从文件路径打开

支持 .db.sqlite 文件和 .db.br.sqlite.br 压缩文件:

// 打开普通数据库文件
const db1 = await sqliteOpen("/static/database.db");
const db2 = await sqliteOpen("/static/database.sqlite");

// 打开压缩的数据库文件(.br 格式)
const db3 = await sqliteOpen("/static/database.db.br");
const db4 = await sqliteOpen("/static/database.sqlite.br");

2. 从 Buffer 打开

支持 ArrayBufferUint8Array

// 方式1:使用 uni.request 从网络获取(小程序环境)
const res = await uni.request({
  url: "https://example.com/database.db",
  responseType: "arraybuffer",
});
const db = await sqliteOpen(res.data);

// 方式2:读取本地文件(小程序环境)
const fs = uni.getFileSystemManager();
const fileData = await new Promise((resolve, reject) => {
  fs.readFile({
    filePath: "/static/database.db",
    success: (res) => {
      const data = new Uint8Array(res.data);
      resolve(data);
    },
    fail: reject,
  });
});
const db = await sqliteOpen(fileData);

// 方式3:直接使用 Uint8Array(示例:从已有数据创建)
const uint8Array = new Uint8Array([0x53, 0x51, 0x4c, 0x69, 0x74, 0x65]); // SQLite 文件头示例
const db = await sqliteOpen(uint8Array);

3. 创建空数据库

不传参数、传入 nullundefined 都可以创建空数据库:

// 创建空数据库(三种方式都可以)
const db1 = await sqliteOpen();
const db2 = await sqliteOpen(null);
const db3 = await sqliteOpen(undefined);

数据库对象方法

数据库对象(db)提供以下主要方法:

exec(sql, params?) - 执行 SQL 查询

执行 SQL 并返回所有结果,适合 SELECT 查询。支持参数绑定:

// 不带参数
const result = db.exec("SELECT * FROM users");

// 带参数(数组形式,位置参数)
const result = db.exec("SELECT * FROM users WHERE age > ?", [18]);

// 带参数(对象形式,命名参数)
const result = db.exec(
  "SELECT * FROM users WHERE name = :name AND age > :age",
  {
    ":name": "Alice",
    ":age": 18,
  },
);

// result 是数组,每个元素包含 columns 和 values

run(sql, params?) - 执行 SQL 不返回结果

执行 SQL 但不返回数据,适合 INSERT/UPDATE/DELETE:

// 不带参数
db.run("INSERT INTO users (name, age) VALUES ('Alice', 25)");

// 带参数(数组形式)
db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Bob", 30]);

// 带参数(对象形式,命名参数)
db.run("INSERT INTO users (name, age) VALUES (:name, :age)", {
  ":name": "Charlie",
  ":age": 35,
});

prepare(sql) - 准备 SQL 语句

准备 SQL 语句,返回 Statement 对象,用于参数化查询:

// 方式1:使用位置参数(?)
const stmt = db.prepare("SELECT * FROM users WHERE age > ?");
stmt.bind([18]);
while (stmt.step()) {
  const row = stmt.getAsObject();
  console.log(row);
}
stmt.free(); // 必须释放

// 方式2:使用命名参数(:name)
const stmt2 = db.prepare(
  "SELECT * FROM users WHERE name = :name AND age > :age",
);
stmt2.bind({ ":name": "Alice", ":age": 18 });
while (stmt2.step()) {
  const row = stmt2.getAsObject();
  console.log(row);
}
stmt2.free(); // 必须释放

export() - 导出数据库

将整个数据库导出为 Uint8Array重要:数据库的所有修改都在内存中,不会自动保存。如需持久化,必须调用此方法导出并手动保存到文件。

const exported = db.export();
// exported 是 Uint8Array,可以保存为文件

// 保存文件示例
const buffer = exported.buffer.slice(
  exported.byteOffset,
  exported.byteOffset + exported.byteLength,
);
const fs = uni.getFileSystemManager();
fs.writeFileSync("/path/to/export.db", buffer);

create_function(name, func) - 创建自定义函数

⚠️ 小程序环境不可用:由于小程序环境的 WebAssembly 实现与标准不一致,此功能在小程序环境中不可用。

在 SQL 中调用 JavaScript 函数:

db.create_function("my_function", (x, y) => {
  return x + y;
});

const result = db.exec("SELECT my_function(1, 2)");
// result[0].values[0][0] 是 3

updateHook(callback) - 注册更新钩子

⚠️ 小程序环境不可用:由于小程序环境的 WebAssembly 实现与标准不一致,此功能在小程序环境中不可用。

监听数据库的 INSERT/UPDATE/DELETE 操作:

db.updateHook((operation, database, table, rowId) => {
  console.log(`${operation} on ${database}.${table} row ${rowId}`);
});

// 取消注册
db.updateHook(null);

Statement 对象方法

通过 prepare() 返回的 Statement 对象提供以下方法:

bind(params) - 绑定参数

为准备好的语句绑定参数值:

// 位置参数:SQL 中使用 ?,bind 时使用数组
const stmt1 = db.prepare("SELECT * FROM users WHERE name = ? AND age > ?");
stmt1.bind(["Alice", 18]);

// 命名参数:SQL 中使用 :name,bind 时使用对象
const stmt2 = db.prepare(
  "SELECT * FROM users WHERE name = :name AND age > :age",
);
stmt2.bind({ ":name": "Alice", ":age": 18 });

step() - 执行一步

执行语句的一步,返回 true 表示还有数据,false 表示完成:

while (stmt.step()) {
  // 还有数据,继续处理
}

get() / getAsObject() - 获取当前行

获取当前行的数据:

// get() 返回数组
const row = stmt.get();
// row = [1, "Alice", 25]

// getAsObject() 返回对象
const rowObj = stmt.getAsObject();
// rowObj = { id: 1, name: "Alice", age: 25 }

run(params?) - 运行语句

执行语句(适合 INSERT/UPDATE/DELETE):

// 位置参数
const stmt1 = db.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
stmt1.run(["Alice", 25]);
stmt1.free();

// 命名参数
const stmt2 = db.prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
stmt2.run({ ":name": "Alice", ":age": 25 });
stmt2.free();

reset() - 重置语句

重置语句状态,可以重新绑定参数执行:

stmt.bind([18]);
while (stmt.step()) {
  // 处理数据
}
stmt.reset(); // 重置后可以重新绑定参数
stmt.bind([20]);

free() - 释放语句

释放语句占用的内存,使用完必须调用

const stmt = db.prepare("SELECT * FROM users");
// ... 使用 stmt ...
stmt.free(); // 必须释放,否则可能导致内存泄漏

完整的参数化查询示例

// 方式1:使用位置参数(?)
const stmt1 = db.prepare("SELECT * FROM users WHERE age > ? AND name LIKE ?");
stmt1.bind([18, "%Alice%"]);
while (stmt1.step()) {
  const row = stmt1.getAsObject();
  console.log(row.name, row.age);
}
stmt1.free(); // 必须释放

// 方式2:使用命名参数(:name)
const stmt2 = db.prepare(
  "SELECT * FROM users WHERE age > :age AND name LIKE :name",
);
stmt2.bind({ ":age": 18, ":name": "%Alice%" });
while (stmt2.step()) {
  const row = stmt2.getAsObject();
  console.log(row.name, row.age);
}
stmt2.free(); // 必须释放

查询结果处理

exec() 返回的结果结构

exec() 返回一个数组,每个元素代表一个查询结果:

const result = db.exec("SELECT * FROM users");

// result 结构:
// [
//   {
//     columns: ["id", "name", "age"],
//     values: [
//       [1, "Alice", 25],
//       [2, "Bob", 30]
//     ]
//   }
// ]

// 获取列名
const columns = result[0].columns;

// 获取数据行
const rows = result[0].values;

// 遍历数据
rows.forEach((row) => {
  console.log(row[0], row[1], row[2]); // id, name, age
});

Statement 方式获取结果

使用 step() + get()getAsObject() 逐行获取:

const stmt = db.prepare("SELECT * FROM users WHERE age > ?");
stmt.bind([18]);

// 方式1:使用 get() 获取数组
while (stmt.step()) {
  const row = stmt.get();
  console.log(row[0], row[1], row[2]);
}

// 方式2:使用 getAsObject() 获取对象
stmt.reset();
stmt.bind([18]);
while (stmt.step()) {
  const row = stmt.getAsObject();
  console.log(row.id, row.name, row.age);
}

stmt.free();

常用操作示例

创建表和插入数据

const db = await sqliteOpen(null);

// 创建表
db.run(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    age INTEGER
  )
`);

// 插入数据
db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Alice", 25]);
db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Bob", 30]);

db.close();

查询数据

使用 exec() 查询

// 不带参数
const result1 = db.exec("SELECT * FROM users");

// 带参数(位置参数)
const result2 = db.exec("SELECT * FROM users WHERE age > ?", [18]);

// 带参数(命名参数)
const result3 = db.exec(
  "SELECT * FROM users WHERE name = :name AND age > :age",
  {
    ":name": "Alice",
    ":age": 18,
  },
);

result2[0].values.forEach((row) => {
  console.log(row);
});

使用 prepare() 查询

// 方式1:使用位置参数(?)
const stmt1 = db.prepare("SELECT * FROM users WHERE age > ?");
stmt1.bind([18]);
while (stmt1.step()) {
  const row = stmt1.getAsObject();
  console.log(row);
}
stmt1.free();

// 方式2:使用命名参数(:name)
const stmt2 = db.prepare("SELECT * FROM users WHERE age > :age");
stmt2.bind({ ":age": 18 });
while (stmt2.step()) {
  const row = stmt2.getAsObject();
  console.log(row);
}
stmt2.free();

更新和删除数据

// 更新
db.run("UPDATE users SET age = ? WHERE name = ?", [26, "Alice"]);

// 删除
db.run("DELETE FROM users WHERE age < ?", [18]);

导出数据库

重要:数据库的所有修改都在内存中进行,不会自动保存到文件。如果需要持久化修改,必须手动调用 export() 方法导出数据库,然后保存到文件。

// 导出为 Uint8Array
const exported = db.export();

// 转换为 ArrayBuffer(如果需要)
const buffer = exported.buffer.slice(
  exported.byteOffset,
  exported.byteOffset + exported.byteLength,
);

// 保存文件(示例)
const fs = uni.getFileSystemManager();
const filePath = `${wx.env.USER_DATA_PATH}/database.db`;
fs.writeFileSync(filePath, buffer);

使用自定义函数

⚠️ 小程序环境不可用:由于小程序环境的 WebAssembly 实现与标准不一致,此功能在小程序环境中不可用。

// 创建自定义函数
db.create_function("greet", (name) => {
  return `Hello, ${name}!`;
});

// 在 SQL 中使用
const result = db.exec("SELECT greet('Alice')");
console.log(result[0].values[0][0]); // "Hello, Alice!"

监听数据库更新

⚠️ 小程序环境不可用:由于小程序环境的 WebAssembly 实现与标准不一致,此功能在小程序环境中不可用。

db.updateHook((operation, database, table, rowId) => {
  console.log(`操作: ${operation}`);
  console.log(`数据库: ${database}`);
  console.log(`表: ${table}`);
  console.log(`行ID: ${rowId}`);
});

// 执行操作时会触发钩子
db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Alice", 25]);
// 输出: 操作: insert, 数据库: main, 表: users, 行ID: 1

// 取消监听
db.updateHook(null);

速查表

打开方式对比

方式 参数类型 示例
文件路径 string sqliteOpen("/static/db.db")
压缩文件 string (.br) sqliteOpen("/static/db.db.br")
Buffer ArrayBuffer / Uint8Array sqliteOpen(arrayBuffer)
空数据库 null / undefined sqliteOpen(null)

Database 方法签名速查

方法 参数 返回值 用途
exec(sql, params?) SQL 字符串,可选参数 结果数组 执行查询,返回所有结果
run(sql, params?) SQL 字符串,可选参数 执行 SQL,不返回数据
prepare(sql) SQL 字符串 Statement 对象 准备参数化查询
export() Uint8Array 导出数据库
create_function(name, func) 函数名,JavaScript 函数 创建自定义函数(小程序环境不可用)
updateHook(callback) 回调函数或 null 注册/取消更新钩子(小程序环境不可用)
close() 关闭数据库

Statement 方法签名速查

方法 参数 返回值 用途
bind(params) 对象或数组 绑定参数
step() boolean 执行一步
get() 数组 获取当前行(数组)
getAsObject() 对象 获取当前行(对象)
run(params?) 可选参数 运行语句
reset() 重置语句
free() 释放语句

exec vs run vs prepare 使用场景对比

方法 适用场景 示例
exec() SELECT 查询,需要所有结果,支持参数绑定 db.exec("SELECT * FROM users WHERE age > ?", [18])
run() INSERT/UPDATE/DELETE,不需要结果,支持参数绑定 db.run("INSERT INTO users (name, age) VALUES (?, ?)", ["Alice", 25])
prepare() 需要参数化查询,或逐行处理大量数据,可重复使用 db.prepare("SELECT * FROM users WHERE age > ?")

重要提醒

必须调用 close() 关闭数据库

使用完数据库后,必须调用 close() 方法关闭数据库,否则会导致内存泄漏。

const db = await sqliteOpen("/static/database.db");

try {
  // 使用数据库
  const result = db.exec("SELECT * FROM users");
  // ... 其他操作 ...
} finally {
  // 确保关闭数据库
  db.close();
}

必须调用 free() 释放 Statement

使用完 Statement 后,必须调用 free() 方法释放,否则可能导致内存泄漏和数据库锁定。

const stmt = db.prepare("SELECT * FROM users");

try {
  while (stmt.step()) {
    const row = stmt.getAsObject();
    // 处理数据
  }
} finally {
  // 确保释放 Statement
  stmt.free();
}

最佳实践

  1. 使用 try-finally 确保资源释放
const db = await sqliteOpen("/static/database.db");
try {
  // 使用数据库
} finally {
  db.close();
}
  1. Statement 使用完立即释放
const stmt = db.prepare("SELECT * FROM users");
stmt.bind([18]);
while (stmt.step()) {
  // 处理数据
}
stmt.free(); // 立即释放
  1. 页面卸载时关闭数据库
onUnload() {
  if (this.db) {
    this.db.close();
    this.db = null;
  }
}

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT license

Copyright (c) 2017 sql.js authors (see AUTHORS)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Some portions of the Makefile taken from:

Copyright 2017 Ryusei Yamaguchi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.