1. 程式人生 > >golang連線達夢資料庫的一個坑

golang連線達夢資料庫的一個坑

# golang連線達夢資料庫的一個坑 有一次專案中用到了達夢資料庫,後端語言使用的`golang`,達夢官方並未適配專門的`golang`連線方式,正一籌莫展的時候發現達夢提供了`odbc`的連線,這樣可以使用類似`mssql`的`odbc`連線方式連線達夢資料庫。 使用的達夢資料庫版本為`DM8` ## 達夢資料庫開啟`odbc`連線 [參考部落格1](https://blog.csdn.net/fengxiaozhenjay/article/details/104256229)、[參考部落格2](https://blog.csdn.net/weixin_46323641/article/details/104298535) 參照上面兩個部落格內容配置`odbc`連線 ## `golang`程式碼 一些參考文件: ```go 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 查詢`就會報錯: ```sh [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中執行是沒問題的,但是放到達夢資料庫中就會報錯,因為達夢不支援雙引號包裹欄位、名稱空間、表名: ![](https://img2020.cnblogs.com/blog/1104741/202006/1104741-20200608171856928-525869082.png) 這樣就很坑爹了,我嘗試著修改結構體的`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` ```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` ```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...)` ```go 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()`的時候會解析配置資訊,並賦值給`engine`的`dialect`屬性,程式碼位置:`src.github.com/go-xorm/xorm/xorm.go` ```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之後去掉雙引號即可,因為做了判斷只有是達夢的型別資料庫的時候才修改,所以不會影響其他型別的資料庫。至此問題得到了解決。 輸出結果: ```shell [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的操作指南](https://www.kancloud.cn/xormplus/xorm/167077) [xorm的pkg文件](https://pkg.go.dev/xorm.io/[email protected]?tab=doc) [go語言中文文件](http://xorm.topgoer.com/chapter-01/)