一、前言
Golang(又称Go)是Google公司开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Go语言主要用作服务器端开发,其定位是用来开发“大型软件”的。
达梦提供dm-go-driver,封装了一系列数据库操作的标准接口。
二、开发环境准备
名称 版本
数据库 1-3-26-2023.06.21-193718-20046-ENT
操作系统 Centos7
CPU X86
GO驱动 #8.1.3.39 #2023.05.30 #16980
三、部署
1、部署达梦数据库
略
2、配置go环境
2.1、下载golang
go1.20.2.linux-amd64.tar.gz
2.2、校验go环境
[root@www drivers]# which go
/usr/bin/which: no go in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
[root@www drivers]# go
-bash: go: command not found
没有go环境则安装go环境。
2.3、安装go环境
我是安装在/home/dmdba下。
[root@www /]# mv go1.20.2.linux-amd64.tar.gz /home/dmdba/
[root@www dmdba]# tar -xvzf go1.20.2.linux-amd64.tar.gz
2.4、达梦go驱动
安装达梦数据库,自带go驱动,存储位置如下:
[root@www bin]# cd /home/dmdba/dmdbms/
[root@www dmdbms]# cd drivers/
[root@www drivers]# cd go/
[root@www go]# ls -lrt
total 184
-rwxr-xr-x 1 dmdba dinstall 186981 Feb 28 19:19 dm-go-driver.zip
2.5、配置环境变量
vi /etc/profile
export PATH=/home/dmdba/go/bin:$PATH
. /etc/profile
2.6、下载和配置第三方包
Go官网下载的环境+达梦提供的go驱动其实还缺少第三方包。
通过go env 查看到 GOROOT=/home/dmdba/go GOPATH=/root/go
1、 go env -w GO111MODULE=on (开启pkg依赖模式,默认pkg 需要pkg下载包, 下载包需要网络,没有的话可以通过有网的先下载,然后把环境拷过去)
2、 go env -w GOPROXY=https://goproxy.io,direct (设置包下载网站,不设置的话国外的下载慢或者不通)
3、 下载第三方包:
go install golang.org/x/text@latest
go install github.com/golang/snappy@latest
安装到了 GOPATH/pkg/mod/XXXXX。
复制到GOPATH:
cp -R /root/go/pkg/mod/www.github.com /root/go/src
cp -R /root/go/pkg/mod/golang.org /root/go/src
重命名:
mv /root/go/src/www.github.com/golang/snappy@v0.0.4 /root/go/src/www.github.com/golang/snappy
mv /root/go/src/golang.org/x/text@v0.8.0 /root/go/src/golang.org/x/text
如果报错的话可能是因为升级了,版本不是0.8.0了,改为对应版本即可。
第三方包换位置后要重命名,因为有版本号,需要去掉版本号。
4、 配置dm驱动
unzip -d dm /home/dmdba/dmdbms/drivers/go/dm-go-driver.zip
cp dm /root/go/src
5、 运行:
go env -w GO111MODULE=off (关闭pkg依赖模式,下载完后再切到src模式下,其实最终是在src模式下工作的)。
go test goswitch_test.go (里面入口是 Test 函数)
go run xxx.go (里面入口是一个main函数)
四、开发示例
在达梦技术文档基础上,添加了建表和服务名连接等操作。
package main
// 引入相关包
import (
"database/sql"
"dm"
"fmt"
"io/ioutil"
"time"
)
var db *sql.DB
var err error
func main() {
driverName := "dm"
//dataSourceName := "dm://SYSDBA:SYSDBA@localhost:5236"
//服务名方式连接
//dataSourceName := "dm://SYSDBA:SYSDBA@dmserver"
//23年新版本才支持这种写法
dataSourceName := "dm://SYSDBA:SYSDBA@dm_server?dm_server=(127.0.0.1:5236)"
if db, err = connect(driverName, dataSourceName); err != nil {
fmt.Println(err)
return
}
if err = dropTable(); err != nil {
fmt.Println(err)
return
}
if err = createTable(); err != nil {
fmt.Println(err)
return
}
if err = insertTable(); err != nil {
fmt.Println(err)
return
}
if err = updateTable(); err != nil {
fmt.Println(err)
return
}
if err = queryTable(); err != nil {
fmt.Println(err)
return
}
if err = deleteTable(); err != nil {
fmt.Println(err)
return
}
if err = disconnect(); err != nil {
fmt.Println(err)
return
}
}
/* 创建数据库连接 */
func connect(driverName string, dataSourceName string) (*sql.DB, error) {
var db sql.DB
var err error
if db, err = sql.Open(driverName, dataSourceName); err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
return nil, err
}
fmt.Printf("connect to "%s" succeed.\n", dataSourceName)
return db, nil
}
/ 如果存在则删除表product /
func dropTable() error {
var sql = "drop table if exists PRODUCT;"
if _, err := db.Exec(sql); err != nil {
return err
}
fmt.Println("dropTable succeed")
return nil
}
/ 创建表product /
func createTable() error {
var sql = "create table PRODUCT(PRODUCTID INT IDENTITY(1,1) PRIMARY KEY,NAME VARCHAR(100) NOT NULL,AUTHOR VARCHAR(25) NOT NULL,PUBLISHER VARCHAR(50) NOT NULL,PUBLISHTIME DATE NOT NULL,PRODUCT_SUBCATEGORYID INT NOT NULL ,PRODUCTNO VARCHAR(25) NOT NULL,SATETYSTOCKLEVEL SMALLINT NOT NULL,ORIGINALPRICE DEC(19,4) NOT NULL,NOWPRICE DEC(19,4) NOT NULL,DISCOUNT DECIMAL(2,1) NOT NULL,DESCRIPTION TEXT,PHOTO IMAGE,TYPE VARCHAR(5),PAPERTOTAL INT,WORDTOTAL INT,SELLSTARTTIME DATE NOT NULL,SELLENDTIME DATE,UNIQUE(PRODUCTNO));"
if _, err := db.Exec(sql); err != nil {
return err
}
fmt.Println("createTable succeed")
return nil
}
/ 往产品信息表插入数据 /
func insertTable() error {
var inFileName = "/home/go_code/1.png"
var sql = INSERT INTO product(name,author,publisher,publishtime, product_subcategoryid,productno,satetystocklevel,originalprice,nowprice,discount, description,photo,type,papertotal,wordtotal,sellstarttime,sellendtime) VALUES(:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,:16,:17);
data, err := ioutil.ReadFile(inFileName)
if err != nil {
return err
}
t1, _ := time.Parse("2006-Jan-02", "2005-Apr-01")
t2, _ := time.Parse("2006-Jan-02", "2006-Mar-20")
t3, _ := time.Parse("2006-Jan-02", "1900-Jan-01")
_, err = db.Exec(sql, "三国演义", "罗贯中", "中华书局", t1, 4, "9787101046126", 10, 19.0000, 15.2000,
8.0,
"《三国演义》是中国第一部长篇章回体小说,中国小说由短篇发展至长篇的原因与说书有关。",
data, "25", 943, 93000, t2, t3)
if err != nil {
return err
}
fmt.Println("insertTable succeed")
return nil
}
/ 修改产品信息表数据 /
func updateTable() error {
var sql = "UPDATE product SET name = :name WHERE productid = 11;"
if _, err := db.Exec(sql, "三国演义(上)"); err != nil {
return err
}
fmt.Println("updateTable succeed")
return nil
}
/ 查询产品信息表 /
func queryTable() error {
var productid int
var name string
var author string
var description dm.DmClob
var photo dm.DmBlob
var sql = "SELECT productid,name,author,description,photo FROM product WHERE productid=11"
rows, err := db.Query(sql)
if err != nil {
return err
}
defer rows.Close()
fmt.Println("queryTable results:")
for rows.Next() {
if err = rows.Scan(&productid, &name, &author, &description, &photo); err != nil {
return err
}
blobLen, _ := photo.GetLength()
fmt.Printf("%v %v %v %v %v\n", productid, name, author, description, blobLen)
}
return nil
}
/ 删除产品信息表数据 /
func deleteTable() error {
var sql = "DELETE FROM product WHERE productid = 12;"
if _, err := db.Exec(sql); err != nil {
return err
}
fmt.Println("deleteTable succeed")
return nil
}
/ 关闭数据库连接 */
func disconnect() error {
if err := db.Close(); err != nil {
fmt.Printf("db close failed: %s.\n", err)
return err
}
fmt.Println("disconnect succeed")
return nil
}
补充:
1、需要在/home/go_code目录添加1.png
2、服务名配置如下:
[root@www go_code]# cat /etc/dm_svc.conf
dmserver=(127.0.0.1:5236)
[dmserver]
TIME_ZONE=(+480)
五、常见问题
1、url串中配置服务名
旧版本Go驱动不支持这种用法:
直接把服务名写在url串中。例如:jdbc:dm://dm_server?dm_server=(192.168.0.1:32141,192.168.0.2:32142)&loginMode=1&doSwitch=1&switchTimes=2000 。
23年3季度版本及之后版本已支持。
2、go驱动自动重连
跟qax适配时发现,配置了服务名方式访问集群,当发生主备切换时,会间歇式地出现各种错误:sql: expected 0 arguments, got 1 、Error -5402:参数个数不匹配、Error -5402:参数不兼容 。原因是旧版本go驱动默认开启自动重连,失败的话返回DM自己封装的error信息,不是标准报错信息。qax是根据标准报错信息自己实现自动重连的,所以一直失败。23年2季度版本支持通过开关控制是否启用达梦自动重连,新增连接串属性driverReconnect,默认为false。
driverReconnect连接串属性说明:配置doSwitch=1/2时使用,表示重连时使用驱动自身的重连机制,否则在连接失效时返回sql标准错误driver.ErrBadConn。当由go来进行重连时,不论driverReconnect取true还是false,都不会影响连接的重连效果,建议设置为false(默认值)。
3、text类型做比较时报错:数据类型不匹配
可通过开oracle兼容 、mysql兼容或者to_char 、cast 、convert处理text类型查询条件方式绕过。
文章
阅读量
获赞