本文在 Windows 7 64bit 系统上使用 golang 连接查询 oracle 数据库。
环境准备
前置条件:
安装mingw(取其gcc及库,因为要用cgo编译),安装git(取其bash)。安装oci。
oracle提供了编程接口,golang 有多个实现库,但均需依赖 oracle 的 instantclient。下载OCI:
https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html 。
版本有 SDK 版本、Basic 版本。压缩包虽不同,但内含目录一致,解压到当前目录即可,拷贝到指定目录,示例:
PKG_CONFIG_PATH环境变量 (失败)
1 | D:\mingw64\lib\pkg-config |
执行
安装库
1、
获取oci8.pc。执行:
1 2 3 4 5 6 7 | go get github.com/wendal/go-oci8 提示: # pkg-config --cflags -- oci8 Package oci8 was not found in the pkg-config search path. Perhaps you should add the directory containing `oci8.pc' to the PKG_CONFIG_PATH environment variable No package 'oci8' found |
无须理会错误,此处是下载源码,主要获取pkg-config.exe和oci8.pc文件。在下载包go-oci8的windows目录,将pkg-config.exe拷贝到git的bin目录,oci8.pc拷贝到 E:\Program Files\Git\mingw64\lib\pkgconfig 目录。
2、
再次执行:
1 2 3 4 5 | go get github.com/wendal/go-oci8 提示: # github.com/wendal/go-oci8 D:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lclntsh collect2.exe: error: ld returned 1 exit status |
一说:把oci8.pc文件的lclntsh改为oci,修改后,再执行,通过。得到
由作者说明得知,
1 | go get github.com/mattn/go-oci8 |
对比两者源码和生成的.a文件,mattn的文件也多,库也比较大。但对比使用者而言无区别。为安全起见,实际工程使用 mattn 库。
3、
oci8.pc内容:
1 2 3 4 5 6 7 8 9 | #prefix=D:/oracle/instantclient includedir=D:/oracle/instantclient/sdk/include libdir=D:/oracle/instantclient/sdk/lib/msvc Name: oci8 Description: Oracle Instant Client Version: 19.8 Cflags: -I${includedir} Libs: -L${libdir} -loci |
注:使用全路径,不使用prefix(因为git安装的目录带有空格),将库修改为oci。
4、
编写测试代码。go build 出错:
1 2 3 4 | # command-line-arguments D:\go\pkg\tool\windows_amd64\link.exe: running gcc failed: exit status 1 D:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -loci collect2.exe: error: ld returned 1 exit status |
猜想:官方oci是.lib格式,gcc不认。而golang的驱动得到.a但没有包括oci里面的函数。将得到的go-oci8.a改名为liboci.a,再次
1 2 | $ pkg-config --cflags -- oci8 -ID:/oracle/instantclient/sdk/include |
在
1 2 3 4 | $ gendef.exe oci.dll # 注:从dll生产def(下一步要用到) * [oci.dll] Found PE image $ dlltool.exe -D oci.dll -d oci.def -l liboci.a # 从dll和def生成.a文件。 |
生成的文件为liboci.a,可用
5、
疑惑1:
似乎golang生成的go-oci8.a没什么用,可能内部链接了里面的函数,但最终的oci函数,还是从官方的oci库中获取,如OCIStmtPrepare。
疑惑2:
在
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | package main import ( "database/sql" "log" "errors" // 导入mysql驱动 _ "github.com/mattn/go-oci8" ) func CreateOracle(dbstr string) (sqldb *sql.DB, err error) { sqldb, err = sql.Open("oci8", dbstr) if err != nil { return nil, errors.New("open database failed: " + err.Error()) } err = sqldb.Ping() if err != nil { return nil, errors.New("connect database failed: " + err.Error()) } log.Println("connect to oracle ok") //log.Println("connect to ", dbParam.server, dbParam.database, "ok") return } func GetVersion(sqldb *sql.DB) () { // 降序,最新的在前面 results, err := sqldb.Query("select version, codetype, addtime from tablevesion order by tablevesion desc") if err != nil { fmt.Println("Query error: ", err) return } for results.Next() { var item1, item2, item3 sql.NullString err := results.Scan(&item1, &item2, &item3) if err != nil { fmt.Println("scan error: ", err) break } if (!item1.Valid || !item2.Valid || !item3.Valid) { continue } fmt.Println(item1.String, item2.String, item3.String) } return } var dbstr = "latelee/[email protected]:1521/mydb" func main() { fmt.Println("oracle test") SQLDB, err := CreateOracle(dbstr) if err != nil { fmt.Println("open db failed", err) return } GetVersion(SQLDB) } |
参考资料
参考资料:
https://www.cnblogs.com/good-temper/p/3791874.html
https://blog.csdn.net/yh_coco/article/details/78068610