如何理解Golang牵手PostgreSQL增删改查+不写结构快速扫描字段

63次阅读
没有评论

共计 7358 个字符,预计需要花费 19 分钟才能阅读完成。

行业资讯    
数据库    
如何理解 Golang 牵手 PostgreSQL 增删改查 + 不写结构快速扫描字段

本篇内容介绍了“如何理解 Golang 牵手 PostgreSQL 增删改查 + 不写结构快速扫描字段”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

驱动安装

执行以下命令安装 postgresql 驱动

go get github.com/lib/pq

pq 包支持的功能

SSL

作为驱动程序, 与 database/sql 结合处理连接相关操作

扫描时间 time.Time 类型, 如 timestamp[tz], time[tz], date

扫描二进制 blobs 对象 (Blob 是内存中的数据缓冲, 用来匹配 strign 类型的 ID 和字节切片), 如: bytea

PostgreSQL 的 hstore 数据类型支持

支持 COPY FROM

pq.ParseURL 方法用来将 urls 转化为 sql.Open 的连接字符串

支持许多 libpq 库兼容的环境变量

支持 Unix socket

支持通知 Notifications, 如:LISTEN/NOTIFY

支持 pgpass

GSS (Kerberos) 认证

连接字符串参数

pq 包与 libpq 类似 (libpq 是用 C 编写的底层接口,   为其他高级语言比如 C ++,Perl,Python,Tcl 和 ECPG 等提供底层 PostgreSQL 支持). 建立连接时需要提供连接参数,   一部分支持 libpq 的参数也支持 pq, 额外的, pq 允许在连接字符串中指定运行时参数 (如:search_path 或 work_mem),  libpq 则不能在连接字符串中指定运行时参数, 只能在 option 参数中指定.

pq 包为了兼容 libpq 包, 下面的连接参数都支持

* dbname -  需要连接的数据库名  * user -  需要使用的用户名  * password -  该用户的密码  * host -  需要连接的 postgresql 主机, unix 域名套接字以 / 开始,  默认是 localhost * port - postgresql 绑定的端口  (默认 5432) * sslmode -  是否使用 SSL (默认是启用 (require), libpq 包默认不启用 SSL) * fallback_application_name -  失败时, 可以提供一个应用程序名来跟踪. * connect_timeout -  连接最大等待秒数, 0 或者不指定,  表示不确定时间的等待  * sslcert -  证书文件位置,  文件中必须包含 PEM 编码的数据  * sslkey -  密钥文件位置,  文件中必须包含 PEM 编码的数据  * sslrootcert -  根证书文件位置,  文件中必须包含 PEM 编码的数据 

sslmode 支持一下模式

* disable -  禁用 SSL * require -  总是使用 SSL(跳过验证) * verify-ca -  总是使用 SSL (验证服务器提供的证书是由可信的 CA 签署的) * verify-full -  总是使用 SSL(验证服务器提供的证书是由受信任的 CA 签署的,并验证服务器主机名是否与证书中的主机名匹配)

更多连接字符串参数请参考官方文档

对包含空格的参数, 需要使用单引号, 如:

user=pqgotest password= with spaces

使用反斜杠进行转义, 如:

user=space\ man password= it\ s valid

注意: 如果要设置 client_encoding 连接参数 (用于设置连接的编码), 必须设置为 UTF8 , 才能与 Postgres 匹配,   设置为其他值将会报错.

除了上面的参数, 在连接字符串中也可以通过后台设置运行时参数, 详细运行时参数, 请参考 runtime-config

支持 libpq 的大部分环境变量也支持 pq 包, 详细环境变量请参考 libpq-envars. 如果没有设置环境变量且连接字符串也没有提供该参数,   程序会 panic 崩溃退出, 字符串参数优先级高于环境变量.

完整 增删改查 示例代码

package main import (  database/sql   encoding/json   fmt  _  github.com/lib/pq   log  ) const ( // Initialize connection constants. HOST =  172.16.xx.xx  PORT = 31976 DATABASE =  postgres  USER =  postgres  PASSWORD =  xxx  ) func checkError(err error) { if err != nil { panic(err) } } type Db struct { db *sql.DB } //  创建表  func (this *Db) CreateTable() { //  以水果库存清单表 inventory 为例  // Drop previous table of same name if one exists.  如果之前存在清单表,  则删除该表  _, err := this.db.Exec( DROP TABLE IF EXISTS inventory;) checkError(err) fmt.Println(Finished dropping table (if existed) ) // Create table.  创建表,  指定 id, name, quantity(数量) 字段,  其中 id 为主键  _, err = this.db.Exec(CREATE TABLE inventory (id serial PRIMARY KEY, name VARCHAR(50), quantity INTEGER); ) checkError(err) fmt.Println(Finished creating table) } //  删除表  func (this *Db) DropTable() { //  以水果库存清单表 inventory 为例  // Drop previous table of same name if one exists.  如果之前存在清单表,  则删除该表  _, err := this.db.Exec( DROP TABLE IF EXISTS inventory;) checkError(err) fmt.Println(Finished dropping table (if existed) ) } //  增加数据  func (this *Db) Insert() { // Insert some data into table.  插入 3 条水果数据  sql_statement :=  INSERT INTO inventory (name, quantity) VALUES ($1, $2);  _, err := this.db.Exec(sql_statement,  banana , 150) checkError(err) _, err = this.db.Exec(sql_statement,  orange , 154) checkError(err) _, err = this.db.Exec(sql_statement,  apple , 100) checkError(err) fmt.Println(Inserted 3 rows of data) } //  读数据 / 查数据  func (this *Db) Read() { // 读取数据  // Read rows from table. var id int var name string var quantity int sql_statement :=  SELECT * from inventory;  rows, err := this.db.Query(sql_statement) checkError(err) defer rows.Close() for rows.Next() { switch err := rows.Scan( id,  name,  quantity); err { case sql.ErrNoRows: fmt.Println( No rows were returned) case nil: fmt.Printf(Data row = (%d, %s, %d)\n , id, name, quantity) default: checkError(err) } } } //  更新数据  func (this *Db) Update() { // Modify some data in table. sql_statement :=  UPDATE inventory SET quantity = $2 WHERE name = $1;  _, err := this.db.Exec(sql_statement,  banana , 200) checkError(err) fmt.Println(Updated 1 row of data) } //  删除数据  func (this *Db) Delete() { // Delete some data from table. sql_statement :=  DELETE FROM inventory WHERE name = $1;  _, err := this.db.Exec(sql_statement,  orange) checkError(err) fmt.Println(Deleted 1 row of data) } //  数据序列化为 Json 字符串,  便于人工查看  func Data2Json(anyData interface{}) string { JsonByte, err := json.Marshal(anyData) if err != nil { log.Printf( 数据序列化为 json 出错:\n%s\n , err.Error()) return   } return string(JsonByte) } // 多行数据解析  func QueryAndParseRows(Db *sql.DB, queryStr string) []map[string]string { rows, err := Db.Query(queryStr) defer rows.Close() if err != nil { log.Printf( 查询出错:\nSQL:\n%s,  错误详情 \n , queryStr, err.Error()) return nil } cols, _ := rows.Columns() // 列名  if len(cols)   0 { var ret []map[string]string // 定义返回的映射切片变量 ret for rows.Next() { buff := make([]interface{}, len(cols)) data := make([][]byte, len(cols)) // 数据库中的 NULL 值可以扫描到字节中  for i, _ := range buff { buff[i] =  data[i] } rows.Scan(buff...) // 扫描到 buff 接口中,实际是字符串类型 data 中  // 将每一行数据存放到数组中  dataKv := make(map[string]string, len(cols)) for k, col := range data { // k 是 index,col 是对应的值  //fmt.Printf( %30s:\t%s\n , cols[k], col) dataKv[cols[k]] = string(col) } ret = append(ret, dataKv) } log.Printf(返回多元素数组:\n%s , Data2Json(ret)) return ret } else { return nil } } // 单行数据解析   查询数据库,解析查询结果,支持动态行数解析  func QueryAndParse(Db *sql.DB, queryStr string) map[string]string { rows, err := Db.Query(queryStr) defer rows.Close() if err != nil { log.Printf( 查询出错:\nSQL:\n%s,  错误详情 \n , queryStr, err.Error()) return nil } //rows, _ := Db.Query(SHOW VARIABLES LIKE  %data%) cols, _ := rows.Columns() if len(cols)   0 { buff := make([]interface{}, len(cols)) //  临时 slice data := make([][]byte, len(cols)) //  存数据 slice dataKv := make(map[string]string, len(cols)) for i, _ := range buff { buff[i] =  data[i] } for rows.Next() { rows.Scan(buff...) // ... 是必须的  } for k, col := range data { dataKv[cols[k]] = string(col) //fmt.Printf(%30s:\t%s\n , cols[k], col) } log.Printf(返回单行数据 Map:\n%s , Data2Json(dataKv)) return dataKv } else { return nil } } func main() { // Initialize connection string.  初始化连接字符串,  参数包含主机, 端口, 用户名, 密码, 数据库名,SSL 模式 ( 禁用), 超时时间  var connectionString string = fmt.Sprintf(host=%s port=%d user=%s password=%s dbname=%s sslmode=disable connect_timeout=3 , HOST, PORT, USER, PASSWORD, DATABASE) // Initialize connection object.  初始化连接对象,  驱动名为 postgres db, err := sql.Open(postgres , connectionString) defer db.Close() checkError(err) postgresDb := Db{ db: db, } err = postgresDb.db.Ping() // 连通性检查  checkError(err) fmt.Println(Successfully created connection to database) postgresDb.CreateTable() // 创建表  postgresDb.Insert() // 插入数据  postgresDb.Read() // 查询数据  QueryAndParseRows(postgresDb.db,  SELECT * from inventory;) // 直接查询和解析多行数据  QueryAndParse(postgresDb.db,  SHOW DateStyle;) // 直接查询和解析单行数据  postgresDb.Update() // 修改 / 更新数据  postgresDb.Read() postgresDb.Delete() // 删除数据  postgresDb.Read() postgresDb.DropTable() }

执行 go run main.go 运行结果如下:

Successfully created connection to database Finished dropping table (if existed) Finished creating table Inserted 3 rows of data Data row = (1, banana, 150) Data row = (2, orange, 154) Data row = (3, apple, 100) 2020/12/15 22:13:33  返回多元素数组: [{id : 1 , name : banana , quantity : 150},{id : 2 , name : orange , quantity : 154},{id : 3 , name : apple , quantity : 100}] 2020/12/15 22:13:33  返回单行数据 Map: {DateStyle : ISO, MDY} Updated 1 row of data Data row = (2, orange, 154) Data row = (3, apple, 100) Data row = (1, banana, 200) Deleted 1 row of data Data row = (3, apple, 100) Data row = (1, banana, 200) Finished dropping table (if existed)

“如何理解 Golang 牵手 PostgreSQL 增删改查 + 不写结构快速扫描字段”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-27发表,共计7358字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)