golang oracle sql,Golang实践录:oracle数据库实践

本文在 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 版本。压缩包虽不同,但内含目录一致,解压到当前目录即可,拷贝到指定目录,示例:D:\oracle\instantclient。

PKG_CONFIG_PATH环境变量 (失败)

1D:\mingw64\lib\pkg-config

执行echo $PKG_CONFIG_PATH查看目录,映射到 E:\Program Files\Git\mingw64\lib 下,不存在pkgconfig,创建之。

安装库

1、

获取oci8.pc。执行:

1

2

3

4

5

6

7go 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目录(位于$GOPATH\src\github.com\wendal\go-oci8\windows),将pkg-config.exe拷贝到 $PATH 目录,oci8.pc拷贝到 $PKG_CONFIG_PATH 目录。

注:如果系统已有pkg-config.exe,则不需拷贝。

注:$PKG_CONFIG_PATH如果没有指定,则设置环境变量。笔者使用的 git bash 已设置好,如下:

1

2

3

4

5echo $PKG_CONFIG_PATH

/mingw64/lib/pkgconfig:/mingw64/share/pkgconfig

实际对应目录为:

C:\Program Files\Git\mingw64\share\pkgconfig

2、

再次执行:

1

2

3

4

5go 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,修改后,再执行,通过。得到pkg\windows_amd64\github.com\wendal\go-oci8.a目录。

由作者说明得知,wendal是从mattn仓库fork得到的,也下载:

1go 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,再次go build,提示未定义的函数,用nm查之,函数前为U,猜测格式不对。

1

2$ pkg-config --cflags -- oci8

-ID:/oracle/instantclient/sdk/include

在D:\oracle\instantclient目录找oci.dll,放到临时目录。从dll生成.a。

1

2

3

4

5$ 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,可用nm查函数,已有定义。将其放到D:\oracle\instantclient\sdk\lib\msvc目录(注:也可以是其它目录)。

5、

疑惑1:

似乎golang生成的go-oci8.a没什么用,可能内部链接了里面的函数,但最终的oci函数,还是从官方的oci库中获取,如OCIStmtPrepare。

疑惑2:

在go get github.com/mattn/go-oci8处,会使用pc文件指定的参数,但似乎只针对头文件,也没有链接,具体未详细研究(因为.lib和.a格式已然不同,不通用,链接不上的,没报错,应该没链接)。

疑惑3:

在测试中发现,如果编译成功后,将 D:\mingw64\lib\liboci.a 改后缀名,也会编译通过(猜测应该是 Golang 做了库的缓存,暂无时间研究透)。

疑惑4:

在编译 Golang 的机器上连接 oracle 一切正常,将可执行文件放到另一台安装了 oci 的电脑上,提示初始化失败,经查,未找到原因。

解决:经查,是 oci 版本导致的。使用 ProcessExplorer 查看其依赖的 oci 库,发现有 oraociei12.dll oraons.dll oci.dll 三个,另一电脑的 oci 为 19 版本,拷贝正确的,可运行。

源码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

61package main

import (

true"database/sql"

true"log"

true"errors"

// 导入mysql驱动

_ "github.com/mattn/go-oci8"

)

func CreateOracle(dbstr string) (sqldb *sql.DB, err error) {

sqldb, err = sql.Open("oci8", dbstr)

trueif err != nil {

truetruereturn nil, errors.New("open database failed: " + err.Error())

true}

trueerr = sqldb.Ping()

trueif err != nil {

truetruereturn nil, errors.New("connect database failed: " + err.Error())

true}

truelog.Println("connect to oracle ok")

//log.Println("connect to ", dbParam.server, dbParam.database, "ok")

return

}

func GetVersion(sqldb *sql.DB) () {

true// 降序,最新的在前面

trueresults, err := sqldb.Query("select version, codetype, addtime from tablevesion order by tablevesion desc")

trueif err != nil {

truetruefmt.Println("Query error: ", err)

truetruereturn

true}

truefor results.Next() {

truetruevar item1, item2, item3 sql.NullString

truetrueerr := results.Scan(&item1, &item2, &item3)

truetrueif err != nil {

truetruetruefmt.Println("scan error: ", err)

truetruetruebreak

truetrue}

truetrueif (!item1.Valid || !item2.Valid || !item3.Valid) {

truetruetruecontinue

truetrue}

truetruefmt.Println(item1.String, item2.String, item3.String)

true}

truereturn

}

var dbstr = "latelee/123456789@192.168.18.188:1521/mydb"

func main() {

fmt.Println("oracle test")

SQLDB, err := CreateOracle(dbstr)

if err != nil {

fmt.Println("open db failed", err)

return

}

GetVersion(SQLDB)

}

参考资料

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值