sql
sql
sql
模块是vino
提供的用来操作关系型数据库的统一接口,支持mysql
、postgres
、sqlserver
、clickhouse
。
快速上手
这是一个简单的案例来操作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:object | Client | 创建数据库连接对象 |
open
open
方法用来创建数据库连接对象。
语法
sql.open(name:string,config: object)
参数
- name 数据库名称。目前支持的数据库:
mysql
、postgres
、sqlserver
、clickhouse
- 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
- sslmode 是否禁用ssl。SSL 模式(disable, allow, prefer, require, verify-ca, verify-full)
示例
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¶m2=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¶m2=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?:class | object[] | 执行查询操作,返回查询结果 |
queryJson | 方法 | sql:string,params?:[]any | string | 执行查询操作,返回查询json字符串 |
exec | 方法 | sql:string,params?:[]any | object | 执行更新操作,返回影响行数等信息 |
begin | 方法 | tx | 开启事务,返回事务对象 | |
prepare | 方法 | sql:string | stmt | 创建预处理语句,返回预处理对象 |
提示
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
并返回执行状态结果,用来执行不需要返回结果集的查询,例如:udpate
、delete
、insert
等
语法
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?:class | object[] | 在事务中执行查询操作 |
queryJson | 方法 | sql:string,params?:[]any | string | 在事务中执行查询操作,并返回json字符串形式的结果 |
exec | 方法 | sql:string,params?:[]any | object | 在事务中执行更新操作 |
prepare | 方法 | sql:string | stmt | 创建预处理 |
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?:class | object[] | 使用预处理语句执行查询操作 |
queryJson | 方法 | params:[]any | string | 使用预处理语句执行查询操作并返回json字符串形式的结果 |
exec | 方法 | params:[]any | object | 使用预处理语句执行更新操作 |
close | 方法 | void | 关闭预处理语句 |
query
query
方法用于向数据库传递占位符参数,返回结果集
语法
stmt.query(params:[]any,clz?:Class);
参数
- params: 用于向预处理对象传递数据库占位符的参数
- clz: (可选) 用于指定返回结果的类型,默认返回的是object数组,如果需要自定义返回结果数据,可以指定一个Class。
返回值
- object[] : 返回的结果集,是一个对象数组,是sql查询结果。该结果集中每一个对象的属性名和sql执行结果的字段名相同
queryJson
queryJson
方法用于向数据库传递占位符参数,返回json字符串形式的结果
语法
stmt.queryJson(params:[]any);
参数
- params: 用于向预处理对象传递数据库占位符的参数
返回值
- string : 返回json字符串结果
exec
exec
方法用于向数据库传递占位符参数,返回执行状态结果,用来执行不需要返回结果集的查询,例如:udpate
、delete
、insert
等
语法
stmt.exec(params:[]any);
参数
- params:用于向预处理对象传递数据库占位符的参数
返回值
- 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占位符
?
mysql
和clickhouse
数据库使用问号?
作为占位符。例如:
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提供了所有数据库一致的、功能更加强大的、容易使用的占位符#{}
和&{}
。它比数据库占位符功能更强大,除了支持数组
以外,还支持对象
和Map
。vino占位符
由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类型 |
---|---|
INT | int |
TINYINT | int |
SMALLINT | int |
MEDIUMINT | int |
BIGINT | bigint |
DECIMAL | string |
FLOAT | float |
DOUBLE | float |
DATE | Date |
DATETIME | Date |
TIMESTAMP | Date |
TIME | Date |
YEAR | int |
CHAR | string |
VARCHAR | string |
BLOB | Buffer |
TEXT | string |
ENUM | string |
SET | []string |
JSON | object |
postgre
数据库类型 | Vino类型 |
---|---|
INT2 | int |
INT4 | int |
INT8 | int |
FLOAT4 | float |
FLOAT8 | float |
NUMERIC | string |
BPCHAR | string |
VARCHAR | string |
TEXT | string |
BOOL | boolean |
TIMESTAMP | Date |
DATE | Date |
TIME | Date |
INTERVAL | string |
POINT | string |
JSON | object |
JSONB | object |
UUID | string |
XML | string |
sqlserver
数据库类型 | Vino类型 |
---|---|
INT | int |
SMALLINT | int |
BIGINT | bigint |
DECIMAL | float |
FLOAT | float |
REAL | float |
DATE | Date |
DATETIME | Date |
DATETIME2 | Date |
SMALLDATETIME | Date |
TIME | Date |
CHAR | string |
VARCHAR | string |
TEXT | string |
NCHAR | string |
NVARCHAR | string |
NTEXT | string |
BINARY | Buffer |
VARBINARY | Buffer |
IMAGE | Buffer |
UNIQUEIDENTIFIER | string |
BIT | boolean |
clickhouse
数据库类型 | Vino类型 |
---|---|
Int8 | int |
Int16 | int |
Int32 | int |
Int64 | int |
UInt8 | int |
UInt16 | int |
UInt32 | int |
UInt64 | int |
Float32 | float |
Float64 | float |
String | string |
FixedString | string |
Date | Date |
DateTime | Date |
Enum8 | []string |
Tuple | []any |
Decimal | string |
IPv4 | string |
IPv6 | string |
UUID | string |
Map | object |