nodejs使用pg连接postgresql(详解Client模块)

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需要配合LISTENNOTIFY使用

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); // 发生警告的位置
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

棋小仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值