Lubejs
Use oracle db as mongodb in node.js, base on node-oracledb
Install / Use
/learn @jovercao/LubejsREADME
Lubejs
Lubejs 是一个用于
node.js诣在方便使用SQL数据库连接. 取名为lube意为润滑,即作为js与sql间的润滑剂般的存在,我们可以尽情使用优雅的 js/ts来 替代拼接 sql 字符串。
lubejs是什么
lubejs 是一套类型化sql构建、执行工具,亦是一套强大易用的Typescript ORM开发框架。
- 完备的SQL构建工具,使用最贴近SQL的语法编写SQL,极低的学习成本
- 强大的Typescript类型支持,支持反向类型推导,返回明确类型,拥有完整类型安全体系,智能语法提示,提高开发效率以及预排除类型错误,强烈建立在typescript项目中使用lubejs。
- ORM配套工具,Code first、数据迁移
- 匹配多种数据库(目前只支持mssql)
- 跨数据库兼容,为此,lubejs建立了标准行为库,把大多数常用的,而在各个数据库中又不尽相同的操作行为,包括在其中。
lubejs理念
- 简洁,极简api,极易上手
- 贴近自然,语法与标准sql极为接近,大大降低学习成本
- 渐进式,lubejs分为两个层级的引用,core及完整功能包
- 多数据库方言统一兼容,建立中间标准操作库并不断丰富。
- 完整的typescript类型安全
快速开始
安装
使用 npm 安装:
# 安装lubejs库
npm install lubejs --save
# 安装lubejs-mssql驱动
npm install lubejs-mssql
开始
Hello world!
// hello-world.ts
import { connect, SQL } from 'lubejs'
// 导入mssql驱动
import 'lubejs-mssql'
(async () => {
// 创建连接
const db = await connect('mssql://user:password@localhost:1433/database');
// SELECT 'hello world'
console.log(await db.queryScalar(SQL.select('hello world!'))); // => 'hello world'
await db.close();
})()
完整范例
// example.ts
import {
connect,
SQL,
Decimal,
Uuid,
Connection,
DbType,
outputCommand,
} from "lubejs";
import "lubejs-mssql";
interface Table1 {
id: number;
name: string;
stringField?: string;
floatField?: number;
dateField?: Date;
decimalField?: Decimal;
uuidField?: Uuid;
updatedAt: Date;
binaryField?: ArrayBuffer;
createdAt: Date;
operator?: string;
}
interface Pay {
id?: number;
year: number;
month: number;
amount: Decimal;
personId: number;
}
interface Person {
id?: number;
name: string;
age: number;
}
/**
* 初始化数据库
*/
async function initDb(db: Connection) {
await db.query(
SQL.if(SQL.std.existsTable('table1')).then(SQL.dropTable("table1"))
);
await db.query(
SQL.createTable("table1").as(({ column }) => [
column("id", DbType.int32).identity().primaryKey(),
column("name", DbType.string(100)).notNull(),
column("stringField", DbType.string(100)).null(),
column("floatField", DbType.float).null(),
column("dateField", DbType.datetimeoffset).null(),
column("decimalField", DbType.decimal(18, 6)),
column("uuidField", DbType.uuid),
column("updatedAt", DbType.datetimeoffset).default(SQL.std.now()),
column("binaryField", DbType.binary(DbType.MAX)),
column("createdAt", DbType.datetimeoffset).default(SQL.std.now()),
column("operator", DbType.string(100)).null(),
])
);
await db.query(
SQL.if(SQL.std.existsTable('pay')).then(SQL.dropTable("pay"))
);
await db.query(
SQL.createTable("pay").as(({ column }) => [
column("id", DbType.int32).identity().primaryKey(),
column("year", DbType.int32),
column("month", DbType.int32),
column("amount", DbType.decimal(18, 2)),
column("personId", DbType.int32),
])
);
await db.query(
SQL.if(SQL.std.existsTable('person')).then(SQL.dropTable("person"))
);
await db.query(
SQL.createTable("person").as(({ column }) => [
column("id", DbType.int32).identity().primaryKey(),
column("name", DbType.int32).notNull(),
column("age", DbType.int32),
])
);
}
/**
* Table1表声明
*/
// 这是一个范例
async function example(db: Connection) {
//---------------插入数据------------------
/*
* INSERT INTO table1 (stringField, floatField, dateField)
* VALUES ('value1-1', 2, Convert(DATETIMEOFFSET, '2019-11-18 00:00:00'))
* ('value1-2', 1, Convert(DATETIMEOFFSET, '2019-11-18 00:00:00'))
* ('value1-3', 45, Convert(DATETIMEOFFSET, '2019-11-18 00:00:00'))
*/
const insertSql = SQL.insert<Table1>("table1").values([
{
name: "item1",
stringField: "value1-1",
floatField: 3.14,
dateField: new Date(),
decimalField: new Decimal("3.1415"),
uuidField: Uuid.new(),
binaryField: Buffer.from('abcdefeg')
},
{
name: "item2",
stringField: "value1-2",
floatField: 1.132,
dateField: new Date(),
decimalField: new Decimal("3.1415"),
uuidField: Uuid.new(),
binaryField: Buffer.from('abcdefeg')
},
{
name: "item3",
stringField: "value1-3",
floatField: 45.2656,
dateField: new Date(),
decimalField: new Decimal("3.1415"),
uuidField: Uuid.new(),
binaryField: Buffer.from('abcdefeg')
},
]);
await db.query(insertSql);
// 你还以使用以下方式插入,等效于上面的写法
await db.insert<Table1>("table1", [
{
name: "item1",
stringField: "value1-1",
floatField: 3.14,
dateField: new Date(),
decimalField: new Decimal("3.1415"),
uuidField: Uuid.new(),
binaryField: Buffer.from('abcdefeg')
},
{
name: "item2",
stringField: "value1-2",
floatField: 1.132,
dateField: new Date(),
decimalField: new Decimal("3.1415"),
uuidField: Uuid.new(),
binaryField: Buffer.from('abcdefeg')
},
{
name: "item3",
stringField: "value1-3",
floatField: 45.2656,
dateField: new Date(),
decimalField: new Decimal("3.1415"),
uuidField: Uuid.new(),
binaryField: Buffer.from('abcdefeg')
},
]);
//---------------更新数据------------------
// UPDATE t SET updatedAt = Convert(DateTime, '2019-11-18 00:00:00') FROM table1 t WHERE id = 1
const t = SQL.table<Table1>("table1").as("t");
const updateSql = SQL.update(t)
.set({ updatedAt: new Date(), operator: "your name" })
.where(t.id.eq(1));
await db.query(updateSql);
// 你还以使用以下方式更新,等效于上面的写法
await db.update<Table1>(
"table1",
{ updatedAt: new Date(), operator: "your name" },
{ id: 1 }
);
//---------------删除数据-------------------
// DELETE t FROM table1 t WHERE t.id = 1
const deleteSql = SQL.delete(t).from(t).where(t.id.eq(1));
await db.query(deleteSql);
// 你还以使用以下方式删除
// DELETE table1 WHERE id = 1
await db.delete("table1", { id: 1 });
//----------------查询数据--------------------
// SELECT t.* FROM table1 AS t WHERE t.id = 1 AND t.name = 'name1'
const selectSql = SQL.select(t.star)
.from(t)
.where(SQL.and(t.id.eq(1), t.name.eq("name1")));
console.log((await db.query(selectSql)).rows);
// You can also select in this way
// SELECT * FROM table1 WHERE id = 1 AND name = 'name1'
console.log(
await await db.select("table1", {
where: {
id: 1,
name: "item1",
},
})
);
// //---------------以下是一个复合查询------------
const p = SQL.table<Person>("person").as("p");
const pay = SQL.table<Pay>("pay");
const sql = SQL.select({
year: pay.year,
month: pay.month,
name: p.name,
age: p.age,
total: SQL.std.sum(pay.amount),
})
.from(pay)
.join(p, pay.personId.eq(p.id))
.where(p.age.lte(18))
.groupBy(p.name, p.age, pay.year, pay.month)
.having(SQL.std.sum(pay.amount).gte(new Decimal(100000)))
.orderBy(pay.year.asc(), pay.month.asc(), SQL.std.sum(pay.amount).asc(), p.age.asc())
.offset(20)
.limit(50);
console.log((await db.query(sql)).rows);
}
(async () => {
// 创建一个Lube连接
const db = await connect("mssql://sa:!crgd-2021@rancher.vm/Test");
// 打开连接
await db.open();
// 输出日志
db.on('command', (cmd) => outputCommand(cmd, process.stdout))
try {
await initDb(db);
await example(db);
} finally {
await db.close();
}
})();
版本说明
注意: lubejs目前仍为预览版,内部会有部分调整,公共API可能会有小许调整,但不会有大调整。
渐进式分离
lubejs/core为核心包,包括sql构建以及sql执行工具。lubejs则为完整包,包括lubejs/core的所有内容以及orm功能,数据迁移cli等。
数据库支持列表
- [x] mssql - 目前支持microsoft sqlserver 2012 或更高版本, 库基于
node-mssql开发. - [ ] mysql - 当前正在开发中
- [ ] postgresql - 计划于2021年底开发
NodeJs版本支持
nodejs >= 12.0
概念
SQL构造器(SQL对象)
所有的SQL构造,均由 SQL对象发起,几乎所有的SQL的语句,均可从SQL对象创建,例如SQL.select,SQL.update,SQL.delete等。
// 导入SQL对象
import { SQL } from 'lubejs';
为了更贴近sql语法,您还可以使用解构来引入需要的关键字
const {
insert,
delete: $delete // 关键字delete需要使用别名
} = SQL
// 构建插入张三、李四两条记录到table1的语句
const sql = insert('table1').values([{ name: '张三', age: 19, sex: '男' }, { name: '李四', age: 25, sex: '男' }]);
// 构建table1表中删除id为1记录的sql语句
const sql = $delete('table1').where({ id: 1 })
更多SQL对象用法,请翻阅《api参考》
注意:delete为js关键字,需要使用别名代替,其它关键字亦是如此
标准行为(SQL.std)
lubejs为了更大程序的兼容多数据库,专门定义了标准行为,用于统一在跨数据库时的操作,避免在跨方言数据库迁移是的重复劳动。
SQL.std中定义了许多常用的函数、操作等行为
常用函数
| 说明 | 函数 | 备注 |
| -------------------- | ------------------------------------------------------------ | ---- |
| 类型转换 | SQL.std.convert(expr, dbType)、Expression.prototype.to(dbType) | |
| 当值为空时返回默认值 | SQL.std.nvl(value, defaultValue) | |
聚合函数
| 说明 | 函数 | 备注 |
| ------ | --------------------- | ---- |
| 计数 | SQL.std.count(expr) | |
| 平均 | SQL.std.avg(expr) | |
| 求和 | SQL.std.sum(expr) | |
| 最大值 | SQL.std.max(expr) | |
| 最小值 | SQL.std.min(expr) | |
日期函数
| 说明 | 函数 | 备注 |
| ---------------------- | -------------------------------------- | ---- |
| 当前时间 | SQL.std.now(expr) | |
| UTC当前时间 | SQL.std.utcNow(expr) | |
| 切换时区 | SQL.std.switchTimezone(date, offset) | |
| 格式化日期 | SQL.std.formatDate(date, format) | |
| 取日期中的年份 | SQL.std.yearOf(date) | |
| 取日期中的月份 | SQL.std.monthOf(date) | |
| 取日期中的日期 | SQL.std.dayOf(date) | |
| 取两个日期之间的天数 | SQL.std.daysBetween(star, end) | |
| 取两个日期之间的月数 | SQL.std.monthsBetween(star, end) | |
| 取两个日期之间的年数 | `
