Go 数据库接口

Go 提供了标准库database/sql用于和数据库交互,database/sql只是一套统一地抽象接口,真正与数据库打交道的是各个数据库对应的驱动实现,因此使用前需要先注册对应数据库的驱动,然后就可以使用SQL中定义的接口来统一地操作数据库了。

Go 标准库中的 SQL 安装包是在$GOROOT/src/database/sql/ 目录下,如图:

image-20220517161747971

开发环境搭建

软件 版本
HGDB 安全版V4、企业版v5及以上版本
Go 1.18.1
IDE Visual Studio Code

选用数据库驱动

Go实现的支持HGDB的驱动也很多

https://github.com/lib/pq    支持database/sql驱动,纯Go写的
https://github.com/jbarham/gopgsqldriver    支持database/sql驱动,纯Go写的
https://github.com/lxn/go-pgsql    支持database/sql驱动,纯Go写的

在下面的示例中采用了第一个驱动,因为它目前使用的人最多,在github上也比较活跃。

设置工作目录以及引入驱动

通过go mod init命令指定工作目录

D:\HighGo_Demo\Go_Demo>go mod init Go_Demo
go: creating new go.mod: module Go_Demo
go: to add module requirements and sums:
go mod tidy

通过go get github.com/lib/pg来引入驱动

D:\HighGo_Demo\Go_Demo> go get github.com/lib/pq
go: added github.com/lib/pq v1.10.5

示例代码

企业版

go_conn.go

package main

import (
"database/sql"
"fmt"
"time"

_ "github.com/lib/pq"
)

const (
host = "127.0.0.1"
port = 5866
dbname = "test"
user = "test"
password = "test"
)

/*
需要注意的是sql.DB并不是数据库连接,而是一个go中的一个数据结构
在拿到sql.DB时并不会创建新的连接,而可以认为是拿到了一个数据库连接池。
通常来说一个sql.DB应该像全局变量一样长期保存,只有在执行数据库操作(如Ping()操作)时才会自动生成一个连接并连接数据库。
在连接操作执行完毕后应该及时地释放,此处说的释放是指释放连接而不是sql.DB连接,而不要在某一个小函数中都进行Open()和Close()操作,否则会引起资源耗尽的问题。
*/

func connectDB() *sql.DB {
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
db, err := sql.Open("postgres", psqlInfo)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("Successfully connected!")
return db
}

func query_version(db *sql.DB) {
var name string
rows, err := db.Query("select '数据库版本:['||version()||']' as name")
if err != nil {
fmt.Println(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&name)
if err != nil {
fmt.Println(err)
}
}
err = rows.Err()
if err != nil {
fmt.Println(err)
}
fmt.Println(name)
}

type student struct {
id int
name string
age int
birthday time.Time
}

func query_data(db *sql.DB) {
//rows, err := db.Query("select * from student where user_name=$1", "ah")
rows, err := db.Query("select * from student")
if err != nil {
panic(err)
}
//延迟关闭rows
defer rows.Close()

for rows.Next() {
stu := student{}
err := rows.Scan(&stu.id, &stu.name, &stu.age, &stu.birthday)
if err != nil {
panic(err)
}
fmt.Printf("id = %v, name = %v, age = %v, birthday = %v\n",
stu.id, stu.name, stu.age, stu.birthday.Format("2006-01-02 15:04:05"))
}

}

func insert_data(db *sql.DB) {
//PostgreSQL是通过$1,$2这种方式来指定要传递的参数
stmt, err := db.Prepare("INSERT INTO student(name, age, birthday) VALUES($1, $2, $3)")
if err != nil {
panic(err)
}

res, err := stmt.Exec("Go语言", 35, time.Now())
/*
使用Exec()函数后会返回一个sql.Result即上面的res变量接收到的返回值,
它提供了LastInserId() (int64, error)和RowsAffected() (int64, error)
分别获取执行语句返回的对应的id和语句执行所影响的行数

注意:pg不支持LastInsertId函数,因为没有实现类似MySQL的自增ID返回
*/
if err != nil {
panic(err)
}

fmt.Printf("res = %d", res)
}

func update_data(db *sql.DB) {
stmt, err := db.Prepare("update stduent set name=$1 WHERE name=$2")
if err != nil {
panic(err)
}
res, err := stmt.Exec("我改了新名字", "Go语言")
if err != nil {
panic(err)
}

fmt.Printf("res = %d", res)
}

func delete_data(db *sql.DB) {
stmt, err := db.Prepare("delete from student where name=$1")
if err != nil {
panic(err)
}
res, err := stmt.Exec("Go语言")
if err != nil {
panic(err)
}

fmt.Printf("res = %d", res)
}

func main() {
db := connectDB()
insert_data(db)
//query_data(db)
//update_data(db)
//delete_data(db)
}

执行结果:

image-20220509142752858

image-20220509142807173

安全版

go_conn_sm3.go

package main

import (
"database/sql"
"fmt"

_ "github.com/lib/pq"
)

const (
host = "192.168.2.5"
port = 5866
dbname = "test"
user = "test"
password = "test"
)

func connectDB() *sql.DB {
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
db, err := sql.Open("postgres", psqlInfo)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("Successfully connected!")
return db
}

func query_version(db *sql.DB) {
var name string
rows, err := db.Query("select '数据库版本:['||version()||']' as name")
if err != nil {
fmt.Println(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&name)
if err != nil {
fmt.Println(err)
}
}
err = rows.Err()
if err != nil {
fmt.Println(err)
}
fmt.Println(name)
}

func main() {
db := connectDB()
query_version(db)

}

执行结果:

image-20220509143224368

由于HGDB安全版对原生PostgreSQL的通信协议进行了安全加固,这导致与PostgreSQL的默认通信协议互相不兼容了,因此,使用lib/pq的PostgreSQL原生版本默认是不能连接HGDB安全版的。会报类似上述错误。

通过重新编译后的lib/pq 解决问题

1、下载编译后的pq包(具体下载地址联系售前技术支持)

2、进行替换

  • 检查GOPATH

image-20220509195425047

  • 将解压后的pq文件放到工作空间中

3、编辑go.mod

module Go_Demo

go 1.18

require github.com/lib/pq v1.10.5 // indirect

//方式一:使用GOPATH(建议使用这种)
replace github.com/lib/pq => C:/Users/Administrator/go/src/github.com/lib/pg


//方式二:使用任意路径(路径不能有空格)
//replace github.com/lib/pq => D:/src/github.com/lib/pg

4、执行结果

image-20220509165840683