nodejs使用pg连接postgresql详解(Client)
pg是为nodejs提供了postgresql连接的一个库,今天来学习一下如何使用这个库
记录一下关于Client模块的学习
1.安装
我用的npm:npm install pg
2.前期准备
1.我创建了一个配置文件,专门用来放我数据库连接有关的信息,通过config
库来进行管理,所以还需要下载:npm install config
,如果已经全局安装过的话也直接添加:npm add config
src/config/default.json
{
"db":{
"user": "testuser",
"host": "127.0.0.1",
"database": "nodepgTest",
"password": "testpsd",
"port": "5432"
}
}
2.为了能够用ES Module的方式来引入库,我修改了package.json,在里面添加了"type":"module"
package.json
,这样就可以使用import
来引入模块了
{
"type": "module",
"dependencies": {
"config": "^3.3.12",
"pg": "^8.12.0"
}
}
这样就基本做好了使用准备
3.pg库的基础使用
当我们下载一个数据库的时候呢,下载的通常是两个部分,一个是这个数据库的server,一个是数据库的客户端
数据库的server主要是用来处理sql语句,管理权限,执行事务等等,一般用来部署在服务器设备上面。
而数据库的客户端就有很多种了,可以是下载数据库时候下载的命令行客户端,也可也是有界面的UI客户端比如pgAdmin,我们在写应用程序的数据库driver(数据库驱动包,比如我们要用的pg),实际上也是用来创建客户端的。
先来看看在nodejs中的基本使用
1.回调函数的写法
import config from "config"
import pg from "pg"
const { Client } = pg
const dbConfig = config.db
const pgClient = new Client(dbConfig)
//1.连接到数据库
pgClient.connect((connectErr) => {
if (connectErr) {
console.error(connectErr,"连接失败")
return
}
console.log("连接成功")
//2.执行查询
pgClient.query('SELECT * from tbl_user where username = $1', ['testuser'], (queryErr, res) => {
if (!queryErr) {
console.log(res.rows[0])
} else {
console.error(queryErr,"查询失败")
}
//3.关闭连接
pgClient.end((endErr) => {
if (endErr) {
console.error(err,"关闭连接失败")
}else{
console.log("关闭连接")
}
})
})
})
2.也可以使用异步函数的写法
import config from "config";
import pg from "pg";
const { Client } = pg
const dbConfig = config.db
const pgClient = new Client(dbConfig)
; (async () => {
try {
//1.连接到数据库
await pgClient.connect()
//2.执行查询
const res = await pgClient.query('SELECT * from tbl_user where username = $1',['testuser'])
console.log(res.rows[0])
} catch (err) {
console.error(err);
} finally {
//3.关闭数据库
await pgClient.end()
}
})()
//这里的SQL语句"SELECT * from tbl_user where username = $1"
//其中的$1是占位符,后面数组中的值['testUser']的位置对应的就是这里的$1
//占位符的数字实际上是后面数组的索引+1,比如$1就是数组索引为0的元素
//我们可以直接把它看成数组的第一个元素
//如果有有多个占位符的话,占位符的数字是几对应的就是数组的第几个元素
//$2对应数组的第二个元素,索引为1
//$3对应数组的第三个元素,索引为2
//如果占位符的位置发生了改变,那么对应的数组的位置也要发生改变
//为了让主要部分的代码看起来清晰,我只写了一个try...catch,如果需要每个步骤都能有独立的异常处理,可以为每一步都包裹上try...catch
实际上,Client模块可选的config还有很多,如下:
type Config = { user?: string, // default process.env.PGUSER || process.env.USER password?: string or function, //default process.env.PGPASSWORD host?: string, // default process.env.PGHOST port?: number, // default process.env.PGPORT database?: string, // default process.env.PGDATABASE || user connectionString?: string, // e.g. //连接字符串示例:postgres://user:password@host:5432/database ssl?: any, // passed directly to node.TLSSocket, supports all tls.connect options //可以配置ssl证书 types?: any, // custom type parsers //可以自定义一些解析器,比如 把时间戳字符串转换为 JavaScript Date 对象 statement_timeout?: number, // number of milliseconds before a statement in query will time out, default is no timeout //在数据库上设置所有连接到当前数据库的语句执行的超时时间,这个超时时间因为是设置到数据库服务器上的,所以会影响到所有连接到该数据库的所有客户端,通常需要有超级权限的用户才可以执行 query_timeout?: number, // number of milliseconds before a query call will timeout, default is no timeout //设置当前客户端的查询请求的超时时间,只作用于我们自己写的这个客户端 lock_timeout?: number, // number of milliseconds a query is allowed to be en lock state before it's cancelled due to lock timeout //作用于客户端,如果客户端请求锁定资源,超过了一定时间还没请求到,就会自动取消 //什么是锁定资源呢,当我们在执行事务的时候,为了避免多个客户端同时修改某个行或者某个表,在一个客户端执行事务的时候,服务器会锁定资源,让其他的客户端无法进行修改以及读取,这样避免多个客户端同时修改以及读取同一个数据产生的矛盾 application_name?: string, // The name of the application that created this Client instance //有了这个以后,可以在日志里面明确的看到是哪个应用程序连接了数据库,方便我们监控管理 connectionTimeoutMillis?: number, // number of milliseconds to wait for connection, default is no timeout //连接超时,如果设置了,超过时间还没连接上的话,就会取消连接,返回连接超时错误 idle_in_transaction_session_timeout?: number // number of milliseconds before terminating any session with an open idle transaction, default is no timeout //空闲事务超时,如果一个事务超过了一定时间没有进行任何的事务请求,就会自动释放 }
pg库的Client
有以下常用的方法
1.connect(callback)
connect的参数只有一个回调函数
1.callback(error):如果连接出现错误,pg就会把错误信息从error参数传入回调,我们写了回调的话就可以拿到错误信息
示例:
client.connect(function(connectError){
console.log(connectError) //出错了就会把错误传递进来
})
2.query(text,value,callback)
//官方源码对query函数的注释:can take in strings, config object or query object
//query可以执行几乎所有的sql语句
query函数有以下三个参数
1.text:就是我们的sql语句,这个text,也可也传入一个config对象,或者是query对象
2.value:是我们sql语句中的参数,是一个数组
3.callback(error,result):回调函数,回调函数会被传入两个参数,第一个是异常信息,第二个是查询结果
因为参数可以不同,所以用法也有不同,下面我给出了三种示例
1.示例1:
(传入的是sql语句)
client.query('SELECT * from tbl_user where username = $1',
['testuser'],
function(queryError, result){
...//这里可以获取err和result,并进行我们需要的处理
})
client.end()
2.示例2:
(传入的是config对象)
const queryConfig = { //官方提供了很多可选配置,我放在了后面
text:"SELECT * from tbl_user where username = $1",
values:['testuser'],
name:'select_by_username'
//其他可选配置
}
client.query(queryConfig,function(queryError,result){
...//处理error和result
})
client.end()
//在这个示例中,其实我们甚至可以把所有的queryConfig给抽离出去,抽离到单独的配置文件里面,然后通过config库来进行读取,比如: config.get("query.queryConfig"),然后保存到单独的query.json文件里,不过要注意不要重名,在名字结构上需要考虑的严谨一些,可以和我们实际的业务结合起来,比如userquery.seletbyName,
//当然我也是pg库初学者,这种抽离的方法只是我的一种假想,不确定实际使用会不会有什么优缺点
//另外config还有很多可选配置,我会在后面列出来
3.示例3:
(传入的是一个query对象)
是一个低配版的queryConfig,它只有text和value
const queryObj = {
text:"SELECT * from tbl_user where username = $1",
values:['testuser']
}
client.query(queryObj,function(queryError,result){
...//处理error和result
})
client.end()
需要注意的是,查询结束以后不要忘记关闭连接
client.end()
官方文档中看到的queryConfig的一些可选配置
type QueryConfig { // the raw query text text: string;//这个是必须的,sql语句 // an array of query parameters values?: Array<any>;//指定查询用到的参数,数组 // name of the query - used for prepared statements name?: string;//可以给查询命名,这样相同的查询语句不会被重复解析,可以提高查询效率 // by default rows come out as a key/value pair for each row // pass the string 'array' here to receive rows as an array of values rowMode?: string;//可以指定返回的行的类型,默认是object // custom type parsers just for this query result types?: Types;//和上面的values用法类似,它可以显式指定参数的类型 // TODO: document queryMode?: string;//这个不知道是啥...没有找到说明,官方文档应该还没写 }
3.end(callback)
end的参数也只有一个回调
1.callback(error):如果连接没能正常关闭,异常信息会通过error参数传递进来
client.end(function(endError)){
console.log(endError)
}
Client的事件监听
1.error
只要客户端运行出错了,就会触发
client.on('error', (err: Error) => void) => void
示例:
client.on('error', (err) => {
console.error('Error occurred:', err);
});
2.end
客户端连接关闭,就会触发
client.on('end') => void
示例:
client.on('end',function()=>{
console.log(连接已经关闭)
})
3.notifaction
在收到服务器发送的通知时触发notifaction
事件
通知,是postgresql的一种单向通信机制,可以实时的对监听了通知的客户端发送消息
notifaction需要配合LISTEN
和NOTIFY
使用
NOTIFY
用来向指定频道发送通知
LISTEN
用来监听指定频道的通知
频道是一个逻辑概念,就只是为了标记一下,通知发送到哪个频道名,以及监听从哪个频道名发送来的
它不会持久化的存储,通知也不会持久化的存储
//官方给的示例中说明了通知消息的类型:
type Notification {
processId: number,
channel: string,
payload?: string
}
//pg会把收到的msg传递到notification的回调中
//msg格式就是前面的Notification类型格式
//我们可以在回调中进行自己需要的处理
const client = new pg.Client()
await client.connect()
client.query('LISTEN foo') ///监听频道foo
client.on('notification', (msg) => {
console.log(msg.channel) // foo //监听
console.log(msg.payload) // bar!
})
client.query(`NOTIFY foo, 'bar!'`) //向频道foo发送消息bar
4.notice
用于接收postgresql服务器发送的警告信息(非致命通知信息,像Error
的话属于致命信息),在服务器发送了警告信息时触发
client.on('notice', (msg) => console.warn('notice:', msg))
示例:
client.on('notice', (notice) => {
console.warn('Received notice:');
console.warn('Message:', notice.message); // 警告消息内容
console.warn('Severity:', notice.severity); // 警告级别
console.warn('Detail:', notice.detail); // 更详细的信息
console.warn('Hint:', notice.hint); // 提示或建议
console.warn('Where:', notice.where); // 发生警告的位置
});