数据库访问手册(打印精灵内置 JS)
本文档面向打印精灵内置 JS 开发者,介绍脚本中数据库访问(DB 对象)的用法,
涵盖 ORM 查询构建器、事务、错误处理与连接池语义。
数据库函数具有广泛的用途,配合打印精灵的Hook函数, 如打印前获取订单的详细信息,打印后更新数据库的状态等等,可以实现各种复杂的业务), 如打印前获取订单的详细信息,打印后更新数据库的状态等等,流程:
例子: 订单获取和打印的例子:
1// updateVars()为钩子函数,每次打印前会自动调用
2function updateVars() {
3 let orderid= get("orderid") //获取从API传递
4 let db = DB.Open("mysql:user:pass@tcp(host:port)db") //连接数据库
5 let data=db.Query(`select * from order where id={$orderid}`) 数据查询
6 return data[0] //使用return 返回查询结果,会更新变量表
7}
该例子中,每次打印前会自动调用updateVars()函数,通过送入的orderid参数获取订单的详细信息,并打印。
虽然通过API送入完整的订单数据是一样的效果,但是更加灵活:API送入的参数只有一个,不需要修改打印程序,只修改标签模板,就可以实现不同产品的订单打印。
1. 支持的数据类型与 DSN
DSN 一律以 类型: 前缀开头(前缀大小写不敏感):
| 类型前缀 | 说明 |
|---|---|
sqlite: / sqlite3: |
SQLite 文件;相对路径基于内置 baseDir 展开 |
sqlite::memory: |
内存库,每次 Open 都是独立数据库,不入池 |
mysql: |
MySQL,DSN 为 user:pass@tcp(host:port)/db |
postgres: / postgresql: / pgsql: |
PostgreSQL,DSN 为键值连接串 |
mssql: / sqlserver: |
SQL Server,DSN 为 //user:pass@host:1433?database=db |
不带冒号前缀的 DSN(或单字母盘符,如 C:\data.xlsx)会被当作 Excel 文件打开,
第二个参数为工作表名。
1const db = DB.Open("sqlite:data.db");
2const db2 = DB.Open("sqlite::memory:");
3const db3 = DB.Open("mysql:root:pwd@tcp(localhost:3306)/shop");
2. 快速开始
1const db = DB.Open("sqlite:data.db");
2
3// 建表
4db.Exec(`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT,
5 name TEXT, age INTEGER)`);
6
7// 插入并取回自增 id
8const id = db.Table("users").Create({ name: "张三", age: 25 });
9
10// 查询
11const rows = db.Table("users").Where("age > ?", 20).Order("age desc").Find();
12
13// 事务
14const r = db.Transaction(function (tx) {
15 tx.Table("users").Where("name = ?", "张三").Update({ age: 26 });
16 return { ok: true }; // 返回普通对象 → 提交;返回含 error 键的对象 → 回滚
17});
18if (r.error) { /* 失败 */ }
19
20db.Close();
3. 数据源开关
DB.Open(dsn[, sheet])— 打开数据源,返回数据库对象;打开失败抛异常。DB.Close(dsn[, sheet])— 释放对应数据源(池内引用减一)。
打开后的对象也可通过 .Close() 关闭:
1const db = DB.Open("sqlite:data.db");
2// ... 使用
3db.Close();
DB.Close("sqlite:data.db")与db.Close()效果相同(引用计数减一),不要重复调用。
4. ORM 查询构建器
db.Table("表名") 返回查询构建器,方法均可链式调用。
| 方法 | 说明 |
|---|---|
Select(fields) |
选择字段,如 "id, name";默认 SELECT * |
Where(...) |
追加 AND 条件 |
Or(...) |
追加 OR 条件 |
Order(order) |
排序,如 "age desc" |
Limit(n) |
限制行数 |
Offset(n) |
偏移 |
Joins(joinSQL) |
追加 JOIN 子句 |
PrimaryKey(col) |
指定主键列(默认 id,影响 UpdateById/DeleteById) |
Where / Or 的写法
1// 1. 字符串 + 占位参数
2db.Table("users").Where("age > ?", 20)
3
4// 2. 单参数对象,键=列,值=等值条件
5db.Table("users").Where({ name: "张三" })
6
7// 3. 多条件叠加(AND)
8db.Table("users").Where("age > ?", 20).Where({ active: 1 })
Or 同理:
1db.Table("users").Where("age > ?", 30).Or("age < ?", 25)
2// 生成: WHERE (age > ?) OR (age < ?)
WHERE 与 OR 段会被自动加上括号,避免
a AND b OR c这类优先级歧义。
分页
1const page2 = db.Table("users").Order("id").Limit(10).Offset(10).Find();
各驱动分页语法:
- sqlite / mysql / postgres:
LIMIT n OFFSET m;只设Offset时自动补齐无上限的 LIMIT。 - mssql:自动转为
ORDER BY ... OFFSET m ROWS FETCH NEXT n ROWS ONLY(无 ORDER BY 时补ORDER BY (SELECT NULL))。
5. 查询执行
| 方法 | 返回 |
|---|---|
Find() |
数组,每行为 {列: 值} |
First() |
单行对象;无记录时报错 |
Count() |
数字 |
Pluck(col) |
某列的值数组 |
1const rows = db.Table("users").Where({ age: 25 }).Find();
2const one = db.Table("users").Where("id = ?", 1).First();
3const n = db.Table("users").Count();
4const names = db.Table("users").Pluck("name");
回调风格
Find/First/Count/Pluck 支持传入回调,出错时回调收到 (null, {error: 消息}),
不再抛异常:
1db.Table("users").Where("id = ?", 1).First(function (err, res) {
2 if (res.error) { console.log("查询失败:", res.error); return; }
3 console.log(res); // {id, name, age}
4});
不传回调时,出错会抛异常终止脚本。
6. 写操作
| 方法 | 返回 | 说明 |
|---|---|---|
Create(data) |
自增 id | 单行插入 |
CreateBulk(rows) |
受影响行数 | 批量插入;行与首行字段不一致会报错 |
Update(data) |
受影响行数 | 必须带 Where/Or,否则报错 |
UpdateById(id, data) |
受影响行数 | 按主键更新(默认 id 列) |
Delete() |
受影响行数 | 必须带 Where/Or,否则报错 |
DeleteById(id) |
受影响行数 | 按主键删除 |
1db.Table("users").Create({ name: "李四", age: 30 });
2db.Table("users").Where("name = ?", "李四").Update({ age: 31 });
3db.Table("users").Where("id = ?", 1).Delete();
4
5// 非 "id" 主键
6db.Table("users").PrimaryKey("uid").UpdateById(1, { age: 32 });
7db.Table("users").PrimaryKey("uid").DeleteById(1);
8
9// 批量
10db.Table("users").CreateBulk([
11 { name: "王五", age: 22 },
12 { name: "赵六", age: 35 },
13]);
写操作出错一律抛异常。
7. 原生 SQL
1const affected = db.Exec("UPDATE users SET age = ? WHERE name = ?", 26, "张三");
2const rows = db.Query("SELECT * FROM users WHERE age > ?", 20);
Exec返回受影响行数,Query返回[{列: 值}]。- 出错抛异常。
8. 事务
db.Transaction(callback)
回调接收 tx(支持 .Table(),用法与 db.Table() 一致)。
返回值决定结果:
- 返回不含
error键的对象(或其它值)→ 提交。 - 返回含
error键的对象 → 回滚并原样返回该对象。 - 回调内部抛异常 → 自动回滚并继续抛出。
1const r = db.Transaction(function (tx) {
2 tx.Table("accounts").Where("name = ?", "张三").Update({ balance: 90 });
3 if (balance < 0) return { error: "余额不足" }; // 回滚
4 return { ok: true }; // 提交
5});
6if (r.error) { console.log(r.error); }
注意事项
- 事务内
Count/Find等查询走事务连接,能看到本事务未提交的数据。 - 事务内不要使用
db.Exec/db.Query(它们是连接级接口,不走事务)。 - 事务异常时保证回滚,不会悬挂连接。
9. 错误处理约定
| 场景 | 行为 |
|---|---|
DB.Open 失败 |
抛异常 |
| 查询(无回调)失败 | 抛异常 |
| 查询(带回调)失败 | 回调收到 {error: 消息} |
First 无记录 |
报错 record not found |
| 写操作失败 | 抛异常 |
| 原生 SQL 失败 | 抛异常 |
| 事务失败 | 返回 {error: 消息} 或抛异常 |
10. 连接池与关闭语义
- 同一 DSN(类型 + 地址)共享一个底层连接池,使用引用计数管理。
Open成功一次计数 +1;Close/db.Close()一次计数 -1;归零时才真正关闭连接。sqlite::memory:例外:不入池,每次Open独立内存库,且限制单连接。
1// 同一文件打开两次,底层共享同一连接池
2const db1 = DB.Open("sqlite:data.db");
3const db2 = DB.Open("sqlite:data.db");
4db1.Close(); // 仍可用,db2 持有引用
5db2.Close(); // 引用归零,真正关闭
11. 驱动方言差异
- 占位符:统一使用
?。postgres 驱动会自动转换为$1、$2…。 - 分页:mssql 自动转
OFFSET/FETCH;其余用LIMIT/OFFSET。 - 主键:
UpdateById/DeleteById默认用id列,可用PrimaryKey()指定。 - SQLite 建库时自动创建文件所在目录。