sql

sql

sql模块是vino提供的用来操作关系型数据库的统一接口,支持mysqlpostgressqlserverclickhouse

快速上手

这是一个简单的案例来操作mysql数据库。

import sql from "sql"

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,       // 连接池中最大打开的连接数.如果达到这个限制,新的连接请求将会等待直到连接池中有空闲的连接或者现有的连接被关闭。
	maxIdleConns: 10,       // 连接池中最大空闲连接数.如果空闲连接数超过这个值,连接将会被关闭以释放资源。保持一定数量的空闲连接可以减少连接建立的开销。
	connMaxIdleTime: 12,    // 连接在连接池中空闲的最大时间(单位:秒)。超过这个时间的连接将会被关闭和移除。这有助于清理长时间未使用的连接。
	connMaxLifetime: 12,    // 连接在连接池中的最大生命周期。(单位:秒).连接达到这个时间后将会被关闭并且在下次需要时重新建立。这个设置可以帮助避免由于数据库连接长时间未更新而出现的问题,如连接的超时和失效。
	bigintToType: 'string',   // 决定是否将bigint类型的字段值转换为字符串(string/bigint/int/float)
};
let db = sql.open("mysql", config);
let users = db.query(`select * from user limit 0,10`);
console.log(users);

属性与方法

名称类型参数返回值说明
open方法name:string,config:objectClient创建数据库连接对象

open

open方法用来创建数据库连接对象。

语法

sql.open(name:string,config: object)

参数

  • name 数据库名称。目前支持的数据库:mysqlpostgressqlserverclickhouse
  • config 数据库连接配置。

config

config参数的配置项如下:

{
  url: "root:123456@tcp(127.0.0.1:3306)/test",
  maxOpenConns: 40,
  maxIdleConns: 10,
  connMaxIdleTime: 12,
  connMaxLifetime: 12,
  bigintToType: 'string',
}

参数

  • maxOpenConns:连接池中最大打开的连接数.如果达到这个限制,新的连接请求将会等待直到连接池中有空闲的连接或者现有的连接被关闭。
  • maxIdleConns:连接池中最大空闲连接数.如果空闲连接数超过这个值,连接将会被关闭以释放资源。保持一定数量的空闲连接可以减少连接建立的开销。
  • connMaxIdleTime:连接在连接池中空闲的最大时间(单位:秒).超过这个时间的连接将会被关闭和移除。这有助于清理长时间未使用的连接。
  • connMaxLifetime:连接在连接池中的最大生命周期。(单位:秒) .连接达到这个时间后将会被关闭并且在下次需要时重新建立。这个设置可以帮助避免由于数据库连接长时间未更新而出现的问题,如连接的超时和失效。

url

不同数据库对应的url格式不同,具体格式如下:

1. mysql

  • 格式: username:password@protocol(address)/dbname?param=value

  • 参数

    • username: 用户名
    • password: 密码
    • protocol : 使用的协议,通常是 tcp 或 unix。
    • address: 数据库地址,对于 TCP 连接是 host:port,对于 Unix 套接字是 socket path。
    • dbname: 默认连接的数据库名称
    • param: 其他参数
      • charset: 设置连接使用的字符集。例如 charset=utf8mb4。
      • collation: 设置连接使用的字符集排序规则。例如 collation=utf8mb4_unicode_ci
      • parseTime: 当设置为 true 时,将数据库的时间类型(DATE, DATETIME)自动解析为Date类型。例如 parseTime=True。
      • loc: 设置时区。例如,要使用本地时区,可以设置为 loc=Local。也可以指定特定的时区,如 loc=America/New_York。
      • tls: 配置 TLS/SSL 连接。可以是 true、false 或一个自定义配置的名称.例如 tls=true
      • timeout: 设置建立连接的超时时间。例如 timeout=30s。
      • readTimeout :读操作超时时间。例如 readTimeout=30s
      • writeTimeout: 写操作超时时间。例如 writeTimeout=30s
      • allowOldPasswords: 允许使用旧的(不安全的)密码格式。例如 allowOldPasswords=1
      • clientFoundRows: 返回找到的行数而不是影响的行数。例如 clientFoundRows=True
      • multiStatements: 允许执行多条 SQL 语句。例如 multiStatements=True
      • maxAllowedPacket: 设置允许的最大数据包大小。
      • serverPubKey: 用于验证服务器公钥。
  • 示例: root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4

示例

import sql from "sql";

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string',
};
let db = sql.open("mysql", config);

2. postgres

  • 格式 postgres://username:password@host:port/dbname?param=value

  • 参数

    • username: 用户名。
    • password: 密码。
    • host 数据库ip或域名
    • port 端口(默认:5432)。
    • dbname 数据库名。
    • param
      • sslmode 是否禁用ssl。SSL 模式(disable, allow, prefer, require, verify-ca, verify-full)
        • disable:不使用 SSL
        • allow:优先不使用 SSL,但服务器要求时使用
        • prefer:优先使用 SSL,但服务器不支持时不使用
        • require:强制使用 SSL
        • verify-ca:使用 SSL 并验证服务器证书是否由受信任的 CA 签名
        • verify-full:使用 SSL 并验证服务器证书是否由受信任的 CA 签名且服务器名称匹配
      • connect_timeout 设置连接超时(单位:秒)例如 connect_timeout=10
      • application_name 应用名称 例如 application_name=myapp
      • fallback_application_name 备用应用名称 例如 fallback_application_name=myfallbackapp
      • search_path 指定 schema 搜索路径 例如 search_path=myschema
      • timezone 设置时区 例如 timezone=UTC
      • sslcert SSL 证书文件 例如 sslcert=/path/to/cert
      • sslkey SSL 密钥文件 例如 sslkey=/path/to/key
      • sslrootcert SSL 根证书文件 例如 sslrootcert=/path/to/rootcert
      • target_session_attrs:目标会话属性(read-write 或 any) 例如 target_session_attrs=read-write
      • client_encoding:客户端编码 例如 client_encoding=UTF8
      • binary_parameters:启用或禁用二进制参数传输(yes 或 no) 例如 binary_parameters=yes
  • 示例 postgres://postgres:123456@127.0.0.1:5432/test?sslmode=disable

示例

import sql from "sql";

let config = {
	url: "postgres://postgres:123456@127.0.0.1:5432/test?sslmode=disable",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string'
};
let db = sql.open("postgres", config);
//执行查询
let users = db.query(`select * from test_dev.user where id = $1`, [1]);
console.log(users);

3. sqlserver

  • 格式 sqlserver://username:password@host:port/instance?param1=value&param2=value

  • 参数

    • username: 用户名
    • password: 密码
    • host: 数据库的ip或域名
    • port: 监听的端口号。(默认:1433)
    • instance: SQL Server 的实例名称,通常无需指定实例名称
    • param
      • database: 要连接的特定数据库名称。
      • encrypt: 是否启用加密
        • disable- 客户端和服务器之间发送的数据未加密。
        • false- 客户端和服务器之间发送的数据除登录数据包外均不加密。(默认)
        • true- 客户端和服务器之间发送的数据是加密的。
      • connection timeout: 连接超时时间(以秒为单位)。connection timeout=30
  • 示例 sqlserver://sa:123456@localhost:1433?database=test

示例

import sql from "sql";

let config = {
	url: "sqlserver://sa:123456@localhost:1433?database=test",
	maxOpenConns: 12,
	maxIdleConns: 12,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: "string",
}
let db = sql.open("sqlserver", config);
let users = db.query(`select * from users where id = @p1`, [1]);
console.log(users);

4. clickhouse

  • 格式 clickhouse://user:password@IP:port/database?param1=value&param2=value

  • 参数

    • username: 用户名
    • password: 密码
    • host: 数据库的ip或域名
    • port: 监听的端口号。(默认:9000)
  • 示例 clickhouse://vino:vino123@127.0.0.1:9000/database

示例

// 导入模块
import sql from "sql";
// 数据库连接池配置
let config = {
	url: "clickhouse://vino:vino123@127.0.0.1:9000/database",
};
// 创建数据库连接客户端Client
let dbClient = sql.open("clickhouse", config);
console.log("dbClient", dbClient);
console.log("res", res);

提示

数据库密码如果有特殊字符,需要进行URLEncoding(如:$ABC => %24ABC

Client

通过数据库客户端对象,就可以操作数据库。

属性与方法

名称类型参数返回值说明
query方法sql:string,params?:[]any,clz?:classobject[]执行查询操作,返回查询结果
queryJson方法sql:string,params?:[]anystring执行查询操作,返回查询json字符串
exec方法sql:string,params?:[]anyobject执行更新操作,返回影响行数等信息
begin方法tx开启事务,返回事务对象
prepare方法sql:stringstmt创建预处理语句,返回预处理对象

提示

query 方法用于执行具有返回值的语句,例如:select语句 exec 方法用于执行返回值为影响行数的语句,例如:insert,update,delete语句

query

query方法用于执行sql并返回结果集

语法

db.query(sql:string,params?:[]any,clz?:class);

参数

  • sql : 需要执行的sql语句字符串。
  • params:(可选) 用于向sql语句中传递数据库占位符vino占位符的参数
  • clz:(可选) 用于指定返回结果的类型,默认返回的是object数组,如果需要自定义返回结果数据,可以指定一个Class。

返回值

  • object[] : 返回的结果集,是一个对象数组,是sql查询结果。

示例

import sql from "sql";

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string',
};
let client = sql.open("mysql", config);
//使用vino占位符
let users = client.query(`select id,name,age from user limit #{0},#{1}`, [1, 10]);
//使用数据库占位符
//let users = client.query(`select id,name,age from user limit ?,?`, [1, 10]);
console.log(users)
import sql from "sql";

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string',
};
let client = sql.open("mysql", config);

//通过类来自定义返回的数据结构
class User {
	id = ""
	age = 0
	name = ""
}

let users = client.query(`select id,name,age from user limit #{0},#{1}`, [1, 10], User);
console.log(users)

queryJson

queryJson方法用于执行sql并返回json字符串形式的结果

提示

该方法适用于在执行完查询操作后,需要直接返回json数据的场景。该方法省略了数据库->vino对象 的转换过程,性能上会比数据库->vino对象->json字符串要高。

语法

db.queryJson(sql:string,params?:[]any);

参数

返回值

  • string : 返回json字符串形式的结果。

示例

import sql from "sql";

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string',
};
let client = sql.open("mysql", config);
let json = client.queryJson(`select id,name,age from user limit #{0},#{1}`, [1, 10]);
console.log(json)

exec

exec方法用于执行sql并返回执行状态结果,用来执行不需要返回结果集的查询,例如:udpatedeleteinsert

语法

db.exec(sql:string,params?:[]any);

参数

返回值

  • object: exec方法的返回值是一个对象,对象结构如下:
{
  rowsAffected: 1,
  //表示SQL语句执行后影响的行数
  lastInsertId: 123456
  //表示最近一次INSERT操作中,插入行的自增ID(如果有的话)。(该属性只有部分数据库支持)
}

示例

import sql from "sql";

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string',
};
let client = sql.open("mysql", config);
//使用vino占位符
let res = client.exec(`insert into user(id,name) values(#{id},#{name})`, {id: 1, name: "vino"});
//使用数据库占位符
//let res = client.exec(`insert into user(id,name) values(?,?)`,[1,'vino']);
console.log(res)

注意事项

  • 支持数据库占位符vino占位符,但不能同时使用2种占位符
  • 当使用数据库占位符params必须为数组
  • 当使用vino占位符params可以为数组对象Map
  • 当数据库为clickhouse时,并不会返回执行后影响的行数

begin

begin方法用于开启事务,并返回一个事务对象tx

语法

db.begin();

返回值

该方法的返回值为一个tx对象,该对象表示一个数据库事务。

prepare

prepare方法用于开启预处理,并返回一个预处理对象stmt

语法

let stmt = db.prepare(sql:string);

参数

  • sql : 预处理语句,这是一个带有数据库占位符的sql字符串
    • 例如:select * from test where id=?

返回值

该方法的返回值为一个stmt对象,该对象表示一个预处理。

作用

预处理语句是一种特殊的SQL语句,它在数据库中被编译并存储,以便多次执行,而无需每次执行时都重新编译。

使用预处理语句的好处包括:

  • 性能提升:预编译的 SQL 语句只需要编译一次,可以多次执行,从而提高性能
  • 安全性高:预处理语句的参数在执行时是分开处理的,这有助于防止SQL注入攻击,因为攻击者无法改变SQL语句的结构。

tx

tx对象代表数据库事务,通过同一个tx对象执行的多次sql都在同一个事务中。

属性与方法

名称类型参数返回值说明
query方法sql:string,params?:[]any,clz?:classobject[]在事务中执行查询操作
queryJson方法sql:string,params?:[]anystring在事务中执行查询操作,并返回json字符串形式的结果
exec方法sql:string,params?:[]anyobject在事务中执行更新操作
prepare方法sql:stringstmt创建预处理
commit方法void提交事务
rollback方法void回滚事务

commit

commit函数用来提交事务。

示例

tx.commit()

rollback

rollback函数用来回滚事务。

示例

tx.rollback()

案例

import sql from "sql";

let config = {
	url: "root:123456@tcp(127.0.0.1:3306)/test",
	maxOpenConns: 40,
	maxIdleConns: 10,
	connMaxIdleTime: 12,
	connMaxLifetime: 12,
	bigintToType: 'string',
};
let client = sql.open("mysql", config);
//开启事务
let tx = client.begin();
try {
	//通过tx对象来执行sql
	let users = tx.query(`select * from user limit 0,10`);
	let res = tx.exec(`insert into user(name) values('vino')`);
	//提交
	tx.commit();
} catch (e) {
	//回滚
	tx.rollback();
}

stmt

stmt对象表示一个预处理对象

属性与方法

名称类型参数返回值说明
query方法params:[]any,clz?:classobject[]使用预处理语句执行查询操作
queryJson方法params:[]anystring使用预处理语句执行查询操作并返回json字符串形式的结果
exec方法params:[]anyobject使用预处理语句执行更新操作
close方法void关闭预处理语句

query

query方法用于向数据库传递占位符参数,返回结果集

语法

stmt.query(params:[]any,clz?:Class);

参数

  • params: 用于向预处理对象传递数据库占位符的参数
  • clz: (可选) 用于指定返回结果的类型,默认返回的是object数组,如果需要自定义返回结果数据,可以指定一个Class。

返回值

  • object[] : 返回的结果集,是一个对象数组,是sql查询结果。该结果集中每一个对象的属性名和sql执行结果的字段名相同

queryJson

queryJson方法用于向数据库传递占位符参数,返回json字符串形式的结果

语法

stmt.queryJson(params:[]any);

参数

返回值

  • string : 返回json字符串结果

exec

exec方法用于向数据库传递占位符参数,返回执行状态结果,用来执行不需要返回结果集的查询,例如:udpatedeleteinsert

语法

stmt.exec(params:[]any);

参数

返回值

  • object: exec方法的返回值是一个对象,对象结构如下:
{
	rowsAffected:1,     //表示SQL语句执行后影响的行数
	lastInsertId:123456 //表示最近一次INSERT操作中,插入行的自增ID(如果有的话)。(该属性只有部分数据库支持)
}

close

close方法用于关闭预处理对象。

示例

stmt.close()

注意事项

预编译必须使用数据占位符,不支持vino占位符

案例

let stmt = client.prepare(`select * from test where id=?`);
try {
	for (let i = 0; i < 10; i++) {
		//调用query方法时,只传递占位符参数
		let res = stmt.query([i]);
		console.log(res)
	}
} catch (e) {
	//异常处理
} finally {
	//处理完成后,记得关闭stmt
	stmt.close();
}

数据库占位符

数据库占位符是数据库原生支持的占位符,其用于在预编译的SQL语句中表示参数的位置。不同的数据库系统使用的占位符符号可能不同,但其作用是相同的:在执行 SQL 语句时,通过占位符将实际的参数值传递给 SQL 语句,以提高安全性和性能。除此以外,vino也提供了vino占位符

?

mysqlclickhouse数据库使用问号?作为占位符。例如:

SELECT * FROM users WHERE id = ?;
INSERT INTO users(name, age) VALUES(?, ?);
  • 使用方式

占位符在sql中的顺序和param数组元素的顺序保持一致。

let data = ["1", "vino"]
client.query(`select id,name,age from user where id=? and name=?`, data);

$

postgres数据库使用$1, $2等位置参数作为占位符。

SELECT name FROM users WHERE id = $1
  • 使用方式

占位符在sql中的位置参数和param数组元素的位置保持一致。

let data = ["1", "vino"]
client.query(`select id,name,age from user where id=$1 and name=$2`, data);

@p

sqlserver数据库使用@p1, @p2等位置参数作为占位符。

SELECT name FROM users WHERE id = @p1
  • 使用方式

占位符在sql中的位置参数和param数组元素的位置保持一致。

let data = ["1", "vino"]
client.query(`select id,name,age from user where id=@p1 and name=@p2`, data);

注意

使用占位符可以防止 SQL 注入攻击。因为参数不会直接拼接到 SQL 语句中,数据库驱动会自动处理参数的转义,从而避免恶意用户注入危险的 SQL 代码。

vino占位符

vino提供了所有数据库一致的、功能更加强大的、容易使用的占位符#{}&{}。它比数据库占位符功能更强大,除了支持数组 以外,还支持对象Mapvino占位符由vino进行解析并最终将其转换为对应的数据库占位符。

#{}

占位符#{}支持数组下标和对象属性,它最终会转换为数据库原生占位符

示例1

params为数组时,占位符中可以使用数组下标

let data = ["1", "vino"]
client.query(`select id,name,age from user where id=#{0} and name=#{1}`, data);
//vino会转换为下列结果:
//params=["1","vino"]
//client.query(`select id,name,age from user where id=? and name=?`, params); //mysql
//client.query(`select id,name,age from user where id=$1 and name=$2`, params);//postgres
//client.query(`select id,name,age from user where id=@p1 and name=@p2`, params);//sqlserver

示例2

params为对象时,占位符中可以使用对象属性名

let data = {
	id: "1",
	name: "vino"
}
client.query(`select id,name,age from user where id=#{id} and name=#{name}`, data);
//vino会转换为下列结果:
//params=["1","vino"]
//client.query(`select id,name,age from user where id=? and name=?`, params); //mysql
//client.query(`select id,name,age from user where id=$1 and name=$2`, params);//postgres
//client.query(`select id,name,age from user where id=@p1 and name=@p2`, params);//sqlserver

示例3

params为Map时,占位符中可以使用Map的key

let map = new Map()
map.set("id", 1)
map.set("name", "vino")
client.query(`select id,name,age from user where id=#{id} and name=#{name}`, map);
//vino会转换为下列结果:
//params=["1","vino"]
//client.query(`select id,name,age from user where id=? and name=?`, params); //mysql
//client.query(`select id,name,age from user where id=$1 and name=$2`, params);//postgres
//client.query(`select id,name,age from user where id=@p1 and name=@p2`, params);//sqlserver

&{}

占位符&{}支持数组下标和对象属性,它最终会将值替换到sql语句中

示例1

params为数组时,占位符中可以使用数组下标

let data = ["1", "vino"]
client.query(`select id,name,age from user where id=&{0} and name=&{1}`, data);
//vino会转换为下列结果:
//params=[]
//client.query(`select id,name,age from user where id=1 and name=vino`, params); //mysql\postgres\sqlserver

示例2

params为对象时,占位符中可以使用对象属性名

let data = {
	id: "1",
	name: "vino"
}
client.query(`select id,name,age from user where id=&{id} and name=&{name}`, data);
//vino会转换为下列结果:
//params=[]
//client.query(`select id,name,age from user where id=1 and name=vino`, params); //mysql\postgres\sqlserver

示例3

params为Map时,占位符中可以使用Map的key

let map = new Map()
map.set("id", 1)
map.set("name", "vino")
client.query(`select id,name,age from user where id=&{id} and name=&{name}`, map);
//vino会转换为下列结果:
//params=[]
//client.query(`select id,name,age from user where id=1 and name=vino`, params); //mysql\postgres\sqlserver

注意

  • &{}占位符会带来SQL注入攻击的风险,请慎重使用。
  • #{}占位符会被转换为数据库原生占位符,可以避免SQL注入攻击。

类型对应关系

下面是数据库类型和vino类型之间的默认转换关系

mysql

数据库类型Vino类型
INTint
TINYINTint
SMALLINTint
MEDIUMINTint
BIGINTbigint
DECIMALstring
FLOATfloat
DOUBLEfloat
DATEDate
DATETIMEDate
TIMESTAMPDate
TIMEDate
YEARint
CHARstring
VARCHARstring
BLOBBuffer
TEXTstring
ENUMstring
SET[]string
JSONobject

postgre

数据库类型Vino类型
INT2int
INT4int
INT8int
FLOAT4float
FLOAT8float
NUMERICstring
BPCHARstring
VARCHARstring
TEXTstring
BOOLboolean
TIMESTAMPDate
DATEDate
TIMEDate
INTERVALstring
POINTstring
JSONobject
JSONBobject
UUIDstring
XMLstring

sqlserver

数据库类型Vino类型
INTint
SMALLINTint
BIGINTbigint
DECIMALfloat
FLOATfloat
REALfloat
DATEDate
DATETIMEDate
DATETIME2Date
SMALLDATETIMEDate
TIMEDate
CHARstring
VARCHARstring
TEXTstring
NCHARstring
NVARCHARstring
NTEXTstring
BINARYBuffer
VARBINARYBuffer
IMAGEBuffer
UNIQUEIDENTIFIERstring
BITboolean

clickhouse

数据库类型Vino类型
Int8int
Int16int
Int32int
Int64int
UInt8int
UInt16int
UInt32int
UInt64int
Float32float
Float64float
Stringstring
FixedStringstring
DateDate
DateTimeDate
Enum8[]string
Tuple[]any
Decimalstring
IPv4string
IPv6string
UUIDstring
Mapobject
更新时间 9/26/2024, 6:49:27 PM