凯发真人娱乐

golang连接达梦数据库的一个坑 -凯发真人娱乐

2023-08-17,,

有一次项目中用到了达梦数据库,后端语言使用的golang,达梦官方并未适配专门的golang连接方式,正一筹莫展的时候发现达梦提供了odbc的连接,这样可以使用类似mssqlodbc连接方式连接达梦数据库。

使用的达梦数据库版本为dm8

达梦数据库开启odbc连接

参考博客1、参考博客2

参照上面两个博客内容配置odbc连接

golang代码

一些参考文档:

package main
import (
"fmt"
_ "github.com/alexbrainman/odbc" // google's odbc driver
"github.com/go-xorm/xorm"
"xorm.io/core"
"github.com/axgle/mahonia"
) type address struct {
addressid int64 `xorm:"addressid"`
address1 string `xorm:"address1"`
address2 string `xorm:"address2"`
city string `xorm:"city"`
postalcode string `xorm:"postalcode"`
} // 字符串解码函数,处理中文乱码
func converttostring(src string, srccode string, tagcode string) string {
srccoder := mahonia.newdecoder(srccode)
srcresult := srccoder.convertstring(src)
tagcoder := mahonia.newdecoder(tagcode)
_, cdata, _ := tagcoder.translate([]byte(srcresult), true)
result := string(cdata)
return result
} func main() {
engine, err := xorm.newengine("odbc", "driver={dm8 odbc driver};server=127.0.0.1:5236;database=dm;uid=sysdba;pwd=password;charset=utf8")
if err != nil {
fmt.println("new engine got error:", err)
return
}
engine.showsql(true)//控制台打印出生成的sql语句;
engine.logger().setlevel(core.log_debug)
if err := engine.ping(); err != nil {
fmt.println("ping got error:", err)
return
} // 1) sql查询
results, err := engine.query("select addressid, address1, address2, city, postalcode from person.address limit 5 offset 2")
if err != nil {
fmt.println("查询出错:", err)
return
}
for i, e := range results {
fmt.printf("%v\t", i)
for k, v := range e {
// 达梦数据库中文默认为gbk
fmt.printf("%v=%v\t", k, converttostring(string(v), "gbk", "utf-8"))
}
fmt.printf("\n")
}
fmt.println("*******************************")
// 2) 使用struct 映射结果
engine.setmapper(core.samemapper{})
var sliceofaddress []address
err = engine.table("person.address").limit(5, 0).find(&sliceofaddress)
if err != nil {
fmt.println("查询出错:", err)
return
}
for i,e := range sliceofaddress {
e.address1 = converttostring(e.address1, "gbk", "utf-8")
e.address2 = converttostring(e.address2, "gbk", "utf-8")
e.city = converttostring(e.city, "gbk", "utf-8")
fmt.printf("%v=%v\n", i, e)
}
}

1)解决 golang.org/x/ 下包下载不下来的问题

https://studygolang.com/articles/19051?fr=sidebar

https://studygolang.com/articles/24075?fr=sidebar

2)无效的表或视图名[person.address](这个也是最坑的一点)

原因:我们使用的是odbc的方式连接达梦数据库,实际上使用的是mssql的驱动,第一个1) sql查询结果是ok的,但是2) struct 查询就会报错:

[xorm] [info]  2020/06/08 16:52:40.183731 [sql] select top 5 "addressid", "address1", "address2", "city", "postalcode" from "person.address"
查询出错: sqlprepare: {42s02} 第1 行附近出现错误:
无效的表或视图名[person.address]

通过日志发现,xorm吧没一个字段和表名都添加上了双引号:select top 5 "addressid", "address1", "address2", "city", "postalcode" from "person.address"这个sql在mssql中执行是没问题的,但是放到达梦数据库中就会报错,因为达梦不支持双引号包裹字段、命名空间、表名:

这样就很坑爹了,我尝试着修改结构体的xorm:"addressid"去掉其中的双引号,但是问题还存在,最后想到打印出来的sql中待了双引号,说明xorm后台还是拼接的sql语句,如果找到拼接sql语句的代码然后去掉其中的双引号不是就好了么。于是跟踪代码查找拼接sql的代码:

1、engine.table("person.address").limit(5, 0).find(&sliceofaddress)

先找到engin模块的find()方法:

代码路径:src.github.com/go-xorm/xorm/engine.go

// find retrieve records from table, condibeans's non-empty fields
// are conditions. beans could be []struct, []*struct, map[int64]struct
// map[int64]*struct
func (engine *engine) find(beans interface{}, condibeans ...interface{}) error {
session := engine.newsession()
defer session.close()
return session.find(beans, condibeans...)
}

发现其实调用的是session.find() 方法

2、src.github.com/go-xorm/session_find.go

// find retrieve records from table, condibeans's non-empty fields
// are conditions. beans could be []struct, []*struct, map[int64]struct
// map[int64]*struct
func (session *session) find(rowssliceptr interface{}, condibean ...interface{}) error {
if session.isautoclose {
defer session.close()
}
return session.find(rowssliceptr, condibean...)
}

发现实际上调用的是session.find(rowssliceptr, condibean...)

func (session *session) find(rowssliceptr interface{}, condibean ...interface{}) error {
	defer session.resetstatement()
	// 代码省略 。。。
	var sqlstr string
var args []interface{}
var err error
// 此处就是拼接sql的代码
if session.statement.rawsql == "" {
// 代码省略 。。。
} else {
sqlstr = session.statement.rawsql
args = session.statement.rawparams
}
// 获得配置信息判断当前数据库类型
uri := session.engine.dialect().uri()
// 判断当前是否是达梦数据库
if uri.dbtype == "mssql" && uri.dbname == "dm" {
newsqlstr := strings.replace(sqlstr, "\"", "", -1) // 去掉双引号
sqlstr = newsqlstr
} // 代码省略 。。。
return session.nocachefind(table, slicevalue, sqlstr, args...)
}

通过session.engine.dialect().uri()获得配置信息,这段代码怎么来的,实际上是在xorm.newengine()的时候会解析配置信息,并赋值给enginedialect属性,代码位置:src.github.com/go-xorm/xorm/xorm.go

engine := &engine{
db: db,
dialect: dialect,
tables: make(map[reflect.type]*core.table),
mutex: &sync.rwmutex{},
tagidentifier: "xorm",
tzlocation: time.local,
taghandlers: defaulttaghandlers,
cachers: make(map[string]core.cacher),
defaultcontext: context.background(),
}

找到sql之后去掉双引号即可,因为做了判断只有是达梦的类型数据库的时候才修改,所以不会影响其他类型的数据库。至此问题得到了解决。

输出结果:

[xorm] [info]  2020/06/08 17:14:18.061667 ping database odbc
[xorm] [info] 2020/06/08 17:14:19.315349 [sql] select addressid, address1, address2, city, postalcode from person.address limit 5 offset 2
0 addressid=3 address1=青山区青翠苑1号 address2= city=武汉市青山区 postalcode=430080
1 addressid=4 address1=武昌区武船新村115号 address2= city=武汉市武昌区 postalcode=430063
2 addressid=5 address1=汉阳大道熊家湾15号 address2= city=武汉市汉阳区 postalcode=430050
3 addressid=6 address1=洪山区保利花园50-1-304 address2= city=武汉市洪山区 postalcode=430073
4 addressid=7 address1=洪山区保利花园51-1-702 address2= city=武汉市洪山区 postalcode=430073
*******************************
[xorm] [info] 2020/06/08 17:14:19.324291 [sql] select top 5 addressid, address1, address2, city, postalcode from person.address
0={1 洪山区369号金地太阳城56-1-202 武汉市洪山区 430073}
1={2 洪山区369号金地太阳城57-2-302 武汉市洪山区 430073}
2={3 青山区青翠苑1号 武汉市青山区 430080}
3={4 武昌区武船新村115号 武汉市武昌区 430063}
4={5 汉阳大道熊家湾15号 武汉市汉阳区 430050}

参考文档

xorm的操作指南

xorm的pkg文档

go语言中文文档

golang连接达梦数据库的一个坑的相关教程结束。

网站地图